Skip to content

Add optional GenericTensorNetworks backend#31

Draft
bernalde wants to merge 6 commits into
mainfrom
codex/gtn-backend-integration
Draft

Add optional GenericTensorNetworks backend#31
bernalde wants to merge 6 commits into
mainfrom
codex/gtn-backend-integration

Conversation

@bernalde

@bernalde bernalde commented May 14, 2026

Copy link
Copy Markdown
Member

Summary

  • add GenericTensorNetworks.jl and ProblemReductions.jl as optional weak dependencies
  • add a stateless, unexported TenSolver.GTNBackend <: AbstractTenSolverBackend in src/backends/gtn.jl
  • select the optional backend through the landed dispatch with backend = :gtn (or backend = TenSolver.GTNBackend() for advanced use)
  • implement exact QUBO and pseudo-Boolean polynomial solves in the package extension
  • expose optimum size, representative configurations, degeneracy, full optimum enumeration, and k-best sizes through minimize(...; backend = :gtn, property = ...)
  • return an unexported GTNSolution wrapper with configurations, the raw GTN result, and contraction metadata

Architecture refresh

This branch now merges current main and follows the backend API that landed after the draft was opened:

  • backend implementations live one-per-file under src/backends/
  • backend-specific methods extend minimize(::Backend, ...) directly
  • the old duplicate AbstractBackend, DMRG implementation, and PseudoBooleanModel layer are no longer part of this PR
  • optional backend types follow the PEPS convention and remain unexported; backend = :gtn is the preferred user-facing selector
  • backend objects carry structure only; GTN settings (property, k, usecuda, element_type, optimizer/slicer, and storage settings) are solve-call keywords
  • every supported property returns the true optimum as the first result; count and configuration payloads are read only for properties that define them, and reader failures propagate
  • non-Boolean domains and TenSolver native constraints are rejected explicitly until the GTN lowering supports them

The existing TenSolver.Optimizer JuMP/MOI path remains DMRG-backed. This PR does not introduce GTN-specific raw optimizer attributes; optional backend forwarding should be added through a generic optimizer architecture rather than special-casing one extension.

GTNSolution remains standalone while PR #44 is open. Once the shared AbstractSolution hierarchy lands, this result type can be reconciled in a focused follow-up.

Supported exact properties

  • :size
  • :single
  • :count (k == 1)
  • :configs (k == 1)
  • :kbest_sizes

For representative k-best configurations, use property = :single, k = n.

Validation

  • full Pkg.test() suite on Julia 1.12: passed
  • Aqua and package doctests: passed
  • full Documenter build: passed
  • focused GTN integration tests: 27/27 passed
  • focused backend-dispatch tests: 32/32 passed

@bernalde bernalde left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub would not allow REQUEST_CHANGES because the authenticated account owns this PR, but the findings below are blocking and should be treated as request-changes.

Review summary:

  1. Blocking issues: GTNBackend.k > 1 paths exposed by the public API are broken for Float64 QUBOs. :kbest_sizes fails when combining GTN/Tropical sizes with the objective constant, and :configs/:count fail before TenSolver can return a result. These need either implementation plus tests or explicit rejection of unsupported combinations.
  2. Nonblocking issues: the one-variable DMRG path silently drops unknown kwargs, and solution_space gives an opaque failure when used with DMRG.
  3. Questions: none.
  4. Tests run and outcomes: julia +1.12 --project=. -e 'using Pkg; Pkg.test(; coverage=false)' passed; julia +1.12 --project=docs/ docs/make.jl passed; targeted GTNBackend(k=2) smoke tests failed as noted inline.
  5. Merge recommendation: I would not merge this until the blocking issues above are addressed.

Comment thread ext/TenSolverGenericTensorNetworksExt.jl Outdated
Comment thread ext/TenSolverGenericTensorNetworksExt.jl Outdated
Comment thread src/solver.jl Outdated
Comment thread src/backends.jl Outdated
@bernalde

Copy link
Copy Markdown
Member Author

Commits pushed:

  • c96eb40 Address GTN backend review comments

Main changes made:

  • Fixed GTNBackend(property=:kbest_sizes, k>1) objective reconstruction by unwrapping GTN/Tropical numeric wrappers before adding the TenSolver constant.
  • Added explicit ArgumentErrors for unsupported GTNBackend(property=:configs, k>1) and GTNBackend(property=:count, k>1) on TenSolver pseudo-Boolean objectives.
  • Added direct ArgumentError handling for solution_space(...; backend=:dmrg).
  • Made the one-variable DMRG shortcut accept known DMRG keywords for API compatibility while rejecting unexpected keywords.
  • Documented the current k support limits in the GTN backend docstrings.

Tests run and results:

  • julia +1.12 --project=. -e 'using Test, TenSolver; include("test/backends.jl")' passed.
  • julia +1.12 --project=. -e 'using Pkg; Pkg.test(; coverage=false)' passed.
  • julia +1.12 --project=docs/ docs/make.jl passed.

Comments intentionally not addressed:

  • None.

Remaining risks or follow-up items:

  • Full k-best enumeration/counting for TenSolver pseudo-Boolean objectives remains unsupported for k > 1; this is now rejected clearly rather than failing inside GenericTensorNetworks. Implementing those modes can be a follow-up if we decide to support weighted k-best enumeration/counting.

@bernalde
bernalde marked this pull request as ready for review May 14, 2026 23:35
@bernalde bernalde changed the title [codex] Add optional GenericTensorNetworks backend Add optional GenericTensorNetworks backend May 14, 2026

@bernalde bernalde left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Senior maintainer review. Note: I am the PR author, so GitHub only permits a COMMENT event from me; a formal approval or change-request must come from another maintainer. No blocking issues found.

Diff verification: merge-base ca27880; git diff <merge-base>..HEAD --stat = 16 files, +720 / -15, matching the PR's reported totals. No issue is referenced (no Closes/Fixes #N); reviewed on the merits.

Optionality / extension correctness — clean:

  • GenericTensorNetworks and ProblemReductions are in [weakdeps] + [extensions], NOT in core [deps]. Core Pkg.instantiate() succeeds without them.
  • Extension key, module name (TenSolverGenericTensorNetworksExt), and filename all match; julia = "1.10" satisfies the >= 1.9 requirement for extensions.
  • When the deps are absent, the GTNBackend stub in src/backends.jl throws a helpful ArgumentError directing the user to using GenericTensorNetworks, ProblemReductions.

Correctness — validated empirically (in an isolated env with the extension deps): QUBO min E=2.0 and maximize E=3.0 with correct configs (sign/sense preserved through _with_objective); :count/:configs/:kbest correct; degree-3 PUBO matches brute force; asymmetric Q matches brute force (confirms Q[i,j]+Q[j,i] symmetrization into ProblemReductions.QUBO). DMRG remains the default everywhere.

Nonblocking

  1. ext/TenSolverGenericTensorNetworksExt.jl:166: metadata["contraction_complexity"] = GenericTensorNetworks.contraction_complexity(gtn) is not wrapped in try/catch, unlike the adjacent estimate_memory call. If it ever throws for some optimizer/problem the whole solve fails after the answer is computed. Wrap it defensively like estimate_memory.
  2. test/gtn.jl guards the GTN tests with try import GenericTensorNetworks, but the deps are listed in test/Project.toml [deps], so they are always installed and the skip branch is dead in CI. The "graceful skip on missing dep" path is therefore never actually exercised; if graceful degradation is a goal, it isn't tested.
  3. src/objective.jl evaluate: selected &= !iszero(xs[i]) treats any nonzero entry as 1, so fractional/invalid input silently yields wrong energies. Document that inputs must be 0/1, or validate.

Questions
4. There is no test exercising the JuMP/MOI path with backend = "gtn" — only the direct-API GTN path is tested. The gtn_T :: Type raw attribute and attribute plumbing are untested end-to-end. Add one JuMP-level test selecting the GTN backend.
5. The PR body says validation used Julia 1.12 "because the manifest is generated for 1.12." No Manifest.toml is tracked and Project.toml declares julia = "1.10"; I validated on 1.10.11. The body's claim is inaccurate.

Tests run (Julia 1.10.11):

  • julia --project=. -e 'using Pkg; Pkg.instantiate()' — success (core env, no GTN deps needed).
  • julia --project=. -e 'using Pkg; Pkg.test()' — passed (exit 0, Testing TenSolver tests passed); with GTN deps installed via test/Project.toml, test/gtn.jl ran for real. QUBODrivers.jl 130, Aqua.jl 11, VRP 2, HDF5 16, Doctests 1.
  • Direct GTN validation in an isolated env: min/max/count/configs/kbest/PUBO/asymmetric-Q all matched expected/brute-force.

Merge readiness: Ready to merge from a correctness/optionality standpoint — optional backend done correctly via weakdeps+extension (not a hard dep), results correct and sign-consistent with DMRG, DMRG remains default, full suite green on 1.10. Recommended follow-ups (non-blocking): a JuMP-level backend = "gtn" test, wrap contraction_complexity in try/catch, and note the gtn.jl skip-guard never runs the missing-dep path in CI. The formal verdict must come from another maintainer.

Comment thread ext/TenSolverGenericTensorNetworksExt.jl Outdated

@bernalde bernalde left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Senior maintainer review of PR #31 "Add optional GenericTensorNetworks backend". Note: I am the PR author under this posting identity, so GitHub will not let me file APPROVE/REQUEST_CHANGES on my own PR. This is submitted as COMMENT; the formal approving/blocking verdict must come from another maintainer. All findings, including blocking ones, are below.

Scope check: diff matches the reported totals (16 files, +720/-15) against merge-base ca27880. CI is green on all 9 Julia jobs plus build/docs at head c96eb40. The optional backend is correctly implemented as a Julia package extension via [weakdeps] + [extensions] (GenericTensorNetworks + ProblemReductions are weakdeps, not hard deps), and both are added to test/Project.toml so the extension loads under CI. GTNBackend without the extension loaded throws a clear ArgumentError. That part of the design is sound.

Blocking 1 — PR is stale and conflicts with current main (v0.2.0). GitHub reports this PR mergeable=CONFLICTING / mergeStateStatus=DIRTY. It was branched from old main ca27880 and is now behind_by 5 commits. The branch still carries version = "0.1.0", QUBODrivers = "0.3", and QUBOTools = "0.10" in Project.toml, whereas main is at version = "0.2.0", QUBODrivers = "0.6.1", QUBOTools = "0.13" (merged via #53/#54). These exact compat lines are the conflict. Why it matters: it cannot merge as-is, and if force-merged it would silently regress the QUBODrivers/QUBOTools bump and the version. Fix: rebase onto current origin/main, drop the version/QUBODrivers/QUBOTools changes (keep main's), and re-resolve. The green CI here ran against the stale base and does not reflect post-rebase behavior.

Blocking 2 — Overlap/likely regression with already-merged #49 (single-variable QUBO fix). This branch re-implements single-variable handling independently: _solve_backend(::DMRGBackend, ...) dispatches to a new _minimize_single_variable, and test/qubo.jl adds "Single variable"/"Single variable tie" cases. Meanwhile #49 (commit b67de66) already fixed single-variable QUBOs in src/solver.jl on main. After rebasing, src/solver.jl will conflict and the two implementations must be reconciled into one. Why it matters: this is the kind of duplicate/divergent fix that produces subtle regressions or a lost fix on merge. Fix: rebase, resolve src/solver.jl against main's #49 implementation, and keep a single canonical single-variable path with the union of tests.

Issue intent: the PR body references no Closes/Fixes/Refs #N and there is no linked closing issue. Per review policy I am flagging the missing issue link (Nonblocking) — if this tracks an issue, add Refs #N.

Nonblocking 1 — Compat bound risk after rebase. The PR pins GenericTensorNetworks = "4" and ProblemReductions = "0.3". These are reasonable, but they were resolved against the old manifest/Julia 1.12. After rebasing onto v0.2.0 (which bumped QUBODrivers to 0.6.1 / QUBOTools 0.13), re-run Pkg.resolve() to confirm GenericTensorNetworks 4 + ProblemReductions 0.3 still co-resolve with the new QUBOTools 0.13 stack; tighten if needed.

Nonblocking 2 — GTNSolution.sample() for property=:single with no enumerated configs falls back to rand(psi.configs) or throws if configs is empty. For :size/:count properties there are legitimately no configs and sample throws ArgumentError, which is correct, but it is worth a docstring note that sample is only meaningful for config-producing properties (:single, :configs). Low priority.

Question 1 — QUBO matrix mapping correctness. In ext/TenSolverGenericTensorNetworksExt.jl, _qubo_matrix writes a quadratic term's combined coefficient only into the upper entry matrix[vars[1], vars[2]] and leaves matrix[vars[2], vars[1]] = 0. The canonical PseudoBooleanModel already folds Q[i,j]+Q[j,i] into one term (confirmed by test/objective.jl: model.terms[[1,2]] == Q[1,2]+Q[2,1]). So this is correct only if ProblemReductions.QUBO evaluates the full asymmetric sum_{i,j} M_ij x_i x_j (counting each off-diagonal once). If QUBO instead symmetrizes or only reads the upper triangle differently, the pairwise energy would be off by a factor. The existing gtn.jl test (Q=[0 2;0 0]) does not discriminate a doubling error because the quadratic term is inactive at the minimum. The higher-order brute-force test exercises the CSP path, not the QUBO path. Please confirm against ProblemReductions' QUBO energy convention and add a test where the quadratic term is active at the optimum and would differ under doubling (e.g. a 2-var case whose minimizer is [1,1]).

Question 2 — maximize for GTN returns _with_objective(sol, -E) which rebuilds a GTNSolution carrying the negated objective but the SAME configs/result computed for the negated (minimization) problem. The test asserts sample(max_sol) lands in the maximizing set, which passes, but max_sol.result and max_sol.metadata["size"] still describe the internal minimized (sign-flipped) problem. Intended? If so, document that raw result/size metadata is in the internal-minimization sign convention.

Tests run: julia and git are denied in this review sandbox (even with the sandbox disabled), so I could not execute Pkg.test() locally. I verified via the GitHub checks API against the exact head SHA c96eb40: all 9 Julia matrix jobs (1/lts/pre x ubuntu/macOS/windows), build, and documenter/deploy pass. The GTN extension is exercised in CI because test/Project.toml carries GenericTensorNetworks + ProblemReductions. Caveat: this CI ran against the stale base ca27880, so it does not validate behavior after the required rebase onto v0.2.0.

Summary

  1. Blocking: (a) PR is stale/CONFLICTING against main v0.2.0 — must rebase and drop the version/QUBODrivers/QUBOTools downgrades; (b) duplicate/divergent single-variable implementation overlapping already-merged #49 — must reconcile src/solver.jl on rebase.
  2. Nonblocking: re-resolve compat after rebase (GenericTensorNetworks 4 / ProblemReductions 0.3 vs new QUBOTools 0.13); document that GTNSolution sample is only meaningful for config-producing properties.
  3. Questions: confirm _qubo_matrix upper-triangular placement matches ProblemReductions.QUBO's energy convention and add a discriminating test; clarify the sign convention of result/size metadata returned by GTN maximize.
  4. Tests run: none locally (julia/git sandbox-denied); fell back to static review + CI verification at head c96eb40 — all checks green, but against the stale base.
  5. Merge-readiness: not ready. Blocked on the rebase and the #49 reconciliation, after which CI must be re-run and the QUBO-mapping question resolved.

I would not merge this until the blocking issues above are addressed.

Comment thread Project.toml
Comment thread ext/TenSolverGenericTensorNetworksExt.jl Outdated
@bernalde

Copy link
Copy Markdown
Member Author

Commits pushed:

  • cef0c74 Merge main and address GTN review comments

Main changes made:

  • Merged current origin/main into the PR branch without rewriting history, keeping TenSolver v0.2.0, QUBODrivers 0.6.1, and QUBOTools 0.13.
  • Re-ran Julia package resolution with GenericTensorNetworks 4.1.0 and ProblemReductions 0.3.4 on the updated stack.
  • Wrapped GTN contraction_complexity metadata collection in try/catch, matching the existing defensive estimate_memory handling.
  • Added GTN regression coverage for an asymmetric QUBO whose active optimum is [1, 1], catching any off-diagonal factor convention error.
  • Preserved the earlier GTN k-best and solution-space fixes after the main merge, and kept explicit DMRG keyword validation for one-variable paths.

Tests run and results:

  • git diff --check origin/main...HEAD passed.
  • julia +1.12 --project=. -e 'using Pkg; Pkg.resolve(); Pkg.instantiate()' passed.
  • julia +1.12 --project=. -e 'using Test, TenSolver; include("test/backends.jl")' passed.
  • julia +1.12 --project=. -e 'using Pkg; Pkg.test(; coverage=false)' passed.
  • julia +1.12 --project=docs/ -e 'using Pkg; Pkg.instantiate()' passed.
  • julia +1.12 --project=docs/ docs/make.jl passed; deployment was skipped outside CI.

Comments intentionally not addressed:

  • None.

Remaining risks or follow-up items:

  • CI for cef0c74 is still running on GitHub.
  • Full k-best enumeration/counting for TenSolver pseudo-Boolean objectives remains unsupported for k > 1; unsupported modes still fail up front with ArgumentError.

@iagoleal

Copy link
Copy Markdown
Collaborator

We should wait for #43 to land and then adapt this PR to use that API.

@iagoleal
iagoleal marked this pull request as draft June 26, 2026 11:33
@bernalde

Copy link
Copy Markdown
Member Author

Context refresh for whoever picks this up: beyond the earlier note to adapt to the #43 backend interface, the constraint/PEPS review cycles on closed PRs hardened several conventions the re-integration must follow:

  • Backend layout (#43 inline): one file per backend under src/backends/, with the abstract interface staying in src/solver.jl — this PR's src/backends.jl/monolithic layout predates that.
  • No leading underscores on unexported helpers (#43 inline, repeated across later reviews).
  • Don't export what users shouldn't call (#43 inline).
  • Solution types: the open PEPS PR Add optional SpinGlassPEPS backend #44 introduces AbstractSolution with the generic sample/prob/in interface; once that lands, GTNSolution should subtype it rather than ship a parallel query surface — matching Iago's guidance on #72 that only the generic query interface is user-visible.
  • Backend objects carry structure, not tunables: solver parameters are solve-call keywords with names shared across backends where concepts overlap (maxdim, iterations, device), per the keyword-unification round on the PEPS backend.

Given the drift already noted, these together reinforce the earlier assessment that a fresh branch porting ext/ + tests is cheaper than rebasing this one.

Cross-referenced from a sweep of review comments on closed PRs, 2026-07-16.

@bernalde

Copy link
Copy Markdown
Member Author

Refreshed this draft against current main in fa236b1 and ported the GTN work to the landed backend architecture.

Key changes:

  • replaced the old monolithic/duplicate backend and pseudo-Boolean layers with a stateless GTNBackend <: AbstractTenSolverBackend
  • moved the marker to src/backends/gtn.jl and implemented matrix/polynomial methods in the optional extension
  • made solution_space use backend dispatch
  • moved all GTN tuning to solve-call keywords
  • kept the JuMP/MOI optimizer DMRG-backed rather than adding GTN-specific raw attributes
  • added explicit errors for unsupported non-Boolean domains and native TenSolver constraints

Validation passed: full Pkg.test() (including Aqua and doctests), full Documenter build, 24 focused GTN assertions, and 31 focused backend-dispatch assertions.

@bernalde bernalde left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maintainer review — optional GenericTensorNetworks backend (draft)

The re-integration landed well. Today's fa236b1 ("Merge main and port GTN backend architecture") does exactly what this branch needed: GTNBackend <: AbstractTenSolverBackend in src/backends/gtn.jl, backend-specific minimize(::GTNBackend, …) in the extension, the old AbstractBackend/PseudoBooleanModel/duplicate-DMRG layer gone, stateless backend with settings as solve-call keywords, and — unlike the PEPS extension — the GTN deps resolve so test/gtn.jl actually runs in CI (12/12 green). Domains and native constraints are rejected explicitly (validate_gtn_inputs), not silently ignored. Good.

This is a draft, so this review is a COMMENT; I'm also the author, so it wouldn't be a formal APPROVE regardless. No correctness blockers found. The one merge-gating item is the public-API surface, below — worth settling before the PR leaves draft.

Findings

Blocking (for merge, not for draft) — the new exported solution_space is a redundant public interface. solution_space(::GTNBackend, args…) just calls minimize(args…; backend, property, kwargs…) (src/backends/gtn.jl:46), so it is a middle-man over minimize(…; backend = :gtn, property = …) — which already returns the same GTNSolution. It's exported (src/TenSolver.jl:27) alongside GTNBackend, adding a third solver verb to a public surface a maintainer has deliberately kept at minimize/maximize. Two clean options: drop solution_space and let callers pass property to minimize (the property machinery already lives there), or, if a distinct query API is genuinely wanted, design it as a real cross-backend interface rather than a GTN-defaulting delegator (right now backend = GTNBackend() is its default and every non-GTN backend just errors). Relatedly, this branch exports GTNBackend while the sibling optional backend PEPSBackend (#44) was deliberately kept unexported — GTN is more defensible since its extension is CI-exercised, but the two optional backends should be consistent; pick one policy.

Question — objective value for non-size properties. In solve_gtn, objective = isnothing(raw_size) ? constant : constant + primary_size(raw_size) (ext:224). For property = :count (and any property whose result carries no readable size), this reports just the additive constant as the objective, which is misleading. Is that intended, or should minimize(…; property = :count) still compute the true optimum size, or refuse to return an objective? Worth pinning down what the first return value means per property.

Nonblocking — the try_read_size/try_read_count/try_read_configs helpers (ext:175+) swallow every exception and return nothing/[] with no record. Unlike the adjacent contraction_complexity/estimate_memory blocks (which store a *_error in metadata), a genuine read failure here vanishes and surfaces later as a confusing "GTNSolution … does not contain sampleable configurations." Narrow the catch to the expected "property has no size/config" case, or record the error in metadata as the other blocks do.

Nonblocking / coordination — GTNSolution is a standalone struct re-implementing sample/sample(_,n)/in. AbstractSolution (which provides those generically) is not on main yet — it's in the still-open #44 — so it can't subtype it today. When #44 lands, GTNSolution and PEPSSolution should both become AbstractSolution subtypes and drop the duplicated methods; whichever merges second reconciles.

Tests / CI

Full CI is green on fa236b1 (12/12), and the GTN extension is genuinely exercised — test/gtn.jl is wired into runtests.jl and gated with a skip only if the deps are missing (they're in test/Project.toml, so CI runs the real solves). I relied on that authoritative same-head CI plus static review rather than installing GenericTensorNetworks locally.

What remains before leaving draft

  1. Settle the solution_space/export surface (the Blocking item).
  2. Decide the objective semantics for non-size properties.
  3. Post-#44: subtype AbstractSolution.
    The core lowering, domain/constraint rejection, and extension packaging are in good shape.

Comment thread src/backends/gtn.jl Outdated
))
end

function solution_space(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blocking (for merge): this is a thin wrapper — solution_space(::GTNBackend, args…) just forwards to minimize(args…; backend, property, kwargs…), which already returns the same GTNSolution. So solution_space(Q; property=:size) is exactly minimize(Q; backend=:gtn, property=:size). Exporting it (with GTNBackend() as the default backend and every other backend erroring) adds a third public solver verb that duplicates minimize. Prefer dropping it and routing property through minimize; if a distinct query API is really wanted, design it as a genuine cross-backend interface rather than a GTN-defaulting delegator.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2681717. I removed the solution_space definitions, export, documentation entry, and call sites. Exact queries now use the existing solver verb directly, e.g. minimize(Q; backend=:gtn, property=:count), with no GTN-specific default path.

Comment thread src/TenSolver.jl Outdated
include("solver.jl")
export minimize, maximize
export DMRGBackend
export DMRGBackend, GTNBackend, solution_space

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of the API-surface finding: GTNBackend and solution_space are exported here, but the sibling optional backend PEPSBackend (#44) was deliberately kept unexported. GTN is more defensible than PEPS since its extension actually resolves and is CI-exercised, but the two optional backends should follow one export policy — reconcile which (if any) of these names belong in the public surface.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2681717. GTNBackend is no longer exported, matching the optional PEPSBackend convention. backend=:gtn is the preferred user-facing selector; TenSolver.GTNBackend() remains documented for advanced/object dispatch use. The backend-interface test now pins the unexported policy.

item = scalar_result(raw)

raw_size = try_read_size(item)
objective = isnothing(raw_size) ? constant : constant + primary_size(raw_size)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: when raw_size is nothing (e.g. property = :count, whose result carries no readable size), the objective becomes just the additive constant, which misreports the optimum. Should minimize(…; property = :count) still compute the true size, or should the first return value be undefined/omitted for size-less properties? Please pin down what the returned objective means per property.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2681717. All supported GTN properties expose read_size, including CountingMin, so solve_gtn now reads it unconditionally and returns constant + primary_size(raw_size). The regression coverage checks the returned optimum for size, count, configs, k-best sizes, and representative k-best configurations; a size-only result is also verified to be non-sampleable.


configs_from_result(config) = append_configs!(Vector{Int}[], config)

function try_read_size(item)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nonblocking: this (and try_read_count/try_read_configs) catches every exception and returns nothing/[] with no trace. A real read failure then vanishes and resurfaces downstream as a confusing "GTNSolution … does not contain sampleable configurations." The contraction_complexity/estimate_memory blocks just below record a *_error in metadata — do the same here, or narrow the catch to the expected "this property has no size/config" case, so genuine failures aren't silently hidden.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in 2681717. I removed the three catch-all reader helpers. Size is always read directly, while configurations and counts are read directly only for properties that define those payloads. A genuine GTN reader failure now propagates. The separate contraction-complexity and memory-estimate catches remain intentional metadata diagnostics and record their errors.

Comment thread src/solution.jl
when the selected GTN property produces them, together with the raw backend
result and metadata.
"""
struct GTNSolution{C, R}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nonblocking / coordination: GTNSolution re-implements sample, sample(_, n), and in standalone. AbstractSolution — which provides those generically — isn't on main yet (it's in the open #44), so this can't subtype it today. When #44 lands, make GTNSolution (and PEPSSolution) <: AbstractSolution and drop the duplicated methods; whichever of #31/#44 merges second does the reconciliation.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No code change for this coordination note yet: PR #44 is still open/conflicting, and current main does not define AbstractSolution, so there is no landed hierarchy for GTNSolution to subtype. I kept the standalone wrapper and recorded the post-#44 reconciliation explicitly in the PR description.

@bernalde

Copy link
Copy Markdown
Member Author

Addressed the latest review in 2681717:

  • removed the redundant solution_space API; exact GTN properties now flow through minimize(...; backend=:gtn, property=...)
  • made GTNBackend unexported, matching the optional PEPSBackend policy while retaining TenSolver.GTNBackend() for advanced use
  • made the first return value the true optimum for every supported property by reading the GTN size unconditionally
  • replaced exception-swallowing payload readers with property-aware direct reads, so genuine reader failures propagate
  • updated the tests, API docs, examples, and PR description accordingly

The AbstractSolution coordination remains intentionally deferred: PR #44 is still open/conflicting and current main has no AbstractSolution hierarchy to subtype against. The PR description now records that focused follow-up.

Local validation on Julia 1.12 is green: focused backend tests (32/32), focused GTN tests (27/27), full Pkg.test() including Aqua/doctests, and the standalone Documenter build. GitHub checks are running on this head.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants