Digital Circuit Walkthrough¶
Circuit is the common Digital programming entrypoint. It supports static gates, typed Catalog arguments, reusable subcircuits, bounded transformations, terminal measurement, and local execution. DigitalOperationIR.arguments preserves canonical bool, int, and finite float values instead of converting every argument to a float.
Start with examples/learning/digital_first_run.py for a Bell circuit. Run the complete ergonomics example with:
python3 examples/user/digital_circuit_ergonomics.py
Declare Parameters¶
from cascaqit import Circuit
rotation = Circuit(("data",))
theta = rotation.parameter(
"theta",
lower_bound=-1.0,
upper_bound=1.0,
)
rotation.rx(theta, "data")
A parameter belongs to one Circuit. Expressions support constants, symbols, +, -, *, /, **, and unary signs. Calls, attributes, indexing, complex values, and non-finite results are rejected.
Reuse And Transform¶
declaration = Circuit(("data",))
declaration.compose(rotation)
declaration.compose(rotation.inverse())
repeated = declaration.repeat(2)
append() provides a uniform entrypoint for the 19 supported gates. compose() accepts an identical qubit layout or a complete explicit qubit_map; it merges identical parameter declarations and rejects conflicts before changing the receiver.
inverse() reverses measurement-free circuits. repeat(count) accepts 1..1024; a measured circuit can only use repeat(1). These methods return independent circuits and leave their source unchanged.
Bind And Run¶
bound = repeated.bind({"theta": 0.3})
result = bound.run(shots=0, return_probabilities=True)
bind() returns a new fully bound Circuit. Missing, unknown, out-of-bounds, non-finite, and invalid-expression values produce structured ProgramValidationError diagnostics. An unbound Circuit cannot call to_program() or run() and cannot become a Local Hybrid Digital payload.
An unbound Circuit can still be inspected without lowering or execution. Source visualization reads display-only declaration evidence, while Canonical visualization formats symbolic parameters structurally:
from cascaqit.visualization import build_circuit_visualization
source_view = build_circuit_visualization(rotation, stage="source")
assert source_view.nodes[0].label == "RX(theta)"
assert source_view.metadata["source_evidence_scope"] == "circuit_declaration"
source_map() remains unavailable until binding because ProgramSourceMapIR must reference a real Program semantic hash.
Angle and float arguments become finite floats after binding. Structural Catalog arguments keep their exact types:
assert isinstance(
bound.to_program().circuit.operations[0].arguments["theta"],
float,
)
from cascaqit.digital import std
qft = Circuit(3).append(std.qft(3, True, 0), (0, 1, 2)).to_program()
arguments = qft.circuit.operations[0].arguments
assert type(arguments["num_qubits"]) is int
assert type(arguments["do_swaps"]) is bool
assert type(arguments["approximation_degree"]) is int
Add A Control¶
Circuit-level control supports every static unitary gate in the fluent Circuit API, including parameterized rotations, H, U, SWAP, and nested controls:
source = Circuit(("target",))
theta = source.parameter("theta")
source.h("target").rx(theta, "target")
controlled = source.controlled("control")
nested = controlled.controlled("outer").bind({"theta": 0.25})
Each added control qubit is placed first. The normalized operations view represents the result as the original base definition plus a control modifier; it does not need a separate name for every controlled gate. Nested positive and negative controls preserve one bit of control_state per ordered control operand, so an outer positive control around an existing negative control produces 10, not 11. A circuit with terminal measurement is rejected atomically. This API represents and locally simulates controls over the built-in Circuit gate set; it does not perform arbitrary matrix synthesis, allocate ancillas, or prove that a hardware Target can execute the result.
Inspect Operations And Facades¶
cascaqit.digital owns the versioned operation model. The fluent Circuit and std are executable entrypoints, while operations exposes their normalized Definition/Application form:
from cascaqit.digital import standard_catalog, std
catalog = standard_catalog()
crx = std.rx(0.25).control().on("control", "target")
toffoli = std.toffoli().on("c0", "c1", "target")
assert catalog.resolve(crx.definition).semantic_kind == "unitary"
assert crx.definition == std.rx.key
assert crx.modifiers[0].control_count == 1
assert toffoli.definition == std.x.key
assert toffoli.modifiers[0].control_count == 2
The Canonical Core also defines rphi, xy, rxx, ryy, rzz, rzx, reset, barrier, delay, and the versioned QFT, H-layer, and GHZ-preparation composite contracts. The three built-in Composite operations have deterministic semantic normalization used by the local Simulator and Compiler, with exact/approximate policy, quota, cycle, and GHZ input-contract checks. General user-defined Composite factories or template interpretation and automatic Target decomposition selection are not implemented.
Facade spelling is non-semantic display evidence. A Circuit keeps declaration evidence separately and, after binding, can project it into ProgramSourceMapIR:
import math
s_circuit = Circuit(1, program_id="same").s(0)
p_circuit = Circuit(1, program_id="same").p(math.pi / 2, 0)
assert s_circuit.to_program().stable_hash() == p_circuit.to_program().stable_hash()
assert s_circuit.source_map().stable_hash() != p_circuit.source_map().stable_hash()
Pass a Circuit directly to a Source visualization, or pass source_map=circuit.source_map() with its DigitalProgramIR. A bare Program has no facade evidence, so Source falls back to canonical labels and reports source_label_status="unavailable". Source Map data never affects optimization, compilation, Target matching, execution, or Program hashes.
Run digital_operation_catalog.py with --output artifacts/digital_operation_catalog.html to inspect the facade-to-definition mappings and save a representative Source circuit.
Run Digital O1¶
O0 preserves the input gate order. Explicit O1 performs deterministic, exact adjacent rewrites and returns before/after hashes, metrics, and lineage:
from cascaqit.digital import std, build_digital_program, optimize_digital_program
program = build_digital_program(
program_id="program.o1",
qubits=("q0",),
operations=(
std.rx(0.25).on("q0", operation_id="rx0"),
std.rx(0.75).on("q0", operation_id="rx1"),
),
)
optimization = optimize_digital_program(program)
assert optimization.program.circuit.operations[0].arguments == {"theta": 1.0}
assert optimization.report.transformations[0].transformation_kind == "rotation_merge"
CompilerPipeline(options=CompilerOptions(optimization_level=1)) uses the same rewritten program and stores the report in the compiled result metadata. Current O1 removes identity and exact zero rotations, cancels adjacent inverse/self-inverse Applications only when the complete modifier state proves that rewrite, and merges adjacent numeric rotations on the same axis and operands. True inverse pairs record inverse_pair_elimination with opposite inverse-parity evidence; repeated Catalog-proven self-inverse Applications record self_inverse_pair_elimination. A power modifier conservatively blocks both predicates. O1 does not reorder across intervening operations or apply approximate, symbolic, or hardware-specific rewrites.
Run digital_o1_optimization.py with --output artifacts/digital_o1_optimization.html to compare source and optimized probabilities and save the Canonical circuit with its transformation table.
Measurement And Results¶
Add measurement only after composition and transformation:
measured = bound.measure_all(key="m")
result = measured.run(shots=32, seed=7, return_probabilities=True)
Read result.metadata["bitstring_ordering"]["qubit_order"] before interpreting counts. Measurement is terminal; mid-circuit measurement, reset, classical conditions, feedback, and dynamic control are not available.
All paths on this page are local and deterministic when a seed is supplied. They do not submit to Hanyuan hardware or CASCAQit Cloud and do not access network endpoints or credentials.