Scientific computing workflows: input models, solvers, and reproduction¶
English · 简体中文
This chapter strings together the complete examples of QHAM, Carleman, LCHS, and CBMD, explaining which objects each step produces, which interfaces remain open, and which conclusions the results can support. The paper keeps the mechanism and representative experiments; full usage, input variants, and step-by-step walkthroughs continue to be maintained here.
Start by distinguishing three layers¶
Layer |
What it decides |
Typical objects |
|---|---|---|
Mathematical input and approximation |
PDE, discretization, truncation order, initial-state norm, physical output window |
|
Quantum-algorithm generation |
which solver and internal simulation kernel to choose, which module calls to generate |
|
Implementation binding and execution |
implement the open slots with gate tables, QRAM, or arithmetic, and supply run-time data |
Replacing the solver usually requires regenerating the program; swapping a compatible implementation into an existing
slot can use bind.
Changing the public bit width or an alpha already used for assembly cannot be done by merely replacing a label.
Identical mathematical conditions and interface compatibility are also two different things: a generator being able
to provide a BE does not mean it satisfies the conditions of every QODE method.
QHAM: from the equation to the lifted linear input¶
Read the minimal QHAM tutorial first. What follows includes the actual example source directly, so imports, equations, data, and solver configuration always match the repository scripts.
from functools import partial
from oracq.algorithms.common.hamiltonian import taylor_hamiltonian
from oracq.algorithms.qode.ode import linear_qode
from oracq.applications.qham import (
Discretization,
Field,
Grid,
Known,
PolynomialPDE,
QHAMPlan,
qham_input_model,
structured_fd_bindings,
)
u = Field("u")
pde = PolynomialPDE.from_equations(
{"u": 0.1 * u.d("x", 2) - u * u.d("x") + Known("forcing")},
label="forced_burgers",
)
plan = QHAMPlan(pde, order=2)
grid = Grid(("x",), (4,), (1.0,), boundary="periodic")
space = Discretization(pde, grid, {"forcing": [0.05, 0, -0.05, 0]})
initial = space.encode_fields({"u": [0.1, 0.2, 0, -0.1]})
bindings = structured_fd_bindings(space, initial)
qode_input = qham_input_model(plan, bindings, eta=-0.4)
# Schrödingerization accepts a general generator; the recovery interval and time approximation still need later validation.
solver = linear_qode(
"schrodingerization", hamiltonian_function=partial(taylor_hamiltonian, degree=1)
)
solution = qode_input.solve(solver, 0.01)
# To switch to CBMD/LCHS instead, first apply an explicit global shift that makes the generator dissipative.
shifted = qode_input.dissipative_shift()
cbmd_solution = shifted.solve(
linear_qode("cbmd", hamiltonian_function=partial(taylor_hamiltonian, degree=1)), 0.01
)
Read this code through the lifecycle of its objects:
FieldandKnownbuild the unknown fields and the forcing terms referenced by name. The expression describes the field to be solved for; it does not apply an ordinary numeric function directly to quantum amplitudes.QHAMPlan(pde, order=2)fixes the finite HAM truncation and the quantum-adapted linearization rules. The plan can be saved, restored, and queried row by row; the full lifted matrix is never materialized up front.GridandDiscretizationdetermine the periodic grid, the derivatives, and the known data. Boundary conditions belong to this layer; they are not guessed by the solver.structured_fd_bindingsbuilds the port BEs from shifts, local coefficients, and multilinear contractions;qham_input_modelthen assembles the lifted generator and the initial state. The relative norms of the lifted initial state’s tensor blocks must be preserved; the blocks must not be normalized independently.linear_qodeselects the linear-evolution method;partial(taylor_hamiltonian, degree=1)explicitly fixes this example’s approximate simulation kernel.solveproduces a state oracle carrying signal and recovery information; it does not directly return a certified-converged classical PDE solution.dissipative_shiftexplicitly modifies the generator to meet another method’s preconditions. The corresponding growth-recovery factor must not be dropped, and this step must not be treated as a free way to raise the success rate.
The original method is in the QHAM paper by Xue et al.. The derivation of the finite closure, the tensor-block weights, and the physical output is in the QHAM mathematical notes; general polynomials, forcing, and discretization restrictions are in the full implementation documentation.
Carleman: open coefficient ports and a replaceable linear solver¶
The Carleman route starts from the coefficient ports of a polynomial ODE and builds the tensor lifting according to the chosen cutoff. Its truncation object differs from QHAM’s; the two truncation orders must not be treated as one shared accuracy parameter.
# 6. Ordinary PDE -> F_p ports -> Carleman -> replaceable linear solver.
u = Field("u")
pde = PolynomialPDE.from_equations({"u": 0.1 * u.d("x", 2) - u * u.d("x")})
space = Discretization(pde, grid)
ports = structured_fd_bindings(space, space.encode_fields({"u": [0.1, 0.2, 0, -0.1]}))
concrete = polynomial_from_bindings(ports)
coefficient_slots, bindings = [], {}
for order, coefficient in concrete.coefficients:
name = "BurgersF" + str(order)
slot = abstract_block_encoding(
name, coefficient.width, coefficient.signal_qubits, coefficient.alpha
)
coefficient_slots.append((order, slot))
bindings[name] = coefficient.operation
initial_slot = abstract_state_prep(
"BurgersInitial", concrete.width, concrete.initial.work_width
)
bindings["BurgersInitial"] = concrete.initial.operation
problem = PolynomialODE(
concrete.width, tuple(coefficient_slots), initial_slot, concrete.initial_norm
)
recovery: dict[str, float] = {}
for method, solver in (
("schrodingerization", schrodinger),
("shifted_lchs", shifted_solver(lchs, recovery)),
):
nonlinear_solver = partial(carleman_qode, linear_solver=solver, cutoff=2)
state = qpde_solver(nonlinear_solver)(PDEInput(problem, "burgers"), time)
records.append(
write(
"burgers_carleman_" + method,
state,
bindings,
notes={
"initial_norm": concrete.initial_norm,
"cutoff": 2,
**(recovery if method == "shifted_lchs" else {}),
},
)
)
Here concrete.coefficients stores the coefficient BEs grouped by nonlinear degree. The loop establishes, for each
coefficient, an open declaration with the same width, signal layout, and alpha, while keeping the matching implementation
in bindings. BurgersInitial is likewise an open state-preparation slot; the original initial-state norm is carried
separately by the host.
PolynomialODE therefore describes the mathematical input; carleman_qode performs the lifting; the passed-in
linear_solver handles the lifted linear problem. The script uses Schrödingerization
and an explicitly shifted LCHS respectively, and writes the recovery factors into the records. Running the generator
twice does not mean the two methods agree in success probability, truncation error, or circuit cost.
Mathematical conventions and interfaces are on the Carleman page.
Download the full input-model script with all the context.
LCHS: gate-table and QRAM bindings on the same open graph¶
An LCHS configuration contains a quadrature plan and a simulation kernel for the Hermitian branch. The Cauchy nodes and the first-order Taylor kernel in the example are concrete approximation choices, not a target accuracy that the language guarantees by default.
# 3. Same open graph, bound to gate / QRAM databases and state preparations respectively.
# The data is an integer encoding of rotation angles; A=diag(1, cos(pi/4)), G=-A.
angles = abstract_database("DiagonalAngles", 1, 2)
a = diagonal_block_encoding(angles, alpha=1.0)
state = lchs(scale(-1, a), initial, time)
records.append(
write(
"given_xor_gate_lchs",
state,
{
"DiagonalAngles": gate_database(1, 2, [0, 1]).operation,
"Initial": initial_gate.operation,
},
)
)
records.append(
write(
"given_xor_qram_lchs",
state,
{
"DiagonalAngles": Binding(
qram_database(1, 2).operation, {"table": "diagonal_angles"}
),
"Initial": Binding(qram_state_prep(1, 2).operation, {"angles": "initial_angles"}),
},
memory={
"diagonal_angles": [0, 1],
"initial_angles": qram_state_angles([1, 1], 2),
},
)
)
DiagonalAngles stores rotation angle words, and diagonal_block_encoding interprets them as
matrix coefficients. Initial is an independent initial-state preparation slot. The two write calls receive the same open
state but choose the gate-table and QRAM implementations respectively; the latter must additionally supply the two
tables in memory.
The script’s save_case saves the open, the first partially bound, and the final closed RIR separately,
and produces modular backend text. Intermediate resource capture remains an explicit parameter; memory contents are
never stuffed into the RIR. For a smaller standalone example see comparing implementations of an open program;
method configuration and applicability conditions are on the LCHS page.
CBMD: reusing QHAM input while handling the dissipative precondition explicitly¶
# ---- CBMD: QHAM open-coefficient input through a dissipative shift (input model reused across families) ------------
model = qham_input_model(plan, open_bindings, eta=-0.4)
state = model.dissipative_shift().solve(cbmd, 0.01)
records.append(
write("cbmd_qham_coeff_qram", state, qram_dict, memory=qram_memory)
)
CBMD’s method originates in contour matrix decomposition by Wang et al..
This code reuses the QHAM plan and the open coefficient input instead of reinterpreting the PDE inside CBMD.
The shift and recovery information produced by model.dissipative_shift() is a mathematical adaptation; only the final
qram_dict and qram_memory constitute the implementation and run-time data binding.
Nodes, residues, the finite truncation, and the omitted terms are on the CBMD page.
Do not infer from check().ok that the contour truncation error or the physical recovery error has been certified.
Input-model variants and reproduction¶
The input-model variant script provides structured ports, spectral
constructions, and QRAM data paths for four families. It is a construction example, not a cross-method accuracy
leaderboard.
PYTHONPATH=src python examples/general_qham.py
PYTHONPATH=src python examples/ode_input_models.py
PYTHONPATH=src python examples/input_models.py
PYTHONPATH=src python examples/research_workflow.py
PYTHONPATH=src python tools/build_qlss_comparison.py
Real-backend checks must use an interpreter with both pysparq and uniqc installed:
PYTHONPATH=src /path/to/backend/python examples/research_workflow.py --native
PYTHONPATH=src /path/to/backend/python tools/run_verification.py \
--group oracles --group blockencoding --group arithmetic \
--group mathfunc --group qham_qfvm --group ode
Numerical reports should explain three things separately: the implementation error of the circuits against an independent finite-precision reference, the method error of the finite precision or truncation relative to the original problem, and the post-selected success rate together with physical norm recovery. “The program exports”, “the backends agree with each other”, and “the method converges on the original PDE” are distinct conclusions. The full boundaries are in Applicability boundaries and validation status.
Format details, resource data, and extras¶
Detail to look up |
Where it is maintained |
|---|---|
RIR nodes, the full serialization text, and the OriginIR-ext grammar |
|
Open declarations, batched binding, resource capture |
|
Intermediate representation and serialization of mathematical functions |
|
Toffoli/rotation/QRAM counts, generic scale sweeps |
|
QFVM inputs, Roe arithmetic, and the recovery chain |
|
The wider algorithm catalog and validation status |
|
Classes, functions, parameter names, and returned objects |
These pages carry the full implementation and tutorial detail. Non-core algorithms remain maintained in the library,
but their count is not used as evidence of the framework’s superiority in the paper. The cross-framework code-size
experiment scripts remain under tools/expressiveness/; they are not part of the evidence retained for the paper.