Skip to content

Solving layer

solving

The solve for the knob settings: parameter realisation as a constrained solve.

The parameter relation of a pipeline is inverted in a single constrained solve for the knob settings of the hardware model. qsimod.solving.problem collects the equations, admissible sets and validity conditions of a relation into a constraint system; qsimod.solving.backends contains the only modules that import an optimiser; qsimod.solving.dispatch selects a backend after an attempt to prove infeasibility with qsimod.solving.reachability; and qsimod.solving.feasible evaluates the same declarations point-wise and region-wise. qsimod.solving.stepcount poses the step count of a product formula as an integer solve against the a-priori error bound.

problem

The declarative statement of a parameter-realisation problem.

RealisationProblem collects the equations, admissible sets and validity conditions of a parameter relation into a ConstraintSystem; the backend that solves it is selected in qsimod.solving.dispatch, and this module imports no solver. Unknown records whether an unknown ranges over the reals or the integers, and a backend declares the problem shapes it handles through supports.

UnknownKind

Bases: Enum

The domain of an unknown: the reals or the integers.

Unknown dataclass

One quantity determined by a solve.

Attributes:

Name Type Description
name str

the parameter name.

kind UnknownKind

whether the unknown is continuous or integral.

bound Bound | None

the box bound, if the admissible set declares one.

initial float | None

a deterministic starting value, if one is declared.

interval property
interval: Interval

The box of the unknown; unbounded if the admissible set declares none.

ConstraintSystem dataclass

The constraint system: equations, inequalities and boxes over named unknowns.

Attributes:

Name Type Description
unknowns tuple[Unknown, ...]

the quantities the solve determines.

equalities tuple[Equation, ...]

the equations whose residual must vanish.

inequalities tuple[InequalityConstraint, ...]

the expressions that must be non-negative.

fixed Mapping[str, float]

the parameters fixed to values: the requested targets and any prescribed knobs.

relation ParameterRelation | None

the parameter relation the equalities are taken from, if any.

classification RelationClassification | None

the static classification of the relation in the requested direction.

unknown_names property
unknown_names: tuple[str, ...]

The names of the unknowns, in declaration order.

has_integral_unknown property
has_integral_unknown: bool

Whether any unknown ranges over the integers.

unknown
unknown(name: str) -> Unknown

The unknown with the given name.

Raises:

Type Description
KeyError

if no unknown has that name.

bounds
bounds() -> tuple[Bound, ...]

The declared box bounds of the unknowns, in declaration order.

admissible_set
admissible_set(description: str = '') -> AdmissibleSet

The boxes of the unknowns and the coupled constraints, as an admissible set.

interval_environment
interval_environment() -> dict[str, Interval]

The boxes of the unknowns and the fixed values, as an interval environment.

residuals
residuals(
    assignment: Mapping[str, float],
) -> dict[str, float]

The residual of every equality whose parameters are all assigned.

definitions
definitions() -> tuple[Definition, ...]

The solved forms of the relation; empty if the system has no relation.

RealisationProblem dataclass

A complete parameter-realisation request, independent of the backend that solves it.

Attributes:

Name Type Description
name str

the name of the request, used in reports.

system ConstraintSystem

the constraint system.

objective Objective

the objective of the solve; defaults to feasibility only.

validity Conjunction

the validity conditions; the domain conditions are always hard constraints.

enforce_regime_conditions bool

whether the regime conditions are imposed as constraints in addition to being reported (default True).

tolerance float

the largest residual that counts as an exact solution.

max_iterations int

the iteration budget of the backend; an exhausted budget yields UNSOLVED, never INFEASIBLE.

hard_constraints
hard_constraints() -> tuple[tuple[str, Scalar], ...]

Every named expression that a solution must keep non-negative.

The tuple comprises the coupled admissibility constraints, the margin surrogates of the domain conditions, the margin surrogates of the regime conditions when these are enforced, and the epigraph constraints of the objective.

enforced_conditions
enforced_conditions() -> tuple[ValidityCondition, ...]

The validity conditions a solution must satisfy.

The domain conditions are always included; the regime conditions are included when enforce_regime_conditions is set.

rejections
rejections(point: Mapping[str, float]) -> tuple[str, ...]

The reasons for which point is not an acceptable solution.

The tuple lists every admissibility constraint the point violates and every enforced validity condition that fails at the point by more than MARGIN_SLACK_DECADES, the rounding permitted to a constraint active at its boundary. It is empty exactly when the point may be reported as a solution.

parameters
parameters() -> frozenset[str]

Every parameter the problem refers to.

build_system

build_system(
    relation: ParameterRelation,
    targets: Mapping[str, float],
    unknowns: Sequence[str],
    admissible_set: AdmissibleSet,
    *,
    initial: Mapping[str, float] | None = None,
    integral: Sequence[str] = (),
) -> ConstraintSystem

Assemble a constraint system from a relation, a target assignment and an admissible set.

Parameters:

Name Type Description Default
relation ParameterRelation

the parameter relation, possibly the composite relation of a pipeline.

required
targets Mapping[str, float]

the parameters fixed by the request.

required
unknowns Sequence[str]

the parameters to solve for. Every parameter the relation refers to that is neither fixed nor listed becomes an unknown as well.

required
admissible_set AdmissibleSet

the declared knob limits of the hardware model.

required
initial Mapping[str, float] | None

deterministic starting values, per unknown.

None
integral Sequence[str]

the unknowns that range over the integers.

()

Returns:

Type Description
ConstraintSystem

The constraint system, with its static classification.

objectives

Objectives for an under-determined solve.

An under-determined system of parameter relations admits multiple knob settings that realise the same request; an objective selects among them. The default, MaximiseWeakestMargin, places the operating point as deep inside the declared regime as the constraints allow. It is posed by epigraph transformation: an auxiliary variable s (SLACK_VARIABLE) is introduced, the margin of every regime condition is constrained to be at least s, and s is maximised; Objective.slack_variable declares the auxiliary variable to a backend. MinimiseExpression and MaximiseExpression accept any scalar expression.

Objective

Bases: ABC

The quantity a solve minimises, with the auxiliary variables and constraints it requires.

A backend minimises cost_expression over the unknowns and any slack_variable; a report states value, the quantity the objective is named after, at the solved point.

slack_variable property
slack_variable: str | None

The auxiliary variable the objective requires, if any.

cost_expression abstractmethod
cost_expression() -> Scalar | None

The expression a backend minimises, over the unknowns and the slack variable.

None declares no preference: any feasible point is accepted.

value abstractmethod
value(assignment: Mapping[str, float]) -> float

The quantity the objective is named after, at a point.

For a maximisation this is the maximised quantity itself, which is the quantity a report states; cost is the quantity the backend minimised.

cost
cost(assignment: Mapping[str, float]) -> float

The quantity the backend minimises, at a point; a lower value is preferred.

parameters abstractmethod
parameters() -> frozenset[str]

The names of the parameters the objective depends on.

epigraph_constraints
epigraph_constraints() -> tuple[tuple[str, Scalar], ...]

The named expressions that must be >= 0 for the epigraph form to be valid.

FeasibilityOnly dataclass

Bases: Objective

No preference: any admissible point that satisfies the relation is accepted.

cost_expression
cost_expression() -> None

None, declaring no preference.

value
value(assignment: Mapping[str, float]) -> float

The constant zero.

parameters
parameters() -> frozenset[str]

The empty set.

MinimiseExpression dataclass

Bases: Objective

Minimise a scalar expression over the parameters of the pipeline.

cost_expression
cost_expression() -> Scalar

The expression itself.

value
value(assignment: Mapping[str, float]) -> float

The expression's value.

parameters
parameters() -> frozenset[str]

The expression's symbols.

MaximiseExpression dataclass

Bases: Objective

Maximise a scalar expression over the parameters of the pipeline.

cost_expression
cost_expression() -> Scalar

The negated expression.

value
value(assignment: Mapping[str, float]) -> float

The expression's value.

cost
cost(assignment: Mapping[str, float]) -> float

The negated value of the expression.

parameters
parameters() -> frozenset[str]

The expression's symbols.

MaximiseWeakestMargin dataclass

Bases: Objective

Maximise the margin of the weakest regime condition.

Attributes:

Name Type Description
conditions tuple[ValidityCondition, ...]

the validity conditions whose margins are traded off; only the regime conditions enter, as the domain conditions are hard constraints.

normalise bool

whether each margin surrogate is divided by the scale of its own requirement, which makes conditions in different units comparable (default True).

regime_conditions property
regime_conditions: tuple[ValidityCondition, ...]

The regime conditions among conditions, which carry the maximised margins.

slack_variable property
slack_variable: str

The name of the epigraph variable.

cost_expression
cost_expression() -> Scalar

The negated epigraph variable, which the constraints hold below every margin.

value
value(assignment: Mapping[str, float]) -> float

The least normalised surrogate margin at a point: the quantity that is maximised.

Each regime condition contributes margin_expression / scale, the expression the epigraph constraints bound below by the slack variable; the value is therefore the optimum in the units of the backend: dimensionless, linear, and positive inside the declared regime. The logarithmic margins in decades are stated in the validity report.

cost
cost(assignment: Mapping[str, float]) -> float

The negated least normalised surrogate margin.

parameters
parameters() -> frozenset[str]

Every parameter that any regime condition depends on.

epigraph_constraints
epigraph_constraints() -> tuple[tuple[str, Scalar], ...]

The constraints surrogate_margin(theta) - s >= 0, one per regime condition.

default_objective

default_objective(
    conditions: Sequence[ValidityCondition],
) -> Objective

The default objective for an under-determined solve.

MaximiseWeakestMargin when regime conditions are declared, FeasibilityOnly otherwise.

minimise

minimise(
    expression: ScalarLike,
    name: str = "",
    description: str = "",
) -> MinimiseExpression

Construct a minimisation objective.

Returns a MinimiseExpression over expression.

maximise

maximise(
    expression: ScalarLike,
    name: str = "",
    description: str = "",
) -> MaximiseExpression

Construct a maximisation objective.

Returns a MaximiseExpression over expression.

result

The result of a parameter-realisation solve: a status and a point.

SolveStatus distinguishes an exact solution, a best fit with a residual per equation, a proved infeasibility that names the binding constraint, and an unconverged search (UNSOLVED), which is not an infeasibility. The backend, its termination message, its iteration count and any random seed are recorded; every backend of the package is deterministic, and the seed is therefore None.

SolveStatus

Bases: Enum

The outcome of a solve.

EXACT_SOLUTION class-attribute instance-attribute
EXACT_SOLUTION = 'EXACT_SOLUTION'

A point that satisfies every equation to the requested tolerance.

APPROXIMATE_SOLUTION class-attribute instance-attribute
APPROXIMATE_SOLUTION = 'APPROXIMATE_SOLUTION'

A best fit; the residual of each equation is reported and does not vanish.

INFEASIBLE class-attribute instance-attribute
INFEASIBLE = 'INFEASIBLE'

A proof that no admissible point realises the request, naming the binding constraint.

UNSOLVED class-attribute instance-attribute
UNSOLVED = 'UNSOLVED'

The solver did not converge; nothing was proved, in contrast to INFEASIBLE.

is_success property
is_success: bool

Whether the solve produced a point that may be reported as a solution.

SolveResult dataclass

The result of a solve.

Attributes:

Name Type Description
status SolveStatus

the outcome of the solve.

point Mapping[str, float]

the full parameter assignment, including the intermediate parameters; empty for a request proved infeasible.

residuals Mapping[str, float]

the residual of each equation at point.

relation_kind RelationKind

the static classification of the relation in the requested direction, determined before the solve.

validity ValidityReport

the validity report at point.

binding_constraints tuple[str, ...]

for an infeasible request, the constraints that make it infeasible; for an approximate solution, the constraints that are active.

violations tuple[ConstraintViolation, ...]

the admissibility constraints point violates; empty for a successful status.

objective_name str

the name of the objective that was optimised.

objective_value float

the natural value of the objective at point: the expression for a minimisation or maximisation, the least normalised margin for the default max-min objective.

backend str

the name of the backend used.

iterations int

the number of iterations the backend took.

termination str

the termination message of the backend.

seed int | None

the random seed used; None for every backend of the package.

notes Mapping[str, str]

further diagnostics for a report.

max_residual property
max_residual: float

The largest absolute residual, or 0.0 if there are no residuals.

value
value(parameter: str) -> float

The solved value of one parameter.

Raises:

Type Description
KeyError

if the parameter is not part of the solution.

subset
subset(parameters: Sequence[str]) -> dict[str, float]

The solved values of a named subset of parameters, omitting any that are absent.

reachability

Proving a request unreachable for a hardware model by interval arithmetic.

The proof involves no numerical solve. The procedure is as follows: (1) every solved form whose inputs are fixed values or bounded knobs is substituted, repeatedly, until no further parameter can be eliminated; (2) for each remaining equation with one side thereby constant, the other side is bounded over the admissible box by the intersection of the interval-arithmetic bound and the qsimod.affine bound; (3) if the constant lies outside the bound, the request is proved unreachable; (4) the binding constraints are named by relaxing the knob boxes one at a time. A bound that contains the request establishes only that infeasibility is not proved, not that the request is reachable: check_reachability then returns None and the caller proceeds to a backend.

ReachabilityVerdict dataclass

A proof that a request cannot be met, naming the constraint responsible.

Attributes:

Name Type Description
equation str

the equation that cannot be satisfied.

requested float

the value the request imposes on one side of the equation.

achievable Interval

the interval that side attains over the admissible box.

binding tuple[str, ...]

the constraints whose relaxation would make the request reachable.

note str

an explanation in prose.

eliminated tuple[str, ...]

the solved forms substituted in the course of the proof, for the report.

ReachabilityReport dataclass

The achievable range of every equation, for a report or a feasibility study.

Attributes:

Name Type Description
ranges Mapping[str, Interval]

the interval the unknown side of each equation attains over the admissible box.

eliminated tuple[str, ...]

the solved forms that were substituted.

achievable_range

achievable_range(
    expression: Scalar,
    system: ConstraintSystem,
    substitutions: Mapping[str, Scalar] | None = None,
) -> Interval

Bound expression over the admissible box, after optional substitutions.

The result is the intersection of the interval-arithmetic bound and the affine bound; it is sound and never wider than either.

bound_over

bound_over(
    expression: Scalar, env: Mapping[str, Interval]
) -> Interval

The tightest sound bound the package forms for expression.

Parameters:

Name Type Description Default
expression Scalar

the expression to bound.

required
env Mapping[str, Interval]

an interval per symbol.

required

Returns:

Type Description
Interval

The interval-arithmetic bound, narrowed by the affine bound where the latter exists

Interval

and is narrower.

check_reachability

check_reachability(
    system: ConstraintSystem, admissible_set: AdmissibleSet
) -> ReachabilityVerdict | None

Attempt to prove the request unreachable over the declared admissible set.

Parameters:

Name Type Description Default
system ConstraintSystem

the constraint system.

required
admissible_set AdmissibleSet

the knob limits of the hardware model, used to name the binding constraint.

required

Returns:

Type Description
ReachabilityVerdict | None

A verdict if unreachability is proved, otherwise None. A None result

ReachabilityVerdict | None

establishes only that infeasibility is not proved, not that the request is reachable.

reachability_report

reachability_report(
    system: ConstraintSystem,
) -> ReachabilityReport

Bound the unknown side of every equation over the admissible box.

feasible

The feasible set of a realisation problem, evaluated point-wise or region-wise.

FeasibleSet.check decides one assignment against the admissible box, the coupled constraints, the equation residuals and the validity conditions. FeasibleSet.grid evaluates the feasible set on a Cartesian grid, FeasibleSet.sample on a seeded uniform sample of the box (default seed DEFAULT_SAMPLE_SEED), and FeasibleSet.constraint_system returns the constraint system unevaluated.

FeasiblePoint dataclass

The verdict on one point, with every reason for its failure.

Attributes:

Name Type Description
values Mapping[str, float]

the full parameter assignment tested.

feasible bool

whether the point satisfies every constraint.

violations tuple[ConstraintViolation, ...]

the admissibility constraints the point violates.

validity ValidityReport

the validity report at the point.

residuals Mapping[str, float]

the equation residuals at the point.

reasons tuple[str, ...]

the failures in prose; empty when the point is feasible.

max_residual property
max_residual: float

The largest absolute residual, or zero if there are no residuals.

weakest_margin property
weakest_margin: float

The least regime margin in decades, or nan if there is none.

as_row
as_row() -> dict[str, float | bool | str]

The point as a flat mapping, suitable as a row of a pandas frame.

FeasibleSet

The feasible set of a realisation problem, evaluated point-wise or region-wise.

check
check(assignment: Mapping[str, float]) -> FeasiblePoint

Decide one assignment against every constraint of the problem.

grid
grid(
    axes: Mapping[str, Sequence[float]],
    complete: Callable[
        [Mapping[str, float]], Mapping[str, float]
    ]
    | None = None,
) -> tuple[FeasiblePoint, ...]

Evaluate the feasible set on a Cartesian grid over axes.

Parameters:

Name Type Description Default
axes Mapping[str, Sequence[float]]

the gridded quantities and their values; they need not be unknowns of the problem.

required
complete Callable[[Mapping[str, float]], Mapping[str, float]] | None

a map from a grid node, with the fixed values of the problem merged in, to the full assignment. Defaults to the identity.

None

Returns:

Type Description
tuple[FeasiblePoint, ...]

One verdict per node, in row-major order over the insertion order of axes.

sample
sample(
    count: int,
    complete: Callable[
        [Mapping[str, float]], Mapping[str, float]
    ]
    | None = None,
    seed: int = DEFAULT_SAMPLE_SEED,
) -> tuple[FeasiblePoint, ...]

Sample the admissible box uniformly and report the verdict on each point.

Parameters:

Name Type Description Default
count int

the number of points to draw.

required
complete Callable[[Mapping[str, float]], Mapping[str, float]] | None

as for grid.

None
seed int

the random seed.

DEFAULT_SAMPLE_SEED

Returns:

Type Description
tuple[FeasiblePoint, ...]

One verdict per sample.

Raises:

Type Description
ValueError

if no unknown has a finite box, or if some unknown has no finite box and no complete is supplied to derive it.

constraint_system
constraint_system() -> ConstraintSystem

The constraint system, unevaluated, for an external analysis.

feasible_fraction staticmethod
feasible_fraction(points: Sequence[FeasiblePoint]) -> float

The fraction of points that are feasible; nan for an empty sequence.

best staticmethod
best(
    points: Sequence[FeasiblePoint],
) -> FeasiblePoint | None

The feasible point with the largest weakest margin, or None if there is none.

dispatch

Selection and execution of a solver backend.

solve first attempts to prove infeasibility by interval arithmetic (qsimod.solving.reachability); if nothing is proved, it runs the first backend whose supports accepts the problem. The default order is ClosedFormBackend, MonotoneBisectionBackend, ScipyNlpBackend.

default_backends

default_backends() -> tuple[SolverBackend, ...]

The backends tried, in order, when none are supplied.

solve

solve(
    problem: RealisationProblem,
    backends: Sequence[SolverBackend] | None = None,
) -> SolveResult

Solve a realisation problem, after an attempt to prove it infeasible.

Parameters:

Name Type Description Default
problem RealisationProblem

the declarative problem.

required
backends Sequence[SolverBackend] | None

the backends to try, in order. Defaults to default_backends.

None

Returns:

Type Description
SolveResult

The solve result.

Raises:

Type Description
ValueError

if no supplied backend supports the shape of the problem.

classify_request

classify_request(
    relation: ParameterRelation,
    targets: Mapping[str, float],
) -> RelationClassification

Classify a relation in the inverse direction.

Every parameter that targets does not fix is treated as an unknown.

realise_parameters

realise_parameters(
    pipeline: Pipeline,
    targets: Mapping[str, float],
    unknowns: Sequence[str],
    admissible_set: AdmissibleSet,
    *,
    objective: Objective | None = None,
    validity: Conjunction | None = None,
    initial: Mapping[str, float] | None = None,
    integral: Sequence[str] = (),
    enforce_regime_conditions: bool = True,
    tolerance: float = 1e-10,
    max_iterations: int = 400,
    backends: Sequence[SolverBackend] | None = None,
    name: str = "",
) -> SolveResult

Solve a pipeline for the knob settings of the hardware model that realise a target.

The composite parameter relation is solved once, over the full pipeline: the intermediate parameters are unknowns alongside the hardware knobs.

Parameters:

Name Type Description Default
pipeline Pipeline

the pipeline whose composite relation is inverted.

required
targets Mapping[str, float]

the parameters the request fixes.

required
unknowns Sequence[str]

the hardware knobs to solve for. The intermediate parameters are added as unknowns automatically.

required
admissible_set AdmissibleSet

the declared knob limits of the hardware model.

required
objective Objective | None

the objective that resolves under-determination; defaults to MaximiseWeakestMargin over the validity conditions of the pipeline.

None
validity Conjunction | None

the conditions to enforce and report; defaults to those of the pipeline.

None
initial Mapping[str, float] | None

deterministic starting values.

None
integral Sequence[str]

the unknowns that range over the integers.

()
enforce_regime_conditions bool

whether the regime conditions are imposed as constraints in addition to being reported.

True
tolerance float

the largest residual that counts as an exact solution.

1e-10
max_iterations int

the iteration budget of the backend.

400
backends Sequence[SolverBackend] | None

the backends to try; defaults to default_backends.

None
name str

the name of the request.

''

Returns:

Type Description
SolveResult

The solve result.

problem_for

problem_for(
    relation: ParameterRelation,
    targets: Mapping[str, float],
    unknowns: Sequence[str],
    admissible_set: AdmissibleSet,
    *,
    objective: Objective | None = None,
    validity: Conjunction | None = None,
    initial: Mapping[str, float] | None = None,
    integral: Sequence[str] = (),
    tolerance: float = 1e-10,
    max_iterations: int = 400,
    name: str = "realisation",
) -> RealisationProblem

Assemble a realisation problem without solving it.

Returns a RealisationProblem, for instance for the inspection of the static classification or the construction of a FeasibleSet.

stepcount

The Trotter step count as an instance of the general solve.

The Trotterisation is an approximate transformation whose error is controlled by a resource parameter, the step count n of the product formula. Given a simulated time t, an order p and a target error epsilon, the solve determines the smallest integer n with error_bound(t, n, p) <= epsilon. Every bound in qsimod.trotter.bounds has the form A / n**p at fixed t and order; A is recovered by evaluating the bound at n = 1, and the constraint becomes the scalar expression epsilon - A * n**(-p) >= 0 over the integral unknown STEPS_SYMBOL. The joint choice of (n, order) is one bisection per order, ranked by the total count of k-local unitaries.

bound_coefficient

bound_coefficient(
    decomposition: LayerDecomposition,
    order: int,
    time: float,
    estimator: NormEstimator | None = None,
) -> float

The coefficient A in error_bound(t, n, order) == A / n**order.

It is obtained by evaluating the bound at n = 1.

step_count_problem

step_count_problem(
    decomposition: LayerDecomposition,
    order: int,
    time: float,
    target_error: float,
    *,
    estimator: NormEstimator | None = None,
    max_steps: int = DEFAULT_MAX_STEPS,
    name: str = "",
) -> RealisationProblem

Pose the solve for the smallest n whose error bound is at most target_error.

Parameters:

Name Type Description Default
decomposition LayerDecomposition

the layer partition to which the product formula is applied.

required
order int

the order of the product formula.

required
time float

the simulated time.

required
target_error float

the accuracy target.

required
estimator NormEstimator | None

the estimator that bounds the layer and commutator norms.

None
max_steps int

the largest step count considered.

DEFAULT_MAX_STEPS
name str

the name of the problem.

''

Returns:

Type Description
RealisationProblem

The declarative problem, in the shape accepted by

RealisationProblem

Raises:

Type Description
ValueError

if target_error is not positive.

minimal_steps

minimal_steps(
    decomposition: LayerDecomposition,
    order: int,
    time: float,
    target_error: float,
    *,
    estimator: NormEstimator | None = None,
    max_steps: int = DEFAULT_MAX_STEPS,
    backends: Sequence[SolverBackend] | None = None,
) -> SolveResult

Solve for the least step count that meets an accuracy target.

Returns:

Type Description
SolveResult

The solve result; point[STEPS_SYMBOL] is the step count, and

SolveResult

notes["minimality"] records that n - 1 violates the bound while n satisfies

SolveResult

it.

resource_candidates

resource_candidates(
    decomposition: LayerDecomposition,
    time: float,
    target_error: float,
    orders: Sequence[int] = (1, 2, 4),
    *,
    estimator: NormEstimator | None = None,
    max_steps: int = DEFAULT_MAX_STEPS,
) -> tuple[ResourceChoice, ...]

One ResourceChoice per feasible order.

The candidates are ranked by the total count of k-local unitary factors; an order that cannot meet the target within max_steps is absent.

backends

Solver backends: the only modules of the package that import an optimiser.

The physics modules declare equations, admissible sets and validity conditions in qsimod.relations, qsimod.parameters and qsimod.validity; none of these imports a backend.

base

The interface of a solver backend.

A backend accepts a RealisationProblem and returns a SolveResult; SolverBackend.supports declares the problem shapes a backend handles, by which the dispatcher selects one.

BackendCapability

The flags that describe the problem features a backend handles.

Attributes:

Name Type Description
continuous

whether the backend handles continuous unknowns.

integral

whether the backend handles integral unknowns.

equalities

whether the backend handles equality constraints.

inequalities

whether the backend handles inequality constraints.

proves_infeasibility

whether a negative answer of the backend constitutes a proof. False for every numerical backend of the package, whose failures are reported as UNSOLVED.

SolverBackend

Bases: ABC

A solver that accepts a declarative problem and returns a status and a point.

solve abstractmethod
solve(problem: RealisationProblem) -> SolveResult

Solve the problem.

A backend returns INFEASIBLE only if its BackendCapability.proves_infeasibility is set; a failed search is reported as UNSOLVED.

supports
supports(problem: RealisationProblem) -> bool

Whether the backend handles the shape of the problem.

closed_form

A backend that evaluates a closed-form elimination chain.

When the static classification identifies the relation as a closed form in the requested direction, the elimination chain is evaluated in order, without iteration, tolerance, starting point or optimiser. The backend imports nothing beyond the expression evaluator of the package.

ClosedFormBackend

Bases: SolverBackend

The backend that evaluates a closed-form elimination chain directly.

supports
supports(problem: RealisationProblem) -> bool

Whether the classification of the problem provides an elimination chain.

solve
solve(problem: RealisationProblem) -> SolveResult

Evaluate the chain and report the resulting point.

Raises:

Type Description
ValueError

if the problem has no elimination chain.

scipy_nlp

The constrained non-linear-programming backend on scipy.optimize.

The default method is SLSQP (sequential least-squares programming) from SciPy. The box bounds are passed to the solver directly. The parameter relations enter as equality constraints; the hard inequalities, that is the coupled admissibility constraints and the domain and regime conditions, and the epigraph objective enter as inequality constraints. Each constraint is a NonlinearConstraint object with an analytic Jacobian obtained by JAX automatic differentiation of the expression layer. An OVER_DETERMINED problem is solved as a least-squares fit of the residuals. The method is local and never returns INFEASIBLE: a failed search is reported as UNSOLVED, and infeasibility is proved separately by qsimod.solving.reachability.

ScipyNlpBackend

Bases: SolverBackend

SLSQP over the admissible box, with equalities, inequalities and an epigraph objective.

Attributes:

Name Type Description
method

the scipy.optimize.minimize method used.

solve
solve(problem: RealisationProblem) -> SolveResult

Run the constrained optimisation and classify the outcome.

integer

A discrete backend: monotone bisection over one integral unknown.

The hard constraints are assumed monotone in the unknown; the a-priori error bounds of the product formulas are proportional to n**-p. The feasible set is therefore an up-set, and the least feasible integer is found by doubling to bracket it and bisecting within the bracket, in O(log n) evaluations. Minimality is witnessed by the bisection: the result records that n - 1 violates the constraints and n satisfies them. minimal_resource_setting selects the (order, steps) candidate of least cost.

DiscreteSolution dataclass

The least integer that satisfies a monotone constraint.

Attributes:

Name Type Description
value int

the least feasible integer.

evaluations int

the number of predicate evaluations the search took.

upper_bracket int

the bracket found by doubling, from which the bisection started.

predecessor_fails bool

whether value is known to be minimal: either value - 1 failed during the bisection, or value is the lower bound of the search.

MonotoneBisectionBackend

Bases: SolverBackend

The backend that solves for the least admissible value of a single integral unknown.

The problem must have exactly one integral unknown and no equalities; the hard constraints are evaluated with that unknown set to each candidate.

supports
supports(problem: RealisationProblem) -> bool

Whether the problem has exactly one unknown, which is integral, and no equalities.

solve
solve(problem: RealisationProblem) -> SolveResult

Bisect for the least feasible value of the integral unknown.

Raises:

Type Description
ValueError

if the problem does not have exactly one integral unknown.

ResourceChoice dataclass

One candidate (order, steps) pair and its cost.

Attributes:

Name Type Description
order int

the order of the product formula.

steps int

the least step count that meets the accuracy target at that order.

cost int

the total count of k-local unitaries, by which candidates are ranked.

error_bound float

the a-priori error bound at that setting.

depth_in_layers int

the depth in layers of disjoint support.

minimal_feasible_integer

minimal_feasible_integer(
    predicate: Callable[[int], bool],
    *,
    lower: int = 1,
    maximum: int = 1 << 30,
) -> DiscreteSolution | None

The least integer at or above lower for which predicate holds.

The predicate is assumed monotone: once it holds, it holds for every larger integer. The search brackets the solution by doubling and then bisects.

Parameters:

Name Type Description Default
predicate Callable[[int], bool]

the feasibility test.

required
lower int

the smallest integer considered.

1
maximum int

the largest integer considered; beyond it the search returns None.

1 << 30

Returns:

Type Description
DiscreteSolution | None

The solution, or None if no integer at or below maximum is feasible.

minimal_resource_setting

minimal_resource_setting(
    candidates: Sequence[ResourceChoice],
) -> ResourceChoice

The candidate of least cost; ties are broken in favour of the lower order.

Raises:

Type Description
ValueError

if there are no candidates.