This module implements the Fermionic QAOA (FQAOA) converter for the Qamomile framework [yoshioka2023fermionic]. FQAOA translates the Hamiltonians into the representation of fermion systems, and the equality constraint is naturally incorporated as a constant number of particles condition.
The parameterized state of -layer QAOA is defined as:
where is the cost Hamiltonian, is the mixer Hamiltonian and and are the variational parameters. The variational parameters are optimized classically to minimize the expectation value . prepares the initial state using Givens rotation gates [jiang2018quantum].
This module provides functionality to convert optimization problems which written by jijmodeling
into FQAOA circuits (), construct cost Hamiltonians (), and decode quantum computation results.
The FQAOAConverter class extends the MathematicalProblemConverter base class, specializing in
FQAOA-specific operations such as ansatz circuit generation and result decoding.
Key Features:
Generation of FQAOA ansatz circuits
Construction of cost Hamiltonians for QAOA
Decoding of quantum computation results into classical optimization solutions
Overview¶
| Function | Description |
|---|---|
fqaoa_state | Generate complete FQAOA state. |
is_close_zero | Check if a given floating-point value is close to zero within a small tolerance. |
| Class | Description |
|---|---|
ExecutableProgram | A fully compiled program ready for execution. |
FQAOAConverter | FQAOA (Fermionic Quantum Approximate Optimization Algorithm) converter class. |
MathematicalProblemConverter | |
Transpiler | Base class for backend-specific transpilers. |
Functions¶
fqaoa_state [source]¶
def fqaoa_state(
p: qmc.UInt,
linear: qmc.Dict[qmc.UInt, qmc.Float],
quad: qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float],
num_qubits: qmc.UInt,
num_fermions: qmc.UInt,
givens_ij: qmc.Matrix[qmc.UInt],
givens_theta: qmc.Vector[qmc.Float],
hopping: qmc.Float,
gammas: qmc.Vector[qmc.Float],
betas: qmc.Vector[qmc.Float],
) -> qmc.Vector[qmc.Qubit]Generate complete FQAOA state.
Parameters:
| Name | Type | Description |
|---|---|---|
p | qmc.UInt | Number of FQAOA layers. |
linear | qmc.Dict[qmc.UInt, qmc.Float] | Linear coefficients of Ising model. |
quad | qmc.Dict[qmc.Tuple[qmc.UInt, qmc.UInt], qmc.Float] | Quadratic coefficients of Ising model. |
num_qubits | qmc.UInt | Number of qubits. |
num_fermions | qmc.UInt | Number of fermions for initial state. |
givens_ij | qmc.Matrix[qmc.UInt] | Matrix of shape (N, 2) with qubit index pairs for Givens rotations. |
givens_theta | qmc.Vector[qmc.Float] | Vector of length N with Givens rotation angles. |
hopping | qmc.Float | Hopping integral for the mixer. |
gammas | qmc.Vector[qmc.Float] | Vector of gamma parameters. |
betas | qmc.Vector[qmc.Float] | Vector of beta parameters. |
Returns:
qmc.Vector[qmc.Qubit] — FQAOA state vector.
is_close_zero [source]¶
def is_close_zero(value: float, abs_tol: float = 1e-15) -> boolCheck if a given floating-point value is close to zero within a small tolerance.
Parameters:
| Name | Type | Description |
|---|---|---|
value | float | The floating-point value to check. |
abs_tol | float | Absolute tolerance passed to :func:math.isclose. Defaults to 1e-15. |
Returns:
bool — True if the value is close to zero, False otherwise.
Classes¶
ExecutableProgram [source]¶
class ExecutableProgram(Generic[T])A fully compiled program ready for execution.
Contains compiled quantum, classical, and expectation-value segments.
Use sample() for multi-shot execution or run() for single
execution.
Example:
executable = transpiler.compile(kernel)
# Sample: multiple shots, returns counts
job = executable.sample(executor, shots=1000)
result = job.result() # SampleResult with counts
# Run: single shot, returns typed result
job = executable.run(executor)
result = job.result() # Returns kernel's return typeConstructor¶
def __init__(
self,
plan: ProgramPlan | None = None,
compiled_quantum: list[CompiledQuantumSegment[T]] = list(),
compiled_classical: list[CompiledClassicalSegment] = list(),
compiled_expval: list[CompiledExpvalSegment] = list(),
output_refs: list[str] = list(),
num_output_bits: int = 0,
) -> NoneAttributes¶
compiled_classical: list[CompiledClassicalSegment]compiled_expval: list[CompiledExpvalSegment]compiled_quantum: list[CompiledQuantumSegment[T]]has_parameters: bool Check if this program has unbound parameters.num_output_bits: intoutput_refs: list[str]parameter_names: list[str] Get list of parameter names that need binding.plan: ProgramPlan | Nonequantum_circuit: T Get the single quantum circuit.
Methods¶
get_circuits¶
def get_circuits(self) -> list[T]Get all quantum circuits in execution order.
get_first_circuit¶
def get_first_circuit(self) -> T | NoneGet the first quantum circuit, or None if no quantum segments.
run¶
def run(
self,
executor: QuantumExecutor[T],
bindings: dict[str, Any] | None = None,
) -> RunJob[Any] | ExpvalJobExecute once and return single result.
Parameters:
| Name | Type | Description |
|---|---|---|
executor | QuantumExecutor[T] | Backend-specific quantum executor. |
bindings | dict[str, Any] | None | Parameter bindings. Supports two formats: - Vector: {“gammas”: [0.1, 0.2], “betas”: [0.3, 0.4]} - Indexed: {“gammas[0]”: 0.1, “gammas[1]”: 0.2} |
Returns:
RunJob[Any] | ExpvalJob — RunJob that resolves to the kernel’s return type, or
RunJob[Any] | ExpvalJob — ExpvalJob if the program contains expectation value computation.
Raises:
ExecutionError— If no quantum circuit to executeValueError— If required parameters are missing
Example:
job = executable.run(executor, bindings={"gamma": [0.5]})
result = job.result()
print(result) # 0.25 (for QFixed) or (0, 1) (for bits)sample¶
def sample(
self,
executor: QuantumExecutor[T],
shots: int = 1024,
bindings: dict[str, Any] | None = None,
) -> SampleJob[Any]Execute with multiple shots and return counts.
Parameters:
| Name | Type | Description |
|---|---|---|
executor | QuantumExecutor[T] | Backend-specific quantum executor. |
shots | int | Number of shots to run. |
bindings | dict[str, Any] | None | Parameter bindings. Supports two formats: - Vector: {“gammas”: [0.1, 0.2], “betas”: [0.3, 0.4]} - Indexed: {“gammas[0]”: 0.1, “gammas[1]”: 0.2} |
Returns:
SampleJob[Any] — SampleJob that resolves to SampleResult with results.
Raises:
ExecutionError— If no quantum circuit to executeValueError— If required parameters are missing
Example:
job = executable.sample(executor, shots=1000, bindings={"gamma": [0.5]})
result = job.result()
print(result.results) # [(0.25, 500), (0.75, 500)]FQAOAConverter [source]¶
class FQAOAConverter(MathematicalProblemConverter)FQAOA (Fermionic Quantum Approximate Optimization Algorithm) converter class.
This class provides methods to convert optimization problems into FQAOA circuits, construct cost Hamiltonians, and decode quantum computation results.
Examples:
from qamomile.optimization.fqaoa import FQAOAConverter
# Initialize with a compiled optimization problem instance
fqaoa_converter = FQAOAConverter(compiled_instance, num_fermions=4)
# Generate cost Hamiltonian and transpile
cost_hamiltonian = fqaoa_converter.get_cost_hamiltonian()
executable = fqaoa_converter.transpile(transpiler, p=2)Constructor¶
def __init__(
self,
instance: ommx.v1.Instance,
num_fermions: int,
normalize_ising: Optional[Literal['abs_max', 'rms']] = None,
)Initialize the FQAOAConverter.
This method initializes the converter with the compiled instance of the optimization problem.
Parameters:
| Name | Type | Description |
|---|---|---|
instance | ommx.v1.Instance | ommx.v1.Instance. |
num_fermions | int | Number of fermions. This means the constraint . |
normalize_ising | Literal[abs_max, rms] | None | The normalization method for the Ising Hamiltonian. Available options: - “abs_max”: Normalize by absolute maximum value - “rms”: Normalize by root mean square Defaults to None. |
Attributes¶
instancenormalize_isingnum_fermions
Methods¶
cyclic_mapping¶
def cyclic_mapping(self) -> dict[tuple[int, int], int]Get variable maps between decision variable indices and qubit index .
Return:
dict[tuple[int, int], int] : A variable map for ring driver.
get_cost_hamiltonian¶
def get_cost_hamiltonian(self) -> qm_o.HamiltonianConstruct the Ising cost Hamiltonian from the spin model.
Builds a Pauli-Z Hamiltonian from the spin model’s linear and quadratic terms.
Returns:
qm_o.Hamiltonian — qm_o.Hamiltonian: The cost Hamiltonian.
get_fermi_orbital¶
def get_fermi_orbital(self) -> np.ndarrayCompute the single-particle wave functions of the occupied spin orbitals.
Return:
numpy.ndarray: A 2D numpy array of shape (num_fermions, num_qubits)
transpile¶
def transpile(
self,
transpiler: Transpiler,
*,
p: int,
hopping: float = 1.0,
) -> ExecutableProgramCompile FQAOA ansatz into an executable program with measurements.
MathematicalProblemConverter [source]¶
class MathematicalProblemConverter(abc.ABC)Constructor¶
def __init__(self, instance: ommx.v1.Instance | BinaryModel) -> NoneAttributes¶
instanceoriginal_vartypespin_model
Methods¶
decode¶
def decode(self, samples: SampleResult[list[int]]) -> BinarySampleSet | ommx.v1.SampleSetDecode quantum measurement results.
The return type tracks the input that built this converter:
Built from an :class:
ommx.v1.Instance— returns an :class:ommx.v1.SampleSetevaluated against the original (un-penalized) instance, so feasibility, objective, and per-constraint violations are available through OMMX’s own API (.summary,.summary_with_constraints,.best_feasible,.feasible,.objectives).Built from a :class:
BinaryModel— returns a :class:BinarySampleSetwith samples in the model’s original vartype (BINARY 0/1 or SPIN ±1), energies, and shot counts.
Parameters:
| Name | Type | Description |
|---|---|---|
samples | SampleResult[list[int]] | Raw quantum measurement results from ExecutableProgram.sample(...).result(). |
Returns:
BinarySampleSet | ommx.v1.SampleSet — BinarySampleSet | ommx.v1.SampleSet: see method description.
See Also:
:meth:decode_to_binary_sampleset: always returns a
:class:BinarySampleSet. Use it when you need the
QUBO-domain (penalty-included) energy — e.g. to drive a
classical optimizer that must penalize infeasibility.
Example:
>>> # OMMX in → OMMX out
>>> converter = QAOAConverter(ommx_instance)
>>> exe = converter.transpile(QiskitTranspiler(), p=2)
>>> result = exe.sample(QiskitTranspiler().executor(),
... shots=1024,
... bindings={"gammas": gs, "betas": bs}).result()
>>> sample_set = converter.decode(result)
>>> sample_set.best_feasible.objectivedecode_to_binary_sampleset¶
def decode_to_binary_sampleset(self, samples: SampleResult[list[int]]) -> BinarySampleSetDecode samples into a :class:BinarySampleSet.
Always returns a :class:BinarySampleSet, regardless of whether
this converter was constructed with an :class:ommx.v1.Instance
or a :class:BinaryModel. Use this when you need:
The QUBO-domain
energy(penalty-included), e.g. as the cost driving a classical optimizer like COBYLA — :meth:decodeon OMMX-backed converters returns the un-penalized OMMX objective which won’t penalize infeasibility.The per-state
samples/num_occurrences/vartypeviews from :class:BinarySampleSet.
For most usage — feasibility, original-objective evaluation,
per-constraint diagnostics — prefer the polymorphic
:meth:decode, which returns an :class:ommx.v1.SampleSet for
OMMX-backed converters.
Parameters:
| Name | Type | Description |
|---|---|---|
samples | SampleResult[list[int]] | Raw quantum measurement results from ExecutableProgram.sample(...).result(). |
Returns:
BinarySampleSet — keyed by the SPIN model’s original variable
BinarySampleSet — indices (the QUBO variable IDs for OMMX-backed converters)
BinarySampleSet — in the converter’s original_vartype — BINARY for
BinarySampleSet — OMMX-backed converters, the :class:BinaryModel’s declared
BinarySampleSet — vartype otherwise.
get_cost_hamiltonian¶
def get_cost_hamiltonian(self) -> qm_o.HamiltonianConstruct the cost Hamiltonian.
Subclasses must implement this method to build the appropriate Hamiltonian for their specific algorithm (e.g., Pauli-Z for QAOA, QRAC-encoded for QRAO).
Returns:
qm_o.Hamiltonian — qm_o.Hamiltonian: The cost Hamiltonian.
Transpiler [source]¶
class Transpiler(ABC, Generic[T])Base class for backend-specific transpilers.
Provides the full compilation pipeline from QKernel to executable program.
Usage:
transpiler = QiskitTranspiler()
Option 1: Full pipeline¶
executable = transpiler.compile(kernel, bindings={“theta”: 0.5}) results = executable.run(transpiler.executor())
Option 2: Step-by-step¶
block = transpiler.to_block(kernel) substituted = transpiler.substitute(block) affine = transpiler.inline(substituted) validated = transpiler.affine_validate(affine) folded = transpiler.constant_fold(validated, bindings={“theta”: 0.5}) analyzed = transpiler.analyze(folded) plan = transpiler.plan(analyzed) executable = transpiler.emit(plan, bindings={“theta”: 0.5})
Option 3: Just get the circuit (no execution)¶
circuit = transpiler.to_circuit(kernel, bindings={“theta”: 0.5})
With configuration (strategy overrides)¶
config = TranspilerConfig.with_strategies({“qft”: “approximate”}) transpiler = QiskitTranspiler(config=config)
Attributes¶
MAX_UNROLL_DEPTH: intconfig: TranspilerConfig Get the transpiler configuration.
Methods¶
affine_validate¶
def affine_validate(self, block: Block) -> BlockPass 1.5: Validate affine type semantics.
This is a safety net to catch affine type violations that may have bypassed frontend checks. Validates that quantum values are used at most once.
analyze¶
def analyze(self, block: Block) -> BlockPass 2: Validate and analyze dependencies.
classical_lowering¶
def classical_lowering(self, block: Block) -> BlockPass 2.25: Lower measurement-derived classical ops.
Identifies CompOp / CondOp / NotOp / BinOp
instances whose operand dataflow traces back to a measurement and
rewrites them to RuntimeClassicalExpr. Compile-time-foldable
and emit-time-foldable (loop-bound, parameter-bound) classical
ops are left unchanged.
Runs after analyze so the measurement-taint analysis has the
full dependency graph available, and before
validate_symbolic_shapes / plan / emit so downstream
passes can rely on the cleaner IR (in particular: future
segmentation work can dispatch on RuntimeClassicalExpr type
instead of the BitType-only heuristic).
constant_fold¶
def constant_fold(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockPass 1.5: Fold constant expressions.
Evaluates BinOp operations when all operands are constants
or bound parameters. This prevents quantum segment splitting
from parametric expressions like phase * 2.
emit¶
def emit(
self,
separated: ProgramPlan,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
) -> ExecutableProgram[T]Pass 4: Generate backend-specific code.
Parameters:
| Name | Type | Description |
|---|---|---|
separated | ProgramPlan | The separated program to emit |
bindings | dict[str, Any] | None | Parameter values to bind at compile time |
parameters | list[str] | None | Parameter names to preserve as backend parameters |
executor¶
def executor(self, **kwargs: Any = {}) -> QuantumExecutor[T]Create a quantum executor for this backend.
inline¶
def inline(self, block: Block) -> BlockPass 1: Inline all CallBlockOperations.
lower_compile_time_ifs¶
def lower_compile_time_ifs(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockPass 1.75: Lower compile-time resolvable IfOperations.
Evaluates IfOperation conditions (including expression-derived conditions via CompOp/CondOp/NotOp) and replaces resolved ones with selected-branch operations. Phi outputs are substituted with selected-branch values throughout the block.
This prevents SegmentationPass from seeing classical-only compile-time IfOperations that would otherwise split quantum segments.
partial_eval¶
def partial_eval(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockPass 1.75: Fold constants and lower compile-time control flow.
plan¶
def plan(self, block: Block) -> ProgramPlanPass 3: Lower and split into a program plan.
Validates C→Q→C pattern with single quantum segment.
resolve_parameter_shapes¶
def resolve_parameter_shapes(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockPass 0.75: Resolve symbolic Vector parameter shape dims.
Qamomile circuits are compile-time fixed-structure. Parameter
Vector[Float] / Vector[UInt] inputs carry symbolic
{name}_dim{i} shape Values so frontend code like
arr.shape[0] returns a usable handle. This pass looks at
bindings and, for every parameter array that has a concrete
binding, substitutes those symbolic dims with constants so that
downstream loop-bound resolution sees fixed lengths.
Parameters without a concrete binding are left as-is; their symbolic dims are harmless as long as no compile-time structure decision depends on them (the library QAOA pattern).
set_config¶
def set_config(self, config: TranspilerConfig) -> NoneSet the transpiler configuration.
Parameters:
| Name | Type | Description |
|---|---|---|
config | TranspilerConfig | Transpiler configuration to use |
substitute¶
def substitute(self, block: Block) -> BlockPass 0.5: Apply substitutions (optional).
This pass replaces CallBlockOperation targets and sets strategy names on CompositeGateOperations based on config.
Parameters:
| Name | Type | Description |
|---|---|---|
block | Block | Block to transform |
Returns:
Block — Block with substitutions applied
to_block¶
def to_block(
self,
kernel: QKernel,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
) -> BlockConvert a QKernel to a Block.
Parameters:
| Name | Type | Description |
|---|---|---|
kernel | QKernel | The QKernel to convert |
bindings | dict[str, Any] | None | Concrete values to bind at trace time (resolves array shapes) |
parameters | list[str] | None | Names to keep as unbound parameters |
When bindings or parameters are provided, uses kernel.build() to properly resolve array shapes from the bound data. Otherwise uses the cached hierarchical block for efficiency.
to_circuit¶
def to_circuit(self, kernel: QKernel, bindings: dict[str, Any] | None = None) -> TCompile and extract just the quantum circuit.
This is a convenience method for when you just want the backend circuit without the full executable.
Parameters:
| Name | Type | Description |
|---|---|---|
kernel | QKernel | The QKernel to compile |
bindings | dict[str, Any] | None | Parameter values to bind |
Returns:
T — Backend-specific quantum circuit
transpile¶
def transpile(
self,
kernel: QKernel,
bindings: dict[str, Any] | None = None,
parameters: list[str] | None = None,
) -> ExecutableProgram[T]Full compilation pipeline from QKernel to executable.
Parameters:
| Name | Type | Description |
|---|---|---|
kernel | QKernel | The QKernel to compile |
bindings | dict[str, Any] | None | Parameter values to bind (also resolves array shapes). Names in bindings and parameters must be disjoint — a name is either compile-time bound or runtime symbolic, never both. |
parameters | list[str] | None | Parameter names to preserve as backend parameters |
Returns:
ExecutableProgram[T] — ExecutableProgram ready for execution
Raises:
ValueError— If a name appears in bothbindingsandparameters. A name being in both is ambiguous (placeholder value vs runtime symbol) and used to silently miscompile control-flow predicates that depended on parameter-array elements; rejecting the overlap up front keeps the contract unambiguous.QamomileCompileError— If compilation fails (validation, dependency errors)
Pipeline:
to_block: Convert QKernel to Block
substitute: Apply substitutions (if configured)
resolve_parameter_shapes: Constant-fold symbolic Vector param dims
inline: Inline CallBlockOperations
affine_validate: Validate affine type semantics
partial_eval: Fold constants and lower compile-time control flow
analyze: Validate and analyze dependencies
validate_symbolic_shapes: Reject unresolved parameter shape dims
plan: Build ProgramPlan (segment into C->Q->C steps)
emit: Generate backend-specific code
unroll_recursion¶
def unroll_recursion(self, block: Block, bindings: dict[str, Any] | None = None) -> BlockFixed-point loop of inline ↔ partial_eval for self-recursive kernels.
Each iteration unrolls one layer of self-referential
CallBlockOperation and then folds the base-case
IfOperation via partial_eval. Terminates when no
CallBlockOperation remains (success), when the call count
stops decreasing (symbolic driver — self-calls are left in the
IR and handled by downstream passes), or when MAX_UNROLL_DEPTH
is reached (non-terminating recursion — raises).
validate_symbolic_shapes¶
def validate_symbolic_shapes(self, block: Block) -> BlockPass 2.5: Reject unresolved parameter shape dims in loop bounds.
Runs after analyze so dependency info is complete. Raises
QamomileCompileError with an actionable message when a
gamma_dim0-style symbolic Value reaches a ForOperation
bound without being folded to a constant by
ParameterShapeResolutionPass.