Skip to content

Bind one phase across two blocks

Use one parameter to set both a Digital RZ angle and an Analog drive phase. The goal is to inspect where a binding lands and predict whether it changes the measured distribution. Complete shared state and Digital parameter binding first. Run from the repository root in the installed environment.

A name must describe the same quantity

The Digital block prepares RZ(φ) H |0⟩. Its Bloch vector is (cos φ, sin φ, 0). The Analog block uses zero detuning and a drive with the same phase, so its rotation axis is (cos φ, sin φ, 0) too. The prepared state is an eigenstate of this drive: evolution changes only its global phase. Predict equal Z probabilities even when the shared phase changes.

Both declarations use phase, a floating-point value in radians, default 0.2, bounded between -1 and 1. HybridProgram.parameters merges compatible declarations into one schema and records two target blocks. A repeated name with conflicting declarations is an error; it does not create two independent variables.

"""Share one canonical parameter across Digital and Analog blocks.

The same parameter name, type, unit, bounds, and default form one schema. A
Digital RZ angle and an Analog drive phase can therefore be bound together by
``HybridProgram`` without introducing mode-specific parameter adapters.
"""

from __future__ import annotations

import json
from typing import cast

from cascaqit import (
    AHSProgram,
    AtomRegister,
    Circuit,
    HybridProgram,
    LocalBackend,
    Waveform,
)


def main() -> None:
    """Declare, merge, bind, and execute one cross-block parameter."""
    digital = Circuit(1, program_id="lesson.hybrid.parameters.digital")
    digital_phase = digital.parameter(
        "phase", default=0.2, lower_bound=-1.0, upper_bound=1.0
    )
    digital.h(0).rz(digital_phase, 0)

    analog = AHSProgram(
        AtomRegister.line(count=1, spacing=5.0),
        program_id="lesson.hybrid.parameters.analog",
    )
    analog_phase = analog.parameter(
        "phase", unit="rad", default=0.2, lower_bound=-1.0, upper_bound=1.0
    )
    analog.drive(
        rabi=Waveform.constant(0.4, duration=0.1),
        detuning=Waveform.constant(0.0, duration=0.1),
        phase=analog_phase,
    )

    program = (
        HybridProgram("lesson.hybrid.parameters")
        .digital("prepare", digital)
        .analog("evolve", analog)
        .measure_all()
    )
    manager = program.parameters
    bound = program.bind({"phase": 0.35})
    result = LocalBackend(analog_time_steps=4).run(bound, shots=16, seed=102).result()

    digital_ir = cast(Circuit, bound.payload("prepare")).to_ir()
    analog_ir = cast(AHSProgram, bound.payload("evolve")).measure().to_ir()

    payload = {
        "track": "hybrid_researcher",
        "level": "foundation",
        "lesson": "canonical_parameters",
        "facts": {
            "schema_names": [item.name for item in manager.schema.parameters],
            "target_count": len(manager.targets),
            "digital_phase": digital_ir.circuit.operations[1].arguments["theta"],
            "analog_phase": analog_ir.hamiltonian.phase,
            "probabilities": result.probabilities,
            "counts_total": sum(result.counts.values()),
        },
        "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/hybrid_researcher/02_foundation_canonical_parameters_en.py

program.bind({"phase": 0.35}) creates a bound Hybrid program. Keep its return value: the original program remains a reusable template. The Digital angle and Analog phase both receive 0.35 rad.

Inspect the payloads and the probabilities

{
  "boundaries": {
    "cloud_execution": false,
    "credentials_loaded": false,
    "hardware_execution": false,
    "network_accessed": false
  },
  "facts": {
    "analog_phase": 0.35,
    "counts_total": 16,
    "digital_phase": 0.35,
    "probabilities": {
      "0": 0.5,
      "1": 0.4999999999999999
    },
    "schema_names": [
      "phase"
    ],
    "target_count": 2
  },
  "lesson": "canonical_parameters",
  "level": "foundation",
  "track": "hybrid_researcher"
}

Check schema_names, target_count, digital_phase and analog_phase. Together they show one declared parameter, two destinations and the actual numeric value at each destination. bound.parameters.assignments is not a binding receipt: the Hybrid parameter manager is reconstructed from its payload declarations, and that dictionary can be empty after successful binding.

The script reads snapshots returned by bound.payload(...). For the Analog snapshot it adds a measurement so that the public standalone to_ir() interface can inspect a complete program. That inspection does not insert an intermediate measurement into the Hybrid experiment; the actual run still measures only at its end.

probabilities should be approximately {"0": 0.5, "1": 0.5}. Sixteen sampled shots need not split evenly. This probability distribution alone cannot prove that the two bindings worked: several incorrect programs could also produce a balanced measurement. Inspect the numeric payloads as well as the physics.

Change a declaration or break the shared phase

  1. Bind phase=-0.6. Predict both numeric payload values and the Z probabilities.
  2. Bind phase=1.2, outside the declared bounds. Locate the binding diagnostic before attempting execution.
  3. Replace the Analog argument phase=analog_phase with the numeric phase=0.0, keeping the Digital binding at 0.35. Predict the new P(1).
Check your reasoning

The first change gives -0.6 in both payloads and leaves the ideal probabilities balanced. The second raises HYBRID_PARAMETER_BIND_INVALID. For the third, the Analog rotation is now about X while the initial Bloch vector has a Y component. With area θ = 0.4 × 0.1 = 0.04 rad, P(1) = [1 − sin(0.35) sin(0.04)]/2 ≈ 0.493144. Sharing a value is therefore a physical choice, not merely a convenient name.

Use different names when the two phases should vary independently. The parameter guide describes the schema and binding interfaces. Continue with parameter scans to execute multiple bindings without confusing their results.

中文版

SDK 1.0.8a · `8b227bff`