Skip to content

Reuse a rotation with different angles

Declare one RY circuit, bind a numerical angle and run it without changing the symbolic declaration. Then use the same declaration for a short scan. Start after the Bell circuit and gate basics; run the commands from the repository root in your installed environment.

For a qubit initially in |0⟩, RY(θ) gives cos(θ/2)|0⟩ + sin(θ/2)|1⟩. Predict P(1) for θ = 0, π/2 and π before running. The API uses radians.

Keep the declaration reusable

"""Declare and bind a typed Digital rotation parameter.

Parameters remain symbolic while a circuit is reusable. ``bind`` creates a
new executable circuit, checks missing or unknown values, and preserves the
original declaration for another experiment.
"""

from __future__ import annotations

import json

from cascaqit import Circuit


def main() -> None:
    """Bind one angle and compare declaration with executable circuit."""
    declaration = Circuit(1, program_id="lesson.digital.parameter")
    theta = declaration.parameter("theta", lower_bound=-3.2, upper_bound=3.2)
    declaration.ry(theta, 0).measure_all()

    # Binding returns a separate circuit; the declaration stays symbolic.
    bound = declaration.bind({"theta": 0.5})
    result = bound.run(shots=0, seed=202, return_probabilities=True)

    payload = {
        "track": "digital_developer",
        "level": "foundation",
        "lesson": "parameter_binding",
        "facts": {
            "declaration_is_bound": declaration.is_bound,
            "bound_is_bound": bound.is_bound,
            "parameter_names": [item.name for item in bound.parameters],
            "bound_angle": bound.operations[0].arguments["theta"],
            "probability_sum": round(sum((result.probabilities or {}).values()), 12),
        },
        "boundaries": {
            "hardware_execution": False,
            "cloud_execution": False,
            "network_accessed": False,
            "credentials_loaded": False,
        },
    }
    print(json.dumps(payload, sort_keys=True))


if __name__ == "__main__":
    main()

Download the full script

python3 examples/user/tracks/digital_developer/02_foundation_parameter_binding_en.py

declaration.parameter("theta", ...) creates a named parameter owned by the circuit. The chosen interval [-3.2, 3.2] includes the three practice angles. Bounds are input constraints; they do not change the meaning of RY.

bind({"theta": 0.5}) returns a separate bound circuit. The original remains symbolic and can be bound again. A bound circuit is not a promise of immutability: circuit builder methods can still change that object. Preserve a separate declaration when you want to compare parameter points.

{
  "boundaries": {
    "cloud_execution": false,
    "credentials_loaded": false,
    "hardware_execution": false,
    "network_accessed": false
  },
  "facts": {
    "bound_angle": 0.5,
    "bound_is_bound": true,
    "declaration_is_bound": false,
    "parameter_names": [
      "theta"
    ],
    "probability_sum": 1.0
  },
  "lesson": "parameter_binding",
  "level": "foundation",
  "track": "digital_developer"
}

declaration_is_bound is false, bound_is_bound is true, and the actual gate angle is 0.5. probability_sum = 1 checks normalization but does not by itself validate the intended rotation. Add print(result.probabilities) in your copy: the predicted P(1) = sin²(0.25) ≈ 0.061209 supplies that independent check.

The example uses shots=0 and requests probabilities. It evolves the state but takes no finite measurement samples. An empty counts dictionary is expected on this path.

Turn one run into a scan

After declaring the circuit, bind each value separately:

from math import pi

for angle in (0.0, pi / 2, pi):
    candidate = declaration.bind({"theta": angle})
    result = candidate.run(shots=0, return_probabilities=True)
    print(angle, result.probabilities)

The probabilities of 1 should be 0, 1/2 and 1 within floating-point precision. Save angles alongside results; list position alone is a fragile way to identify a scan point.

Diagnose an invalid binding

Try bind({}), bind({"thta": 0.5}) and bind({"theta": 4.0}) separately, so one exception does not stop all three exercises. Read the ProgramValidationError message, correct the value or name and rerun. Also explain why increasing shots cannot repair an out-of-bounds angle.

Check your reasoning

The first binding omits the required theta; the second supplies an unknown name; the third exceeds the declared upper bound. Sampling happens after successful binding and execution, so changing shots cannot fix any of these input errors. At θ = π, a tiny residual P(0) can be floating-point roundoff rather than a physical error. Do not widen parameter bounds merely to silence an error without checking the intended scan range.

Next, combine reusable declarations in subcircuits and inverses. The Digital guide lists supported parameter expressions and their restrictions.

中文版

SDK 1.0.8a · `8b227bff`