Skip to content

Add Spin-domain optimization support - #107

Merged
iagoleal merged 4 commits into
mainfrom
feat/issue-106-spin-domains
Jul 20, 2026
Merged

iagoleal merged 4 commits into
mainfrom
feat/issue-106-spin-domains

Conversation

@bernalde

@bernalde bernalde commented Jul 20, 2026 •

Copy link
Copy Markdown
Member

Summary

  • add domain = [-1, 1] support for quadratic and polynomial DMRG solves while keeping 0:1 as the Boolean default
  • use domain as the single public spelling, including domain = 0:(d - 1) for nonnegative integer domains
  • carry ordered physical domain values through Hamiltonian operators, explicit internal APIs, Solution sampling and probability queries, infeasible results, and constraint projection MPOs
  • accept either Spin ordering while rejecting empty, duplicate, non-real, and otherwise unsupported domains at the public boundary
  • retain the v1 nonnegative-domain assumption for SumConstraint; other constraint types continue to operate on physical Spin values

Validation

  • julia +release --project=. -e 'using Pkg; Pkg.test()'
  • targeted projection, non-binary-domain, Spin-domain, constrained-solve, and JuMP suites
  • Documenter doctests and Aqua

Related work

Branch hygiene

Closes #106

@bernalde
bernalde marked this pull request as ready for review July 20, 2026 13:32

@iagoleal iagoleal left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The code works, but still has too many rough edges. We can smooth it out to make the transition easier for implementing more general domains later on.

Also, it is better to not keep two interfaces (domain vs domain_dim) doing roughly the same thing.

About the general weights SumConstraint, I would rather keep it for a future version. Building the partial sums can be quite expensive and produce huge MPOs. It is also not part of our v1 constraints assumptions.

Comment thread docs/src/examples.md Outdated
Comment thread src/backends/dmrg.jl Outdated
Comment thread src/backends/dmrg.jl Outdated
Comment thread src/backends/dmrg.jl Outdated
Comment on lines +76 to +78
- `domain` - The variable domain. Supported values are `[0, 1]` (the default)
and `[-1, 1]` for Ising spins. `domain_dim` remains available for the legacy
nonnegative integer domain `0:(domain_dim - 1)`; do not pass both keywords.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Subsume it as only the domain keyword.

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.

Updated the backend documentation to expose only the domain keyword.

Comment thread src/backends/dmrg.jl Outdated
Comment thread src/solution.jl Outdated
Comment thread src/solution.jl
Comment thread src/solver.jl Outdated
Comment thread test/spin_domains.jl Outdated
Comment thread README.md 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.

Maintainer review — Spin-domain support (Closes #106)

Solid, well-tested PR that completes the Ising/Spin child of #67. It adds a domain = [-1, 1] keyword to minimize/maximize, keeps [0, 1] as the default and domain_dim as the legacy qudit spelling, and rejects combining the two. I authored it, so this posts as COMMENT (GitHub blocks author APPROVE); main is not branch-protected and no review gate is reported, so nothing formally blocks merge. No blocking findings — two nonblocking notes inline.

What I verified

  • Physical domain values thread through correctly. op("D"; domain) makes the local operator diagm(domain), so D = diagm([-1, 1]) gives D² = I; the (already-diagonal-inclusive) tensorize then encodes the diagonal Q_ii x_i² as the constant tr(Q) shift that spins actually contribute, and the off-diagonals as x_i x_j. Hand-checked the Ising doctest (min −2x₁x₂ + 0.25x₁ − 0.5x₂ → −2.25 at [1, 1]), which is CI-verified.
  • Sampling/queries map indices ↔ values. sample indexes psi.domain[sample!(...)] (generalizing the old .- 1); coeff/prob map values back to 0-based positions and reject out-of-domain queries. The Solution{T,D} change keeps backward-compatible constructors defaulting to 0:(d-1).
  • Signed SumConstraint DFA is correct and permutation-safe. Building the state set as the subset-sum closure over all constrained weights means every prefix-sum in a real single pass is a reachable state regardless of site order, and the if … in states guard only drops transitions that can't occur in a valid pass — so no feasible assignment is rejected. The preprocess = true signed-constraint test (asserting psi.permutation != 1:3, feasibility, and a brute_force match) exercises exactly this and passes.
  • Infeasibility-as-status contract holds, now carrying the domain (infeasible_result(T, domain) → psi.domain == [-1, 1], still +Inf/-Inf, still throws on sample).

Findings — nonblocking

  1. src/projection_mpo.jl:390 — A = eltype(alphabet_values) is computed but unused.
  2. src/backends/dmrg.jl:53 — domain is matched by exact equality, so [1, -1] (the spin domain written high-spin-first) is rejected; worth accepting orderings or spelling out the ascending-order requirement in the error.

The signed-domain bond dimension grows with the number of reachable partial sums (vs rhs + 2 for nonnegative) — inherent to signed sums and now documented in constraints.md, so just noting it, not a concern.

Tests / CI

Full Pkg.test() passes locally (0 failures) and CI is green across all 11 checks on 9ad2747, including the new test/spin_domains.jl (quadratic/single-site/maximize/polynomial/constrained-with-preprocess/infeasible/validation, each cross-checked against brute_force(…; domain = [-1, 1])) and the Ising doctest.

Merge-readiness

No blocking issues; the two notes are optional polish. Correctly says Closes #106 (the sibling arbitrary-finite-domain and independent-domain children of #67 remain separate). As flagged in the PR body, open PRs #31/#44/#45 touch src/solution.jl/src/solver.jl and should reconcile after this lands. This account can't post a formal APPROVE on its own PR; no gate is reported as required.

Comment thread src/projection_mpo.jl Outdated
Comment thread src/backends/dmrg.jl Outdated
@bernalde

Copy link
Copy Markdown
Member Author

Addressed every unresolved review thread, including both non-blocking notes, in 17032021f496281634e21091aa74ab5d6d67edcb.

  • removed the unreleased domain_dim API and made domain the single public spelling, with 0:1 as the default
  • kept domain validation at the public boundary while making tensorization and projection internals explicit and collection-free
  • restored the v1 nonnegative-domain restriction for SumConstraint and removed the signed partial-sum construction
  • simplified Solution to one numeric type, removed implicit domain defaults, and preserved Boolean-only JuMP sample typing at its adapter boundary
  • accepted both Spin orderings, clarified the basis-position mapping, moved general domain tests out of the Spin suite, and kept Ising material in the inner docs

Validation: targeted projection/domain/Spin/constrained/JuMP suites passed; full Pkg.test() passed including Aqua and doctests; all 11 hosted checks are green on this head.

The formal review decision is still CHANGES_REQUESTED until the reviewer updates or dismisses that review.

@iagoleal
iagoleal merged commit 6fece79 into main Jul 20, 2026
11 checks passed
@iagoleal
iagoleal deleted the feat/issue-106-spin-domains branch July 20, 2026 19:34
@iagoleal iagoleal mentioned this pull request Jul 20, 2026
3 tasks
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.

Optimize Ising models / Spin domains

2 participants