Problem API#

Problem data (C, A, b): the container, the cost hierarchy, and the constraint-operator hierarchy with its explicit cvxpy encoding.

sdplab.problem.SDPProblem

Base representation of a conic problem with linear equality constraints.

sdplab.problem.as_member

Return x as a raw member of space represented in ctx.

sdplab.problem.ConstraintOp

Linear constraint operator \(\mathcal{A}: \operatorname{dom} \to \operatorname{cod}\) with a cvxpy encoding.

sdplab.problem.MatrixFreeConstraintOp

Constraint operator defined only by apply/rapply (no stored matrix).

sdplab.problem.WrappedConstraintOp

Adapt an arbitrary LinOp into a constraint operator.

sdplab.problem.DenseConstraintOp

Constraint operator backed by a dense coordinate tensor.

sdplab.problem.SparseConstraintOp

Constraint operator backed by a sparse coordinate matrix.

sdplab.problem.Cost

Abstract cost \(X \mapsto \langle C, X\rangle\) on a Jordan domain.

sdplab.problem.ElementCost

A cost stored directly as a domain element (works on any EJA, incl.

sdplab.problem.HermitianCost

Self-adjoint SDP cost represented as an operator and matrix-space element.

sdplab.problem.DenseHermitianCost

Hermitian cost backed by a dense matrix.

sdplab.problem.SparseHermitianCost

Hermitian cost backed by a sparse matrix.

class sdplab.problem.SDPProblem(C, A, b, ctx=None)[source]#

Bases: ContextBound

Base representation of a conic problem with linear equality constraints.

An instance stores the triple \((C, \mathcal{A}, b)\) from the standard primal problem

\[\begin{split}\min_{X \in \operatorname{dom}(\mathcal{A})}\ &\langle C, X\rangle \\ \text{s.t.}\quad &\mathcal{A}X = b, \\ &X \succeq 0.\end{split}\]

Here \(C \in \operatorname{dom}(\mathcal{A})\), the linear constraint operator is \(\mathcal{A} : \operatorname{dom}(\mathcal{A}) \to \operatorname{cod}(\mathcal{A})\), and \(b \in \operatorname{cod}(\mathcal{A})\). Both \(C\) and \(b\) are stored as plain elements of their spaces; primal and dual variables are likewise plain elements of dom and cod.

Think of this class as the common language between modeling code and solvers. It does not decide which algorithm to use. It only stores the mathematical data and the operations that every solver needs.

Parameters:
  • C (Cost | ArrayLike)

  • A (LinOp)

  • b (ArrayLike)

  • ctx (Context | str | None)

__init__(C, A, b, ctx=None)[source]#

Create the problem data \((C, \mathcal{A}, b)\).

Parameters:
  • C (Cost | ArrayLike) – Objective in \(\operatorname{dom}(\mathcal{A})\) — a prepared Cost, a dense or sparse matrix on a Hermitian domain, or a plain domain element (a tree of blocks on a tree domain). Arrays are wrapped through _dispatch_cost().

  • A (LinOp) – Linear constraint operator \(\mathcal{A} : \operatorname{dom}(\mathcal{A}) \to \operatorname{cod}(\mathcal{A})\).

  • b (ArrayLike) – Right-hand side in \(\operatorname{cod}(\mathcal{A})\).

  • ctx (Context | str | None) – Optional backend context. When supplied, A and array data are converted to this context.

Raises:

TypeError or ValueError – If \(C\) is not a member of \(\operatorname{dom}(\mathcal{A})\) or \(b\) is not a member of \(\operatorname{cod}(\mathcal{A})\). This catches many modeling mistakes early: wrong matrix size, wrong dtype, or a constraint vector with the wrong length.

property dom: EuclideanJordanAlgebraSpace#

Return \(\operatorname{dom}(\mathcal{A})\), the primal space containing \(C\) and \(X\).

property cod: InnerProductSpace#

Return \(\operatorname{cod}(\mathcal{A})\), the space containing \(\mathcal{A}X\), \(b\), and \(y\).

primal_objective(X)[source]#

Evaluate the primal objective \(\langle C, X\rangle\) at a dom element.

In dense symmetric or Hermitian matrix spaces this is the trace objective \(\operatorname{Re}\operatorname{Tr}[C X]\).

Parameters:

X (Any)

Return type:

Any

dual_objective(y)[source]#

Evaluate the linear dual objective term \(\langle b, y\rangle\) at a cod element.

Parameters:

y (Any)

Return type:

Any

dual_slack(y)[source]#

Return the dual slack \(\mathcal{A}^\dagger y - C\) in dom.

Parameters:

y (Any)

Return type:

Any

feasibility_gap(X)[source]#

Return \(\mathcal{A}X - b\), the equality-constraint residual in cod.

Parameters:

X (Any)

Return type:

Any

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property dtype: Any#

Return the default dtype associated with this object’s context.

property ops: BackendOps#

Return backend operations associated with this object’s context.

sdplab.problem.as_member(space, x, ctx)[source]#

Return x as a raw member of space represented in ctx.

Accepts backend arrays, array-likes, raw trees, and bound TreeElement values; validates membership according to the context’s check level.

Parameters:
  • space (InnerProductSpace)

  • x (Any)

  • ctx (Context)

Return type:

Any

class sdplab.problem.ConstraintOp(dom, cod, ctx=None)[source]#

Bases: LinOp[EuclideanJordanAlgebraSpace, InnerProductSpace]

Linear constraint operator \(\mathcal{A}: \operatorname{dom} \to \operatorname{cod}\) with a cvxpy encoding.

Beyond the LinOp action, a constraint operator knows how to present itself to cvxpy as a list of per-constraint matrices (see the module docstring). Concrete backends:

Parameters:
  • dom (Domain)

  • cod (Codomain)

  • ctx (Context | str | None)

abstractmethod to_cvxpy()[source]#

Return the per-constraint matrices [A_0, ..., A_{m-1}].

Each A_i lives in the domain matrix space and is oriented so that the i-th equality reads \(\operatorname{Re}\operatorname{Tr}[A_i X] = b_i\).

Return type:

list[DenseArray | SparseArray]

rhs_to_cvxpy(b)[source]#

Return the real right-hand side matching the rows of to_cvxpy().

The default flattens b into cod coordinates and takes the real part – correct when the codomain is a real coordinate space of m scalar constraints. Operators with a structured codomain (e.g. QOT’s Hermitian marginals reduced to real generators) override this.

Parameters:

b (Any)

Return type:

DenseArray

dual_from_cvxpy(y)[source]#

Reassemble a per-constraint value vector into a cod element.

Inverse of the row layout produced by rhs_to_cvxpy(). The default unflattens y back into cod; structured codomains override. The caller (solver) is responsible for the dual sign.

Parameters:

y (DenseArray)

Return type:

Any

classmethod from_linop(op)[source]#
Parameters:

op (LinOp)

Return type:

ConstraintOp

property A: Any#

Native numerical representation of this operator.

Concrete subclasses may choose the representation that best matches their storage model: for example, dense operators return a dense array while sparse operators return their sparse matrix. Matrix-free or lazy operators generally do not have such a representation and should leave this property unimplemented. Use to_dense() when a dense tensor materialization is explicitly required.

property H: LinOp#

Hermitian-adjoint view of this linear operator.

Returns:

Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).

Return type:

LinOp

adjoint()#

Return the Hermitian-adjoint view of this linear operator.

Return type:

LinOp

adjoint_apply(y)#

Apply the adjoint of this linear operator to y.

Parameters:

y (Any)

Return type:

Any

abstractmethod apply(x)#

Apply the forward map to an element of self.domain.

Parameters:

x (Any)

Return type:

Any

assert_codomain(y)#

Raise if y is not in the codomain.

Parameters:

y (Any)

Return type:

None

assert_domain(x)#

Raise if x is not in the domain.

Parameters:

x (Any)

Return type:

None

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

property codomain: Codomain#

Codomain space of this linear operator.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property domain: Domain#

Domain space of this linear operator.

property dtype: Any#

Return the default dtype associated with this object’s context.

fuse(*, materialize=False)#

Return an equivalent operator with fusible sub-expressions multiplied out.

Tier-2 lazy-algebra simplification ([ADR-021](021_lazy_operator_algebra_and_simplification.md)): collapse each maximal subtree of densely-fusible operators into a single materialized operator — for example, a composition of dense operators becomes one DenseLinOp holding the matrix product \(M_A M_B\) — while leaving matrix-free and other non-materializable leaves intact.

This is an explicit, opt-in materialization. The result is mathematically equal to self but only within floating-point rounding: fusing reassociates the arithmetic (multiplying matrices then applying differs from applying in sequence at the ulp level), so equality holds up to tolerance, not bit-for-bit. The fused operator preserves the domain, codomain, context, and scalar-field/dtype identity. A leaf operator returns itself.

Parameters:

materialize (bool, optional) – With the default False, a matrix-free operand ([ADR-008](008_linop_subclasses.md)) is never densified: it remains a lazy leaf and only breaks a fusible run. With True the caller explicitly accepts giving up the matrix-free contract: a matrix-free operand is densified into a DenseLinOp (via its to_dense basis probe, which may be expensive), allowing the enclosing expression to collapse to a single dense operator.

Returns:

A fused operator with the same action as self (up to rounding).

Return type:

LinOp

is_hermitian()#

Return whether this operator is structurally Hermitian when known.

Returns:

True or False when the subclass can verify the structure cheaply, otherwise None for unknown or matrix-free operators.

Return type:

bool | None

property ops: BackendOps#

Return backend operations associated with this object’s context.

abstractmethod rapply(y)#

Apply the adjoint map to an element of self.codomain.

Parameters:

y (Any)

Return type:

Any

rvapply(ys)#

Apply the adjoint over a leading batch axis. Input must have shape (N,) + codomain.shape; use moveaxis for other layouts.

Parameters:

ys (Any)

Return type:

Any

to_dense()#

Materialize this operator as a dense backend array.

The returned array has shape self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.

Return type:

Any

to_matrix()#

Materialize this operator as a 2D dense coordinate matrix.

The returned array has shape (prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and calls vapply() once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.

Return type:

Any

to_sparse()#
vapply(xs)#

Apply over a leading batch axis. Input must have shape (N,) + domain.shape; use moveaxis for other layouts.

Parameters:

xs (Any)

Return type:

Any

class sdplab.problem.MatrixFreeConstraintOp(dom, cod, ctx=None)[source]#

Bases: ConstraintOp

Constraint operator defined only by apply/rapply (no stored matrix).

The default to_cvxpy() materializes each constraint matrix as \(A_i = \mathcal{A}^\dagger e_i\), the adjoint applied to the i-th codomain basis vector. For a Hermitian domain this is exactly the matrix for which \(\operatorname{Tr}[A_i X] = (\mathcal{A}X)_i\). Subclasses with a cheaper or reduced encoding (e.g. QOTConstraintOp) override to_cvxpy(), rhs_to_cvxpy(), and dual_from_cvxpy().

The default assumes a real coordinate codomain, so that the unit vectors e_i are valid codomain members.

Parameters:
  • dom (Domain)

  • cod (Codomain)

  • ctx (Context | str | None)

to_cvxpy()[source]#

Materialize [A_i = adjoint(e_i)] over the codomain coordinate basis.

Return type:

list[DenseArray]

property A: Any#

Native numerical representation of this operator.

Concrete subclasses may choose the representation that best matches their storage model: for example, dense operators return a dense array while sparse operators return their sparse matrix. Matrix-free or lazy operators generally do not have such a representation and should leave this property unimplemented. Use to_dense() when a dense tensor materialization is explicitly required.

property H: LinOp#

Hermitian-adjoint view of this linear operator.

Returns:

Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).

Return type:

LinOp

adjoint()#

Return the Hermitian-adjoint view of this linear operator.

Return type:

LinOp

adjoint_apply(y)#

Apply the adjoint of this linear operator to y.

Parameters:

y (Any)

Return type:

Any

abstractmethod apply(x)#

Apply the forward map to an element of self.domain.

Parameters:

x (Any)

Return type:

Any

assert_codomain(y)#

Raise if y is not in the codomain.

Parameters:

y (Any)

Return type:

None

assert_domain(x)#

Raise if x is not in the domain.

Parameters:

x (Any)

Return type:

None

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

property codomain: Codomain#

Codomain space of this linear operator.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property domain: Domain#

Domain space of this linear operator.

property dtype: Any#

Return the default dtype associated with this object’s context.

dual_from_cvxpy(y)#

Reassemble a per-constraint value vector into a cod element.

Inverse of the row layout produced by rhs_to_cvxpy(). The default unflattens y back into cod; structured codomains override. The caller (solver) is responsible for the dual sign.

Parameters:

y (DenseArray)

Return type:

Any

classmethod from_linop(op)#
Parameters:

op (LinOp)

Return type:

ConstraintOp

fuse(*, materialize=False)#

Return an equivalent operator with fusible sub-expressions multiplied out.

Tier-2 lazy-algebra simplification ([ADR-021](021_lazy_operator_algebra_and_simplification.md)): collapse each maximal subtree of densely-fusible operators into a single materialized operator — for example, a composition of dense operators becomes one DenseLinOp holding the matrix product \(M_A M_B\) — while leaving matrix-free and other non-materializable leaves intact.

This is an explicit, opt-in materialization. The result is mathematically equal to self but only within floating-point rounding: fusing reassociates the arithmetic (multiplying matrices then applying differs from applying in sequence at the ulp level), so equality holds up to tolerance, not bit-for-bit. The fused operator preserves the domain, codomain, context, and scalar-field/dtype identity. A leaf operator returns itself.

Parameters:

materialize (bool, optional) – With the default False, a matrix-free operand ([ADR-008](008_linop_subclasses.md)) is never densified: it remains a lazy leaf and only breaks a fusible run. With True the caller explicitly accepts giving up the matrix-free contract: a matrix-free operand is densified into a DenseLinOp (via its to_dense basis probe, which may be expensive), allowing the enclosing expression to collapse to a single dense operator.

Returns:

A fused operator with the same action as self (up to rounding).

Return type:

LinOp

is_hermitian()#

Return whether this operator is structurally Hermitian when known.

Returns:

True or False when the subclass can verify the structure cheaply, otherwise None for unknown or matrix-free operators.

Return type:

bool | None

property ops: BackendOps#

Return backend operations associated with this object’s context.

abstractmethod rapply(y)#

Apply the adjoint map to an element of self.codomain.

Parameters:

y (Any)

Return type:

Any

rhs_to_cvxpy(b)#

Return the real right-hand side matching the rows of to_cvxpy().

The default flattens b into cod coordinates and takes the real part – correct when the codomain is a real coordinate space of m scalar constraints. Operators with a structured codomain (e.g. QOT’s Hermitian marginals reduced to real generators) override this.

Parameters:

b (Any)

Return type:

DenseArray

rvapply(ys)#

Apply the adjoint over a leading batch axis. Input must have shape (N,) + codomain.shape; use moveaxis for other layouts.

Parameters:

ys (Any)

Return type:

Any

to_dense()#

Materialize this operator as a dense backend array.

The returned array has shape self.codomain.shape + self.domain.shape. The default implementation is intended for small problems, debugging, and tests. It materializes the full coordinate matrix, so subclasses that already store a dense or sparse matrix should override this method for efficiency.

Return type:

Any

to_matrix()#

Materialize this operator as a 2D dense coordinate matrix.

The returned array has shape (prod(self.codomain.shape), prod(self.domain.shape)). The default implementation builds a batch of standard basis vectors and calls vapply() once. If a space cannot batch-flatten or batch-unflatten its representation, it falls back to a safe Python loop. This method is for small/testing use; concrete storage-backed subclasses should override it when they can expose a matrix directly.

Return type:

Any

to_sparse()#
vapply(xs)#

Apply over a leading batch axis. Input must have shape (N,) + domain.shape; use moveaxis for other layouts.

Parameters:

xs (Any)

Return type:

Any

class sdplab.problem.WrappedConstraintOp(op, ctx=None)[source]#

Bases: MatrixFreeConstraintOp

Adapt an arbitrary LinOp into a constraint operator.

This is the fallback used by sdplab.problem._base._dispatch_constraint() for a user-supplied operator that is neither a DenseLinOp nor a SparseLinOp – a hand-written matrix-free operator, an algebra expression, and so on. The wrapper only forwards apply/rapply, so the operator stays matrix-free for the first-order solvers; the per-constraint matrices are built lazily by the inherited MatrixFreeConstraintOp.to_cvxpy() and only if the cvxpy backend is actually called.

As for every MatrixFreeConstraintOp, the default cvxpy encoding assumes a real coordinate codomain. Wrap a structured-codomain operator in a subclass that overrides to_cvxpy(), rhs_to_cvxpy(), and dual_from_cvxpy().

Parameters:
  • op (LinOp)

  • ctx (Context | str | None)

__init__(op, ctx=None)[source]#

Wrap op, converting it (and its spaces) onto the resolved context.

Parameters:
  • op (LinOp)

  • ctx (Context | str | None)

apply(x)[source]#

Apply the wrapped operator (its own membership checks still run).

Parameters:

x (Any)

Return type:

Any

rapply(y)[source]#

Apply the adjoint of the wrapped operator.

Parameters:

y (Any)

Return type:

Any

to_dense()[source]#

Delegate dense materialization, keeping any efficient override.

Return type:

DenseArray

to_matrix()[source]#

Delegate flat-matrix materialization, keeping any efficient override.

Return type:

DenseArray

classmethod from_linop(op)[source]#

Wrap any LinOp without materializing it.

Parameters:

op (LinOp)

Return type:

WrappedConstraintOp

property A: Any#

Native numerical representation of this operator.

Concrete subclasses may choose the representation that best matches their storage model: for example, dense operators return a dense array while sparse operators return their sparse matrix. Matrix-free or lazy operators generally do not have such a representation and should leave this property unimplemented. Use to_dense() when a dense tensor materialization is explicitly required.

property H: LinOp#

Hermitian-adjoint view of this linear operator.

Returns:

Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).

Return type:

LinOp

adjoint()#

Return the Hermitian-adjoint view of this linear operator.

Return type:

LinOp

adjoint_apply(y)#

Apply the adjoint of this linear operator to y.

Parameters:

y (Any)

Return type:

Any

assert_codomain(y)#

Raise if y is not in the codomain.

Parameters:

y (Any)

Return type:

None

assert_domain(x)#

Raise if x is not in the domain.

Parameters:

x (Any)

Return type:

None

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

property codomain: Codomain#

Codomain space of this linear operator.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property domain: Domain#

Domain space of this linear operator.

property dtype: Any#

Return the default dtype associated with this object’s context.

dual_from_cvxpy(y)#

Reassemble a per-constraint value vector into a cod element.

Inverse of the row layout produced by rhs_to_cvxpy(). The default unflattens y back into cod; structured codomains override. The caller (solver) is responsible for the dual sign.

Parameters:

y (DenseArray)

Return type:

Any

fuse(*, materialize=False)#

Return an equivalent operator with fusible sub-expressions multiplied out.

Tier-2 lazy-algebra simplification ([ADR-021](021_lazy_operator_algebra_and_simplification.md)): collapse each maximal subtree of densely-fusible operators into a single materialized operator — for example, a composition of dense operators becomes one DenseLinOp holding the matrix product \(M_A M_B\) — while leaving matrix-free and other non-materializable leaves intact.

This is an explicit, opt-in materialization. The result is mathematically equal to self but only within floating-point rounding: fusing reassociates the arithmetic (multiplying matrices then applying differs from applying in sequence at the ulp level), so equality holds up to tolerance, not bit-for-bit. The fused operator preserves the domain, codomain, context, and scalar-field/dtype identity. A leaf operator returns itself.

Parameters:

materialize (bool, optional) – With the default False, a matrix-free operand ([ADR-008](008_linop_subclasses.md)) is never densified: it remains a lazy leaf and only breaks a fusible run. With True the caller explicitly accepts giving up the matrix-free contract: a matrix-free operand is densified into a DenseLinOp (via its to_dense basis probe, which may be expensive), allowing the enclosing expression to collapse to a single dense operator.

Returns:

A fused operator with the same action as self (up to rounding).

Return type:

LinOp

is_hermitian()#

Return whether this operator is structurally Hermitian when known.

Returns:

True or False when the subclass can verify the structure cheaply, otherwise None for unknown or matrix-free operators.

Return type:

bool | None

property ops: BackendOps#

Return backend operations associated with this object’s context.

rhs_to_cvxpy(b)#

Return the real right-hand side matching the rows of to_cvxpy().

The default flattens b into cod coordinates and takes the real part – correct when the codomain is a real coordinate space of m scalar constraints. Operators with a structured codomain (e.g. QOT’s Hermitian marginals reduced to real generators) override this.

Parameters:

b (Any)

Return type:

DenseArray

rvapply(ys)#

Apply the adjoint over a leading batch axis. Input must have shape (N,) + codomain.shape; use moveaxis for other layouts.

Parameters:

ys (Any)

Return type:

Any

to_cvxpy()#

Materialize [A_i = adjoint(e_i)] over the codomain coordinate basis.

Return type:

list[DenseArray]

to_sparse()#
vapply(xs)#

Apply over a leading batch axis. Input must have shape (N,) + domain.shape; use moveaxis for other layouts.

Parameters:

xs (Any)

Return type:

Any

class sdplab.problem.DenseConstraintOp(A, dom, cod=None, ctx=None)[source]#

Bases: ConstraintOp, DenseLinOp

Constraint operator backed by a dense coordinate tensor.

The stored tensor T of shape cod.shape + dom.shape acts by \((\mathcal{A}X)_i = \sum_{jk} T_{i,jk} X_{jk}\) (a Frobenius pairing, no conjugation). To present the trace convention \(\operatorname{Tr}[A_i X] = (\mathcal{A}X)_i\), the i-th constraint matrix is the transpose of the i-th tensor slice, A_i = T_i^{T}.

Note the conjugation carefully: for a genuinely complex Hermitian slice the transpose already equals the conjugate, T_i^{T} = \overline{T_i}, and that is the correct Hermitian matrix for which Tr[A_i X] reproduces the operator. A conjugate-transpose T_i^{H} would instead return T_i itself and break the identity. The transpose is a no-op only for the real symmetric constraint matrices of a real SDP.

Parameters:
  • A (DenseArray)

  • dom (Domain)

  • cod (Codomain | None)

  • ctx (Context | str | None)

to_cvxpy()[source]#

Return the per-constraint matrices [A_0, ..., A_{m-1}].

Each A_i lives in the domain matrix space and is oriented so that the i-th equality reads \(\operatorname{Re}\operatorname{Tr}[A_i X] = b_i\).

Return type:

list[DenseArray]

classmethod from_linop(op)[source]#

Wrap an existing DenseLinOp as a constraint operator.

Parameters:

op (DenseLinOp)

Return type:

DenseConstraintOp

property A: DenseArray#

Stored dense tensor representation of this operator.

The returned array has shape self.codomain.shape + self.domain.shape and is the same object supplied at construction.

property H: LinOp#

Hermitian-adjoint view of this linear operator.

Returns:

Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).

Return type:

LinOp

adjoint()#

Return the Hermitian-adjoint view of this linear operator.

Return type:

LinOp

adjoint_apply(y)#

Apply the adjoint of this linear operator to y.

Parameters:

y (Any)

Return type:

Any

apply(x)#

Apply the dense operator to x.

Parameters:

x (DenseArray)

Return type:

DenseArray

assert_codomain(y)#

Raise if y is not in the codomain.

Parameters:

y (Any)

Return type:

None

assert_domain(x)#

Raise if x is not in the domain.

Parameters:

x (Any)

Return type:

None

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

property codomain: Codomain#

Codomain space of this linear operator.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property domain: Domain#

Domain space of this linear operator.

property dtype: Any#

Return the default dtype associated with this object’s context.

dual_from_cvxpy(y)#

Reassemble a per-constraint value vector into a cod element.

Inverse of the row layout produced by rhs_to_cvxpy(). The default unflattens y back into cod; structured codomains override. The caller (solver) is responsible for the dual sign.

Parameters:

y (DenseArray)

Return type:

Any

fuse(*, materialize=False)#

Return an equivalent operator with fusible sub-expressions multiplied out.

Tier-2 lazy-algebra simplification ([ADR-021](021_lazy_operator_algebra_and_simplification.md)): collapse each maximal subtree of densely-fusible operators into a single materialized operator — for example, a composition of dense operators becomes one DenseLinOp holding the matrix product \(M_A M_B\) — while leaving matrix-free and other non-materializable leaves intact.

This is an explicit, opt-in materialization. The result is mathematically equal to self but only within floating-point rounding: fusing reassociates the arithmetic (multiplying matrices then applying differs from applying in sequence at the ulp level), so equality holds up to tolerance, not bit-for-bit. The fused operator preserves the domain, codomain, context, and scalar-field/dtype identity. A leaf operator returns itself.

Parameters:

materialize (bool, optional) – With the default False, a matrix-free operand ([ADR-008](008_linop_subclasses.md)) is never densified: it remains a lazy leaf and only breaks a fusible run. With True the caller explicitly accepts giving up the matrix-free contract: a matrix-free operand is densified into a DenseLinOp (via its to_dense basis probe, which may be expensive), allowing the enclosing expression to collapse to a single dense operator.

Returns:

A fused operator with the same action as self (up to rounding).

Return type:

LinOp

is_hermitian()#

Return whether this dense operator is structurally self-adjoint.

Returns:

True or False when the structure can be checked, otherwise None.

Return type:

bool or None

property ops: BackendOps#

Return backend operations associated with this object’s context.

rapply(y)#

Apply the adjoint dense operator to y.

Euclidean spaces use the conjugate transpose of the flattened matrix. Non-Euclidean spaces apply the codomain and domain Riesz maps around that Euclidean adjoint.

Parameters:

y (DenseArray)

Return type:

DenseArray

rhs_to_cvxpy(b)#

Return the real right-hand side matching the rows of to_cvxpy().

The default flattens b into cod coordinates and takes the real part – correct when the codomain is a real coordinate space of m scalar constraints. Operators with a structured codomain (e.g. QOT’s Hermitian marginals reduced to real generators) override this.

Parameters:

b (Any)

Return type:

DenseArray

rvapply(ys)#

Apply the adjoint over a leading batch axis. Input must have shape (N,) + codomain.shape; use moveaxis for other layouts.

Parameters:

ys (DenseArray)

Return type:

DenseArray

to_dense()#

Return the stored dense tensor representation of this operator.

The returned array has shape self.codomain.shape + self.domain.shape.

Return type:

DenseArray

to_matrix()#

Return the flattened dense matrix representation.

The returned array has shape (prod(self.codomain.shape), prod(self.domain.shape)). It is a reshape/view of the stored dense tensor when the backend permits.

Return type:

DenseArray

to_sparse()#
vapply(xs)#

Apply over a leading batch axis. Input must have shape (N,) + domain.shape; use moveaxis for other layouts.

Parameters:

xs (DenseArray)

Return type:

DenseArray

class sdplab.problem.SparseConstraintOp(A, dom, cod, ctx=None)[source]#

Bases: ConstraintOp, SparseLinOp

Constraint operator backed by a sparse coordinate matrix.

A stores the flattened (m, dom.size) matrix whose i-th row, reshaped to dom.shape, is the operator tensor slice T_i. to_cvxpy() returns each A_i = T_i^{T} as a sparse (n, n) matrix (the trace convention). As for the dense case the map is a plain transpose, which for a complex Hermitian slice equals its conjugate \overline{T_i} – the correct Hermitian matrix – and is a no-op only for a real symmetric SDP; it keeps large constraints sparse.

to_cvxpy() uses scipy.sparse row-slice/reshape/transpose semantics – the only sparse backend in use – and feeds the cvxpy backend, which operates on numpy/scipy data.

Parameters:
  • A (SparseArray)

  • dom (CoordinateSpace)

  • cod (CoordinateSpace)

  • ctx (Context | str | None)

to_cvxpy()[source]#

Return the per-constraint matrices [A_0, ..., A_{m-1}].

Each A_i lives in the domain matrix space and is oriented so that the i-th equality reads \(\operatorname{Re}\operatorname{Tr}[A_i X] = b_i\).

Return type:

list[SparseArray]

classmethod from_linop(op)[source]#

Wrap an existing SparseLinOp as a constraint operator.

Parameters:

op (SparseLinOp)

Return type:

SparseConstraintOp

property A: SparseArray#

Stored sparse matrix representation of this operator.

The returned sparse matrix has shape (prod(self.codomain.shape), prod(self.domain.shape)) and is the same object supplied at construction.

property H: LinOp#

Hermitian-adjoint view of this linear operator.

Returns:

Adjoint view satisfying \(\langle A x, y\rangle_Y = \langle x, A^* y\rangle_X\).

Return type:

LinOp

adjoint()#

Return the Hermitian-adjoint view of this linear operator.

Return type:

LinOp

adjoint_apply(y)#

Apply the adjoint of this linear operator to y.

Parameters:

y (Any)

Return type:

Any

apply(x)#

Forward action y = A @ x in Euclidean coordinates.

x must have shape dom.shape (dense).

Parameters:

x (DenseArray)

Return type:

DenseArray

assert_codomain(y)#

Raise if y is not in the codomain.

Parameters:

y (Any)

Return type:

None

assert_domain(x)#

Raise if x is not in the domain.

Parameters:

x (Any)

Return type:

None

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

property codomain: Codomain#

Codomain space of this linear operator.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property domain: Domain#

Domain space of this linear operator.

property dtype: Any#

Return the default dtype associated with this object’s context.

dual_from_cvxpy(y)#

Reassemble a per-constraint value vector into a cod element.

Inverse of the row layout produced by rhs_to_cvxpy(). The default unflattens y back into cod; structured codomains override. The caller (solver) is responsible for the dual sign.

Parameters:

y (DenseArray)

Return type:

Any

fuse(*, materialize=False)#

Return an equivalent operator with fusible sub-expressions multiplied out.

Tier-2 lazy-algebra simplification ([ADR-021](021_lazy_operator_algebra_and_simplification.md)): collapse each maximal subtree of densely-fusible operators into a single materialized operator — for example, a composition of dense operators becomes one DenseLinOp holding the matrix product \(M_A M_B\) — while leaving matrix-free and other non-materializable leaves intact.

This is an explicit, opt-in materialization. The result is mathematically equal to self but only within floating-point rounding: fusing reassociates the arithmetic (multiplying matrices then applying differs from applying in sequence at the ulp level), so equality holds up to tolerance, not bit-for-bit. The fused operator preserves the domain, codomain, context, and scalar-field/dtype identity. A leaf operator returns itself.

Parameters:

materialize (bool, optional) – With the default False, a matrix-free operand ([ADR-008](008_linop_subclasses.md)) is never densified: it remains a lazy leaf and only breaks a fusible run. With True the caller explicitly accepts giving up the matrix-free contract: a matrix-free operand is densified into a DenseLinOp (via its to_dense basis probe, which may be expensive), allowing the enclosing expression to collapse to a single dense operator.

Returns:

A fused operator with the same action as self (up to rounding).

Return type:

LinOp

is_hermitian()#

Return whether this sparse operator is structurally self-adjoint.

Returns:

True or False when the structure can be checked, otherwise None.

Return type:

bool or None

property ops: BackendOps#

Return backend operations associated with this object’s context.

rapply(y)#

Metric-aware adjoint action.

y must have shape cod.shape (dense).

Parameters:

y (DenseArray)

Return type:

DenseArray

rhs_to_cvxpy(b)#

Return the real right-hand side matching the rows of to_cvxpy().

The default flattens b into cod coordinates and takes the real part – correct when the codomain is a real coordinate space of m scalar constraints. Operators with a structured codomain (e.g. QOT’s Hermitian marginals reduced to real generators) override this.

Parameters:

b (Any)

Return type:

DenseArray

rvapply(ys)#

Apply the adjoint over a leading batch axis. Input must have shape (N,) + codomain.shape; use moveaxis for other layouts.

Parameters:

ys (DenseArray)

Return type:

DenseArray

to_dense()#

Materialize the stored sparse matrix as a dense operator tensor.

The returned array has shape self.codomain.shape + self.domain.shape.

Return type:

DenseArray

to_matrix()#

Materialize the stored sparse matrix as a dense 2D coordinate matrix.

Use to_sparse() when sparse storage should be preserved.

Return type:

DenseArray

to_sparse()#

Return the stored sparse matrix representation without copying.

The returned object is exactly the sparse array supplied at construction.

Return type:

SparseArray

vapply(xs)#

Apply over a leading batch axis. Input must have shape (N,) + domain.shape; use moveaxis for other layouts.

Parameters:

xs (DenseArray)

Return type:

DenseArray

class sdplab.problem.Cost(space, ctx=None)[source]#

Bases: ContextBound, ABC, Generic[MSpace]

Abstract cost \(X \mapsto \langle C, X\rangle\) on a Jordan domain.

The two operations every solver needs:

  • inner() evaluates the (real) objective pairing \(\langle C, X\rangle\).

  • element returns the cost as a plain element of space, used to form the dual slack \(\mathcal{A}^\dagger y - C\) and first-order primal updates.

Parameters:
  • space (MSpace)

  • ctx (Context | str | None)

space: MSpace#
abstractmethod inner(X)[source]#

Return the real pairing \(\langle C, X\rangle\) as a backend scalar.

The result is a backend array (not a Python float), so the pairing can be evaluated inside compiled loops and under autodiff.

Parameters:

X (Any)

Return type:

Any

abstract property element: Any#

Return the cost as a plain element of space.

abstractmethod to_cvxpy()[source]#

Return the cost matrix C in cvxpy-ready form.

The objective handed to cvxpy is \(\operatorname{Re}\operatorname{Tr}[C\, X]\), so C is the Hermitian cost matrix (dense or sparse). Only matrix (Hermitian) costs support this; element/tree costs do not.

Return type:

Any

to_dense()[source]#

Return a dense representation; the default is element.

Return type:

Any

to_sparse()[source]#

Return a sparse representation; the default sparsifies to_dense().

Return type:

SparseArray

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property dtype: Any#

Return the default dtype associated with this object’s context.

property ops: BackendOps#

Return backend operations associated with this object’s context.

class sdplab.problem.ElementCost(value, space, ctx=None)[source]#

Bases: Cost[EuclideanJordanAlgebraSpace]

A cost stored directly as a domain element (works on any EJA, incl. trees).

Parameters:
  • value (Any)

  • space (MSpace)

  • ctx (Context | str | None)

property element: Any#

Return the cost as a plain element of space.

to_cvxpy()[source]#

Return the cost matrix C in cvxpy-ready form.

The objective handed to cvxpy is \(\operatorname{Re}\operatorname{Tr}[C\, X]\), so C is the Hermitian cost matrix (dense or sparse). Only matrix (Hermitian) costs support this; element/tree costs do not.

Return type:

Any

inner(X)[source]#

Return the real pairing \(\langle C, X\rangle\) as a backend scalar.

The result is a backend array (not a Python float), so the pairing can be evaluated inside compiled loops and under autodiff.

Parameters:

X (Any)

Return type:

Any

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property dtype: Any#

Return the default dtype associated with this object’s context.

property ops: BackendOps#

Return backend operations associated with this object’s context.

to_dense()#

Return a dense representation; the default is element.

Return type:

Any

to_sparse()#

Return a sparse representation; the default sparsifies to_dense().

Return type:

SparseArray

space: MSpace#
class sdplab.problem.HermitianCost(space, ctx=None)[source]#

Bases: Cost[HermitianSpace]

Self-adjoint SDP cost represented as an operator and matrix-space element.

Beyond the Cost contract, a Hermitian cost acts on vectors of the underlying Hilbert space through operator (matvec), which is what matrix-free spectral algorithms consume.

Parameters:
  • space (MSpace)

  • ctx (Context | str | None)

operator: LinOp#
__init__(space, ctx=None)[source]#

Bind the matrix space, rejecting domains that are not Hermitian.

The Cost base only requires a Euclidean Jordan domain, but a Hermitian cost additionally reads space.n and acts on vectors of the underlying Hilbert space, so the domain must be a HermitianSpace (real symmetric or complex Hermitian).

Parameters:
  • space (HermitianSpace)

  • ctx (Context | str | None)

Return type:

None

property matrix_space: HermitianSpace#

The Hermitian matrix space this cost lives in (alias of space).

property vector_space: InnerProductSpace#

Vector space on which the cost acts.

matvec(x)[source]#

Apply the cost to one vector.

Parameters:

x (Any)

Return type:

Any

matvec_batch(xs)[source]#

Apply the cost independently over leading batch axes.

Parameters:

xs (Any)

Return type:

Any

classmethod from_dense(matrix, matrix_space, ctx=None)[source]#
Parameters:
  • matrix (DenseArray)

  • matrix_space (HermitianSpace)

  • ctx (Context | str | None)

Return type:

DenseHermitianCost

classmethod from_sparse(matrix, matrix_space, ctx=None)[source]#
Parameters:
  • matrix (SparseArray)

  • matrix_space (HermitianSpace)

  • ctx (Context | str | None)

Return type:

SparseHermitianCost

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property dtype: Any#

Return the default dtype associated with this object’s context.

abstract property element: Any#

Return the cost as a plain element of space.

abstractmethod inner(X)#

Return the real pairing \(\langle C, X\rangle\) as a backend scalar.

The result is a backend array (not a Python float), so the pairing can be evaluated inside compiled loops and under autodiff.

Parameters:

X (Any)

Return type:

Any

property ops: BackendOps#

Return backend operations associated with this object’s context.

abstractmethod to_cvxpy()#

Return the cost matrix C in cvxpy-ready form.

The objective handed to cvxpy is \(\operatorname{Re}\operatorname{Tr}[C\, X]\), so C is the Hermitian cost matrix (dense or sparse). Only matrix (Hermitian) costs support this; element/tree costs do not.

Return type:

Any

to_dense()#

Return a dense representation; the default is element.

Return type:

Any

to_sparse()#

Return a sparse representation; the default sparsifies to_dense().

Return type:

SparseArray

space: MSpace#
class sdplab.problem.DenseHermitianCost(matrix, matrix_space, ctx=None)[source]#

Bases: HermitianCost

Hermitian cost backed by a dense matrix.

Parameters:
  • matrix (DenseArray)

  • matrix_space (HermitianSpace)

  • ctx (Context | str | None)

property matrix: DenseArray#

Stored dense matrix.

property element: DenseArray#

Return the cost as a plain element of space.

to_cvxpy()[source]#

Return the dense Hermitian cost matrix for Re Tr[C X].

Return type:

DenseArray

inner(X)[source]#

Return the real pairing \(\langle C, X\rangle\) as a backend scalar.

The result is a backend array (not a Python float), so the pairing can be evaluated inside compiled loops and under autodiff.

Parameters:

X (Any)

Return type:

Any

to_dense()[source]#

Return a dense representation; the default is element.

Return type:

DenseArray

to_sparse()[source]#

Return a sparse representation; the default sparsifies to_dense().

Return type:

SparseArray

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property dtype: Any#

Return the default dtype associated with this object’s context.

classmethod from_dense(matrix, matrix_space, ctx=None)#
Parameters:
  • matrix (DenseArray)

  • matrix_space (HermitianSpace)

  • ctx (Context | str | None)

Return type:

DenseHermitianCost

classmethod from_sparse(matrix, matrix_space, ctx=None)#
Parameters:
  • matrix (SparseArray)

  • matrix_space (HermitianSpace)

  • ctx (Context | str | None)

Return type:

SparseHermitianCost

property matrix_space: HermitianSpace#

The Hermitian matrix space this cost lives in (alias of space).

matvec(x)#

Apply the cost to one vector.

Parameters:

x (Any)

Return type:

Any

matvec_batch(xs)#

Apply the cost independently over leading batch axes.

Parameters:

xs (Any)

Return type:

Any

property ops: BackendOps#

Return backend operations associated with this object’s context.

property vector_space: InnerProductSpace#

Vector space on which the cost acts.

operator: LinOp#
space: MSpace#
class sdplab.problem.SparseHermitianCost(matrix, matrix_space, ctx=None)[source]#

Bases: HermitianCost

Hermitian cost backed by a sparse matrix.

element densifies the stored matrix; algorithms that only need the pairing (inner()) or the vector action (matvec()) never pay that cost.

Parameters:
  • matrix (SparseArray)

  • matrix_space (HermitianSpace)

  • ctx (Context | str | None)

property matrix: SparseArray#

Stored sparse matrix.

property element: DenseArray#

Return the cost as a plain element of space.

to_cvxpy()[source]#

Return the sparse Hermitian cost matrix for Re Tr[C X].

Return type:

SparseArray

inner(X)[source]#

Return the real pairing \(\langle C, X\rangle\) as a backend scalar.

The result is a backend array (not a Python float), so the pairing can be evaluated inside compiled loops and under autodiff.

Parameters:

X (Any)

Return type:

Any

to_dense()[source]#

Return a dense representation; the default is element.

Return type:

DenseArray

to_sparse()[source]#

Return a sparse representation; the default sparsifies to_dense().

Return type:

SparseArray

property check_level: Literal['none', 'cheap', 'standard', 'strict']#

Return this object’s runtime validation level.

convert(new_ctx=None)#

Return this object represented in new_ctx.

Parameters:

new_ctx (Context | BackendFamily | str | None)

Return type:

Self

property ctx: Context#

Return the execution context bound to this object.

property dtype: Any#

Return the default dtype associated with this object’s context.

classmethod from_dense(matrix, matrix_space, ctx=None)#
Parameters:
  • matrix (DenseArray)

  • matrix_space (HermitianSpace)

  • ctx (Context | str | None)

Return type:

DenseHermitianCost

classmethod from_sparse(matrix, matrix_space, ctx=None)#
Parameters:
  • matrix (SparseArray)

  • matrix_space (HermitianSpace)

  • ctx (Context | str | None)

Return type:

SparseHermitianCost

property matrix_space: HermitianSpace#

The Hermitian matrix space this cost lives in (alias of space).

matvec(x)#

Apply the cost to one vector.

Parameters:

x (Any)

Return type:

Any

matvec_batch(xs)#

Apply the cost independently over leading batch axes.

Parameters:

xs (Any)

Return type:

Any

property ops: BackendOps#

Return backend operations associated with this object’s context.

property vector_space: InnerProductSpace#

Vector space on which the cost acts.

operator: LinOp#
space: MSpace#