Composing linear ODE inputs and solve instances¶
English · 简体中文
ODEProblem describes the autonomous homogeneous system u' = G u, physical
initial values, and a final time. qode_solve selects a method and returns an
unprepared instance. As with PDE instances, prepare()
checks input contracts and generates one cached modular RIR circuit for
export, resource estimates, and native execution.
Prepare, inspect, and execute¶
The matrix is the generator G, including its sign. For example, diagonal
entries -1 and -2 represent decay, following u' = G u:
import oracq
problem = oracq.ODEProblem(
generator=oracq.MatrixInput([[-1, 2], [0, -2]]),
initial=oracq.ArrayInput([2, -1]),
components=("u", "v"),
final_time=0.1,
)
instance = oracq.qode_solve(
problem, method="taylor", config=oracq.ODEConfig(taylor_degree=2),
).prepare()
circuit = instance.circuit()
assert instance.prepared.report.ok
assert oracq.loads(circuit.dumps()) == circuit.program
assert circuit.resource_estimate().complete
assert "DEF " in circuit.originir_ext().text
assert instance.prepare().circuit() is circuit
Preparation and export import neither PySparQ nor UnifiedQuantum. With those optional backends installed, execute the same prepared circuit:
result = instance.run_pysparq()
result = instance.run_originir_ext()
print(result.success_probability)
print(result.physical_amplitudes())
print(result.physical_values()) # approximately (1.64, -0.82) for this polynomial
physical_amplitudes() normalizes the physical success channel by default;
normalize=False returns the raw success amplitudes. physical_values() uses
the known LCU normalization and initial norm to recover the finite Taylor
polynomial’s values. This is a quantum circuit evaluation of
(I + t G + t² G² / 2) u(0), with independent Taylor truncation error; it does
not substitute a classical solver’s output for backend amplitudes.
The vector contains physical components in the declared order. A scalar uses
one target bit; other dimensions use ceil(log2(size)) bits, with a minimum of
one. Unused addresses are excluded from readout, and the linear method masks
both sides of G to prevent paths through padded components. A zero initial
vector produces zero physical values and zero success probability, with no
normalized conditional state.
Replace the generator and initial inputs¶
Generator input |
Access model |
Scope |
|---|---|---|
|
Pauli gate expansion |
Finite real or complex matrices, at most five target bits |
|
Dense real matrix angle bank |
Finite real entries bounded by |
|
Existing dense matrix angle bank |
Explicit dimension, offset, scale, and immutable memory |
|
Existing block-encoding provider |
Caller supplies operator semantics and any leaf memory banks |
These choices are independent of UniformInput, ArrayInput, QRAMInput,
and OracleInput, the shared initial inputs
also used by PDEs. ArrayInput takes one flat vector of physical components;
named mappings take a single sample per component, such as
{"u": [2], "v": [-1]}. Signed and complex gate initial states are supported;
the built-in QRAM rotation tree accepts nonnegative real amplitudes.
OracleInput wraps application-specific state preparation with its physical
norm and memory banks.
An existing block encoding can replace the explicit matrix:
from dataclasses import replace
encoded = problem.generator.prepare_generator(oracq.ODELayout(2))
oracle_problem = replace(
problem,
generator=oracq.OracleGeneratorInput(encoded.encoding, size=2),
)
assert oracq.qode_solve(oracle_problem).prepare().prepared.report.ok
A registered QRAM bank supplies raw word access, not amplitude preparation or a block encoding by itself. The following adapter explicitly implements G:
matrix_config = oracq.QRAMMatrixConfig(size=2, value_scale=2, offset=4)
words = matrix_config.encode_matrix([[-1, 1], [0.5, -2]], word_length=4)
bank = oracq.register_qram(words, address_length=4, word_length=4,
name="ode_generator")
qram_problem = replace(
problem,
generator=oracq.QRAMMatrixInput(bank, matrix_config),
initial=oracq.ArrayInput([1, 1], encoding="qram", angle_width=3),
)
qram_instance = oracq.qode_solve(
qram_problem, config=oracq.ODEConfig(taylor_degree=1),
).prepare()
assert qram_instance.circuit().memory
assert qram_instance.circuit().resource_estimate().qram_total > 0
Matrix words use offset + row*2**width + column and represent
value_scale*cos(pi*word/2**word_length). A zero word represents positive
value_scale; zero matrix entries require half-turn words. encode_matrix
fills padded rows and columns and saturates the negative endpoint. The
per-entry quantization error is bounded by value_scale*pi/2**word_length.
The encoding uses two queries per invocation with normalization
2**width*value_scale; dense QRAM storage carries no sparse-access speedup.
Memory snapshots stay outside RIR. Preparation resolves resource aliases
through module calls without expanding Repeat. Missing snapshots, conflicting
bank names, unbound oracles, and incompatible widths fail before execution.
Select an existing QODE algorithm¶
Named method |
Configuration |
Preconditions and magnitude readout |
|---|---|---|
|
|
Finite polynomial; known normalization supports |
|
|
Requires |
|
|
Requires |
|
|
Caller validates the auxiliary grid and physical recovery region |
For example, reuse LCHS with a caller-supplied quadrature:
from oracq.algorithms.qode.lchs import QuadraturePlan
decay = oracq.ODEProblem(
generator=oracq.MatrixInput([[-1, 0], [0, -2]]),
initial=oracq.UniformInput(), final_time=0.01,
dissipative=True, evidence="diagonal real nonpositive generator",
)
lchs_instance = oracq.qode_solve(
decay, method="lchs",
config=oracq.ODEConfig(plan=QuadraturePlan.cauchy(cutoff=1)),
).prepare()
assert lchs_instance.prepared.report.ok
dissipative=True records a mathematical promise; preparation checks the
declaration, not a proof of Hermitian(G) <= 0. No automatic generator shift
is applied. ODEConfig.hamiltonian_function replaces the Hamiltonian simulation
protocol used inside LCHS, CBMD, and Schrödingerization.
These three methods currently expose conditional amplitudes through the
instance, without a complete physical magnitude recovery contract;
physical_values() therefore raises. This remains true if an arbitrary
QODESolver is passed as method. An application-defined
ODEMethod.prepare(problem) -> PreparedODE can supply its own verified scale
and reuse the same lifecycle. Configure custom methods directly.
This entry covers autonomous homogeneous linear systems. Time-dependent
generators, source terms, and nonlinear ODEs require explicit adaptations.
The existing low-level QODEProblem, QODESolver, and linear_qode interfaces
remain available for circuit composition.
Run the complete example:
python examples/ode_instances.py
python examples/ode_instances.py --generator qram --initial qram --degree 1 --backend pysparq
python examples/ode_instances.py --backend originir_ext
python examples/ode_instances.py --method lchs
See the ODE input API, ODE instance API, and existing QODE assembly tutorial.