API Reference
This page documents the public API of TenSolver.jl.
Optimization Functions
TenSolver.minimize — Function
minimize([Q::Matrix], [l::Vector], [c::Number] ; domain, kwargs...)
minimize(p::AbstractPolynomial ; domain, kwargs...)Solve a polynomial discrete optimization problem
min p(x)
s.t. x_i in domain
constraintsIn the matrix version, the objective is limited to quadratic forms x -> x'Qx + l'x + c. Missing arguments (quadratic, linear or constant term) are allowed and taken to be zero.
Return the optimal value E and a probability distribution ψ over optimal solutions. You can use sample to get an actual solution vector from ψ.
There are multiple backends available, selected through the keyword backend. By default, it uses DMRG to calculate the optimal solution.
Keyword arguments:
constraints :: AbstractVector{<:AbstractConstraint}- Experimental native Julia hard constraints. Defaults toAbstractConstraint[]. In constrained DMRG solves, TenSolver lowers each constraint to a projection MPO, solves the projected Hamiltonian, and returns a feasible sampled assignment. For polynomial objectives, constraints are expressed in the same order as theireffective_variables. If the constraints admit no solution at all, the solve does not error: it logs a warning and returns+Inftogether with an infeasibleSolution(seeis_feasible).domain- Possible variable values. Defaults to[0, 1]. Unconstrained DMRG optimization accepts any finite collection of real values; individual constraint types can impose narrower requirements. Use[-1, 1]for Ising spins. Domains are sorted and deduplicated before solving.iterations :: Int- Maximum iterations the solver should run. Defaults to10.cutoff :: Float64- Any absolute value below this threshold is considered zero. Defaults to1e-8. You can use this keyword to control the solver's accuracy vs resources trade-off.time_limit :: Float64- If specified, determines the maximum running time in seconds. It only determines whether a new iteration should start or not, thus the solver may run for longer if the threshold happens during an iteration.device = cpu- Accelerator device used during computation. See the section below for how to run on GPUs.preprocess :: Bool- Defaults tofalse. Iftrue, permute QUBO variables before constructing the MPS Hamiltonian so coupled variables are closer in the one-dimensional tensor order. Samples are returned in the caller's original variable order. This is an experimental feature and may be subject to changes.check_variance_every_iteration :: Int- Calculate and record the Hamiltonian variance every N iterations. Must be >= 1. Defaults to10.on_iteration :: Function- Called after each recorded iteration asf(psi::MPS; iteration, objective, bond_dim, elapsed_time).objectiveis the expected objective function ⟨ψ|H|ψ⟩ at this iteration. Use to collect statistics or serialize intermediate states.psiis the MPS for that iteration. Default:nothing(no callback).callback_every :: Int- Invoke the callback every N iterations. Must be >= 1. Default:1.backend- Solver backend. Defaults to the current DMRG implementation. Usebackend = :dmrgorbackend = DMRGBackend()to select it explicitly. Other backends are reserved for optional extensions.Other keywords might be available depending on the chosen backend. See the documentation for each backend for comprehensive lists.
Some keywords, such as
constraintsanddomain, may have limited support depending on the backend.
The returned Solution carries per-iteration convergence data in solution.stats.
Provably infeasible constrained models are reported as a status: minimize logs a warning and returns +Inf (the minimum over an empty feasible set) together with an infeasible Solution, which cannot be sampled. Check it with is_feasible.
Running on GPU:
The optional keyword device controls whether the solver should run on CPU or GPU. For using a GPU, you can import the respective package, e.g. CUDA.jl, and pass its accelerator as argument.
import CUDA
minimize(Q; device = CUDA.cu)
import Metal
minimize(Q; device = Metal.mtl)See also maximize.
TenSolver.maximize — Function
maximize(Q::Matrix[, l::Vector[, c::Number; kwargs...)
maximize(p::AbstractPolynomial; kwargs...)Solve the Quadratic Unconstrained Binary Optimization problem for maximization.
max b'Qb + l'b + c
s.t. b_i in {0, 1}All keywords accepted by minimize can also be used for maximization problems. Provably infeasible constrained models return -Inf (the supremum over an empty feasible set) together with an infeasible Solution.
See also minimize.
Solver Backends
TenSolver.AbstractTenSolverBackend — Type
AbstractTenSolverBackendAbstract solver backend marker for TenSolver implementations.
Backends must provide backend-specific minimize methods for the normalized optimization inputs they support. Matrix backends implement minimize(::MyBackend, Q::AbstractMatrix, l, c; kwargs...); polynomial backends implement minimize(::MyBackend, p::AbstractPolynomial; kwargs...). Extensions that support symbolic selection must also define normalize_backend(::Val{:my_backend}) = MyBackend(...).
See also
TenSolver.DMRGBackend — Type
DMRGBackend()Select TenSolver's default ITensorMPS DMRG backend.
TenSolver.normalize_backend — Function
normalize_backend(backend)Normalize a user-facing backend selector into a backend object.
Backends can support backend = :my_backend by defining normalize_backend(::Val{:my_backend}) = MyBackend(...).
Solution
TenSolver.Solution — Type
Solution{T}The result of running minimize or maximize: an MPS wave function over the optimal solution space, together with per-iteration convergence stats.
Use sample to draw vectors from it.
Fields
tensor: the underlying MPS, ornothingwhen the model is infeasible.domain: possible variable values.permutation: original variable index represented by each tensor site.stats: per-iteration convergence stats. SeeSolverStatistics.
Provably infeasible models produce a Solution with no MPS and empty stats vectors; check with is_feasible before sampling.
TenSolver.SolverStatistics — Type
SolverStatistics{T}Fields
energies: per-iteration calculated energy/objective value;bond_dims: per-iteration solution maximum bond dimension;elapsed_times: per-iteration total until the iteration completed;variances: per-iteration Hamiltonian variance, ornothingon iterations where the variance was not checked;max_bonds: structure containing bond dimensions for multiple tensors used throughout the iterationinitial_state: bond for the initial MPS guess;objective: bond for the MPO representing the objective functionH;projections: bonds for the MPOs representing each constraint;hamiltonian: bond for the MPO representing the actual HamiltonianP'HPrepresenting both objective and constraints;
Sampling Functions
TenSolver.sample — Function
sample(psi)Sample a vector from a (quantum) probability distribution.
Throw a DomainError when psi is infeasible (see is_feasible), since there is no solution to query.
Boolean/Spin Conversions
TenSolver.bool_to_spin — Function
bool_to_spin(x)Convert a Boolean bit vector x_i in {0, 1} to Ising spins s_i in {-1, +1} using QUBOTools' BoolDomain => SpinDomain cast.
TenSolver.spin_to_bool — Function
spin_to_bool(s)Convert an Ising spin vector s_i in {-1, +1} to Boolean bits x_i in {0, 1} using QUBOTools' SpinDomain => BoolDomain cast.
TenSolver.qubo_to_ising — Function
qubo_to_ising(Q[, l[, c]]; convention = :spin)
qubo_to_ising(form; convention = :spin)Convert TenSolver's Boolean QUBO objective
dot(x, Q, x) + dot(l, x) + cwith x_i in {0, 1} into a sparse QUBOTools form in SpinDomain. For non-symmetric matrices, the QUBOTools form constructor preserves TenSolver's dot(x, Q, x) convention by storing the effective pair coefficient Q[i, j] + Q[j, i] once in the upper triangle.
The Boolean-to-spin conversion introduces halves and quarters. Integer coefficient inputs therefore return floating-point forms, while rational inputs preserve exact rational arithmetic.
The returned form includes the constant offset, so
dot(x, Q, x) + dot(l, x) + c == QUBOTools.value(bool_to_spin(x), qubo_to_ising(Q, l, c))for every Boolean vector x.
TenSolver.ising_to_qubo — Function
ising_to_qubo(form)
ising_to_qubo(J, h[, offset])Convert a QUBOTools spin-domain Ising form back to a sparse Boolean-domain QUBOTools form. The matrix/vector method first builds a QUBOTools spin form from
dot(s, J, s) + dot(h, s) + offsetwith s_i in {-1, +1}. QUBOTools folds diagonal quadratic spin terms into the constant offset and stores each off-diagonal unordered pair once in the upper triangle.
As with qubo_to_ising, integer coefficient inputs return floating-point forms when the conversion introduces fractional coefficients, while rational inputs preserve exact rational arithmetic.
Constraints
Hard constraints are enforced by lowering each one to an exact projection MPO, following CoTenN (Sharma, Peng, Dangwal, and Achour, "CoTenN: Constrained Optimization with Tensor Networks," PLDI 2026). See Constrained Optimization for a worked example.
TenSolver.AbstractConstraint — Type
AbstractConstraintSupertype for conditions over a vector x addressed by 1-based site indices.
API
These are an interface for feasibility constraints. Any concrete subtype is expected to implement
Constraint types are experimental. They currently provide TenSolver's Julia lowering target for projection-MPO constrained solves; future JuMP/MOI integration may change which constraint abstraction is considered stable public API.
See also SumConstraint, SumModConstraint, NotEqualsConstraint, AssignmentConstraint, and RelationConstraint.
TenSolver.SumConstraint — Type
SumConstraint{T} <: AbstractConstraint
SumConstraint(sites, weights, relation, rhs)
SumConstraint(sites, weights, rhs; relation)Weighted-sum constraint over a vector x:
sum(weights[i] * x[sites[i]] for i in eachindex(sites)) relation rhs.sites must be unique positive integers, weights must be the same length as sites and only contain nonnegative integers, and relation must be one of :(==), :(!=), :(<=), or :(>=).
Warning: The == and != relations use exact arithmetic comparison.
TenSolver.SumModConstraint — Type
SumModConstraint{T} <: AbstractConstraint
SumModConstraint(sites, weights, rhs; mod)Modular weighted-sum constraint over a vector x:
sum(weights[i] * x[sites[i]] for i in eachindex(sites)) ≡ rhs (mod m).sites must be unique positive integers, weights must be the same length as sites, weights and rhs must be integer-valued, and mod must be a positive integer.
Weights and the rhs are stored as their least nonnegative residues modulo mod.
TenSolver.NotEqualsConstraint — Type
NotEqualsConstraint <: AbstractConstraint
NotEqualsConstraint(sites, values)Excludes a single assignment over a vector x: at least one component of x[sites] must differ from values. Equivalently, the partial assignment x[sites] == values is forbidden.
sites must be unique positive integers, and values must have the same length as sites.
TenSolver.AssignmentConstraint — Type
AssignmentConstraint{T} <: AbstractConstraint
AssignmentConstraint(sites, values, relation, rhs)Restrict how many sites satisfy x[site] in values, i.e.,
count(x[site] in values for site in sites) relation rhs.rhs must be a nonnegative integer. The count and rhs are stored independently of the numeric element type of values.
A common application is to restrict exactly one variable to be a certain value,
ExactlyOne(sites, value) = AssignmentConstraint(sites, [value], :(==), 1)TenSolver.RelationConstraint — Type
RelationConstraint <: AbstractConstraint
RelationConstraint(left_site, relation, right_site)Pairwise constraint over a vector x: x[left_site] relation x[right_site].
left_site and right_site must be distinct positive integers, and relation must be one of :(==), :(!=), :(<=), or :(>=).
TenSolver.is_feasible — Function
is_feasible(x, constraint::AbstractConstraint)Test whether the vector x satisfies a single constraint.
TenSolver.constraint_sites — Function
constraint_sites(constraint::AbstractConstraint)Access the site indices stored in the constraint.
Utility Functions
Internal Functions
These functions are part of the internal implementation and are not exported. They are documented here for advanced users who may need to understand the internals. Notice: As unexported method and types, they are subject to change without warning.
Objective Construction
TenSolver.tensorize — Function
tensorize(p)Turn a polynomial function action on bitstrings into an equivalent MPO Hamiltonian acting on Qudit sites. The conversion consists of exchanging each integer variable x_i for a matrix P_i whose eigenvalues represent its feasible set K_i.
∑ Q_ij x_i x_j + ∑ l_i x_i --> H = Σ Q_ij D_i D_j + ∑ l_i D_iTenSolver.qmatrix_permutation — Function
qmatrix_permutation(Q; cutoff=0)Return a deterministic permutation that places coupled QUBO variables closer together in the one-dimensional MPS ordering.
cutoff controls which quadratic couplings are included in the ordering graph: an undirected edge between variables i and j is used when abs(Q[i, j] + Q[j, i]) > cutoff. The default cutoff = 0 preserves every nonzero coupling for callers that only want the ordering.
The returned permutation maps each tensor site to its original variable index: entry k is the original QUBO variable represented by tensor site k.
Examples
Q = [0.0 0.0 1.0;
0.0 0.0 0.0;
1.0 0.0 0.0]
permutation = qmatrix_permutation(Q)
Q[permutation, permutation]
# output
3×3 Matrix{Float64}:
0.0 1.0 0.0
1.0 0.0 0.0
0.0 0.0 0.0TenSolver.preprocess_qubo — Function
preprocess_qubo(Q, l, cutoff)Permute QUBO variables before Hamiltonian construction so coupled variables are closer in the one-dimensional tensor order.
Returns (Qp, lp, permutation), where Qp and lp are reordered for tensor-site order and permutation[k] is the caller's original variable index represented by tensor site k.
Standalone qmatrix_permutation uses cutoff = 0 by default so direct callers keep every nonzero coupling. The solve path passes the solver cutoff, which defaults to 1e-8, so numerically tiny couplings are ignored during preprocessing.
MPO Construction
TenSolver.DFA — Type
DFA{S, A}Step-dependent deterministic finite automaton.
Fields:
states: DFA states, used to define the MPO bond dimension.alphabet: local symbols, ordered to match the physical basis positions.initial: start state.accepting: set of accepting states.transitions: one transition table per site; each table maps(state, symbol)to the next state. Missing entries are rejected.
TenSolver.constraint_to_dfa — Function
constraint_to_dfa(constraint, n, alphabet)Build a DFA recognizing constraint with transitions for n steps. The alphabet parameter represents the domain for a constraint's variables.
TenSolver.dfa_to_mpo — Function
dfa_to_mpo([T], dfa, sites)Build an exact diagonal projection MPO from a step-dependent DFA.
The physical index basis positions are matched against dfa.alphabet in order: alphabet[k] corresponds to local basis state k.
The MPO bond dimension equals length(dfa.states).
TenSolver.projection_mpo — Function
projection_mpo([T], constraint, sites; domain)Build a projection MPO representing a constraint applicable to any MPS over sites.
The diagonal entry is one(T) for computational basis states satisfying constraint, and zero(T) otherwise. The current construction is exact and uncompressed. Constraint site numbers use the same 1-based register indexing as sites.
Known constraints
SumConstraintuses a exact integer partial-sum automaton. For a constraint with rhsk, its maximum bond dimension isk+2.SumModConstraintuses a modular partial-sum automaton. Itsmresidue states give it bond dimensionm.NotEqualsConstraintuses a MPO with bond dimension2, independently of the rhs.AssignmentConstraintuses a membership counting automaton. For rhsk, the maximum bond dimension isk+2.RelationConstraintuses a MPO with bond dimension2, independently of the compared site positions.
TenSolver.projection_mpos — Function
projection_mpos([T], constraints, sites; domain)Build a list of projection MPOs representing constraints applicable to any MPS over sites.
This is a convenience wrapper around projection_mpo. T controls the numeric element type of the assembled MPO tensors.
Projected Hamiltonian Construction
TenSolver.project_hamiltonian — Function
project_hamiltonian(H, projections; formulation=:commuting, cutoff=1e-8, kwargs...)Project a Hamiltonian MPO with one or more projection MPOs.
If Q = P₁ * ⋯ * Pₙ is the combined projector, the effective Hamiltonian has the semantics Q' * H * Q.
With the default formulation=:commuting, H and all Pᵢ must be mutually commuting, while each Pᵢ must an orthogonal projection (Hermitian and idempotent). The construction then simplifies to H * Q, with bond dimension bounded by the product of H's links and each projection link. TenSolver's objective and constraint MPOs satisfy these assumptions because they are diagonal.
Use formulation=:sandwich for general, potentially noncommuting MPOs. It constructs Q' * H * Q directly, so each projection link contributes twice to the bond-dimension bound.
TenSolver.project_state — Function
project_state(psi, projections; cutoff=1e-8, kwargs...)Apply one or more diagonal projection MPOs to an MPS.
The result has zero amplitude on basis states rejected by any projection, while keeping the original unprimed site indices so it can be used as a DMRG input state.
Index
TenSolver.AbstractConstraintTenSolver.AbstractTenSolverBackendTenSolver.AssignmentConstraintTenSolver.DFATenSolver.DMRGBackendTenSolver.NotEqualsConstraintTenSolver.RelationConstraintTenSolver.SolutionTenSolver.SolverStatisticsTenSolver.SumConstraintTenSolver.SumModConstraintBase.inTenSolver.bool_to_spinTenSolver.constraint_sitesTenSolver.constraint_to_dfaTenSolver.dfa_to_mpoTenSolver.is_feasibleTenSolver.ising_to_quboTenSolver.maximizeTenSolver.minimizeTenSolver.normalize_backendTenSolver.preprocess_quboTenSolver.project_hamiltonianTenSolver.project_stateTenSolver.projection_mpoTenSolver.projection_mposTenSolver.qmatrix_permutationTenSolver.qubo_to_isingTenSolver.sampleTenSolver.spin_to_boolTenSolver.tensorize