Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 53 additions & 1 deletion server/quandle_semantic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,46 @@ function _serialise_int_poly(poly::Dict{Int,Int})::String
isempty(pairs) ? "0:0" : join(pairs, ",")
end

"""
_serialise_int_poly_2var(poly::Dict{Tuple{Int,Int},Int}) -> String

Serialise a two-variable Laurent polynomial-as-dict (e.g. HOMFLY-PT,
keyed by `(a_exp, z_exp) -> coeff`) to the canonical form
`"a_exp,z_exp:coeff;a_exp,z_exp:coeff;..."` (zero coefficients dropped,
keys sorted lexicographically by `(a_exp, z_exp)`).

Distinct from `_serialise_int_poly` so single-variable and two-variable
polynomials are unambiguously different strings.
"""
function _serialise_int_poly_2var(poly::Dict{Tuple{Int,Int},Int})::String
pairs = String[]
for key in sort(collect(keys(poly)))
coeff = poly[key]
coeff == 0 && continue
push!(pairs, string(key[1], ",", key[2], ":", coeff))
end
isempty(pairs) ? "0,0:0" : join(pairs, ";")
end

# Hard limit from KnotTheory.MAX_CROSSINGS_FOR_HOMFLY = 15. Above this,
# HOMFLY's skein recursion is too expensive to keep `quandle_descriptor`
# cheap; we return a sentinel rather than throwing.
const HOMFLY_MAX_CROSSINGS = 15
const HOMFLY_DEFERRED_SENTINEL = "deferred:too_many_crossings"

"""
_safe_homfly_serialised(pd::KnotTheory.PlanarDiagram) -> String

Compute and serialise the HOMFLY-PT polynomial of `pd`, guarded by the
`HOMFLY_MAX_CROSSINGS` bound. Returns the sentinel string
`HOMFLY_DEFERRED_SENTINEL` if `pd` has more than 15 crossings.
"""
function _safe_homfly_serialised(pd::KnotTheory.PlanarDiagram)::String
length(pd.crossings) > HOMFLY_MAX_CROSSINGS && return HOMFLY_DEFERRED_SENTINEL
poly = KnotTheory.homfly_polynomial(pd)
_serialise_int_poly_2var(poly)
end

"""
quandle_descriptor(pd::KnotTheory.PlanarDiagram) -> NamedTuple

Expand Down Expand Up @@ -429,6 +469,12 @@ function quandle_descriptor(pd::KnotTheory.PlanarDiagram)
alexander_poly = KnotTheory.alexander_polynomial(pd)
alexander_str = _serialise_int_poly(alexander_poly)

# KT-2 extension: Jones, Conway (both Laurent in one variable),
# and HOMFLY-PT (two-variable, guarded at HOMFLY_MAX_CROSSINGS).
jones_str = _serialise_int_poly(KnotTheory.jones_polynomial(pd))
conway_str = _serialise_int_poly(KnotTheory.conway_polynomial(pd))
homfly_str = _safe_homfly_serialised(pd)

key = string(
canon.generator_count, ":",
rel_count, ":",
Expand All @@ -437,7 +483,10 @@ function quandle_descriptor(pd::KnotTheory.PlanarDiagram)
color5, ":",
image_hist_3_str, ":",
image_hist_5_str, ":",
alexander_str,
alexander_str, ":",
jones_str, ":",
conway_str, ":",
homfly_str,
)

(
Expand All @@ -453,6 +502,9 @@ function quandle_descriptor(pd::KnotTheory.PlanarDiagram)
image_histogram_3 = image_hist_3,
image_histogram_5 = image_hist_5,
alexander_polynomial = alexander_str,
jones_polynomial = jones_str,
conway_polynomial = conway_str,
homfly_polynomial = homfly_str,
quandle_key = key,
)
end
Expand Down
96 changes: 96 additions & 0 deletions server/test_quandle_axioms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,100 @@ end
@test f.alexander_polynomial != c.alexander_polynomial
end

# ---------------------------------------------------------------------------
# § 12. KT-2 extension — Jones, Conway, HOMFLY populated + non-trivial
#
# Same shape as the Alexander tests in §11. The three polynomial fields
# extend the quandle_descriptor; together with Alexander they form a
# strictly more discriminating descriptor.
#
# Exact-value assertions are deliberately limited to "populated + does
# not equal the trivial-unknot output". A separate exact-fixture suite
# is queued (see KT-2 extension follow-up issue) where the test corpus
# captures the exact strings KnotTheory.jl produces.
# ---------------------------------------------------------------------------

@testset "KT-2 ext: Jones polynomial is populated + non-trivial" begin
for (pd, label) in [
(trefoil().pd, "trefoil"),
(figure_eight().pd, "figure-eight"),
(cinquefoil().pd, "cinquefoil"),
]
@testset label begin
d = quandle_descriptor(pd)
@test !isempty(d.jones_polynomial)
@test d.jones_polynomial != "0:0"
end
end
end

@testset "KT-2 ext: Conway polynomial is populated + non-trivial" begin
for (pd, label) in [
(trefoil().pd, "trefoil"),
(figure_eight().pd, "figure-eight"),
(cinquefoil().pd, "cinquefoil"),
]
@testset label begin
d = quandle_descriptor(pd)
@test !isempty(d.conway_polynomial)
@test d.conway_polynomial != "0:0"
end
end
end

@testset "KT-2 ext: HOMFLY polynomial is populated within bounds" begin
for (pd, label) in [
(trefoil().pd, "trefoil"),
(figure_eight().pd, "figure-eight"),
(cinquefoil().pd, "cinquefoil"),
]
@testset label begin
d = quandle_descriptor(pd)
# All three test knots have < 15 crossings, so HOMFLY computes.
@test d.homfly_polynomial != "deferred:too_many_crossings"
@test !isempty(d.homfly_polynomial)
# HOMFLY is two-variable; format is "a_exp,z_exp:coeff;..."
@test occursin(';', d.homfly_polynomial) ||
occursin(',', d.homfly_polynomial)
end
end
end

@testset "KT-2 ext: HOMFLY guard returns sentinel above MAX_CROSSINGS" begin
# Build a synthetic 16-crossing PD by braid word s1^16. KnotTheory's
# MAX_CROSSINGS_FOR_HOMFLY is 15, so this triggers the guard.
pd_large = from_braid_word(join(["s1" for _ in 1:16], ".")).pd
d_large = quandle_descriptor(pd_large)
@test d_large.homfly_polynomial == "deferred:too_many_crossings"
end

@testset "KT-2 ext: Jones polynomial distinguishes the test knots" begin
t = quandle_descriptor(trefoil().pd)
f = quandle_descriptor(figure_eight().pd)
c = quandle_descriptor(cinquefoil().pd)
@test t.jones_polynomial != f.jones_polynomial
@test t.jones_polynomial != c.jones_polynomial
@test f.jones_polynomial != c.jones_polynomial
end

@testset "KT-2 ext: Conway polynomial distinguishes the test knots" begin
t = quandle_descriptor(trefoil().pd)
f = quandle_descriptor(figure_eight().pd)
c = quandle_descriptor(cinquefoil().pd)
@test t.conway_polynomial != f.conway_polynomial
@test t.conway_polynomial != c.conway_polynomial
@test f.conway_polynomial != c.conway_polynomial
end

@testset "KT-2 ext: HOMFLY distinguishes the test knots" begin
# HOMFLY-PT subsumes both Alexander and Jones; for these knots it
# should distinguish all pairs.
t = quandle_descriptor(trefoil().pd)
f = quandle_descriptor(figure_eight().pd)
c = quandle_descriptor(cinquefoil().pd)
@test t.homfly_polynomial != f.homfly_polynomial
@test t.homfly_polynomial != c.homfly_polynomial
@test f.homfly_polynomial != c.homfly_polynomial
end

println("quandle-axiom-tests-ok")
Loading