Skip to content

Qubit Release

The following page describes how to let the engine know it can release qubits on the Classiq platform. Indicating which qubits may be released allows the synthesis engine to reuse them to provide better, narrower quantum circuits. We stress released qubit declaration should be done with care since incorrect instructions will most likely lead to malfunctioning circuits.

One may indicate the release of qubits used either implicitly (aiding zero qubits) and explicitly (functional qubits). The following sections describe the differences between the two and their respective release methods.

Implicit Qubit Release

Aiding zero qubits are qubits used implicitly to match input and output sizes. Note that these are distinct from auxiliary qubits and play a different role. Auxiliary qubits enter a function in a zero-state and exit as such. On the other hand, zero qubits enter in state zero and exit as outputs. Auxiliary qubits are implementation-dependent, in contrast to zero inputs.

Consider, for example, the case of an addition function. Suppose we add two unsigned integer registers. Generally speaking, the result is larger and requires more qubits for its representation. Even if the computation is done "in place," one should add one more qubit as an MSB. Note that the added qubit is independent of the choice of the addition implementation. We call this new qubit is a zero qubit. It is added implicitly by the Classiq engine.

Suppose we perform an addition operation, use the result, and with the intent of writing efficient code, we uncompute the addition operation to release the added MSB qubit. Inversion doesn't automatically release the qubit, as elaborated on in the inversion documentation. In fact, the qubit necessarily returns to state zero after uncomputation if and only if the entire operation in-between computation and uncomputation is "quantum free." Knowing the functional model and whether addition qubit release is allowed, it may be requested using the release_by_inverse flag, as depicted in the examples below. Note the flag is effective only if the is_inverse flag is switched on as well.

Usage

{
"logic_flow": [
  {
    "function": "Adder",
    "function_params": {
      "left_arg": { "size": 3, "name": "adder_arg" },
      "right_arg": 1,
      "inplace_arg": "left",
      "output_name": "adder_result"
    },
    "inputs": { "adder_arg": "arg_in_wire" },
    "outputs": { "adder_result": "adder_to_multiplier_wire" },
    "is_inverse": false
  },
  {
    "function": "Multiplier",
    "function_params": {
      "left_arg": { "size": 4, "name": "multiplier_arg" },
      "right_arg": 2,
      "output_name": "multiplier_result"
    },
    "inputs": { "multiplier_arg": "adder_to_multiplier_wire" },
    "outputs": {
      "multiplier_result": "multiplier_result_wire",
      "multiplier_arg": "multiplier_to_adder_uncomp_wire"
    }
  },
  {
    "function": "Adder",
    "function_params": {
      "left_arg": { "size": 3, "name": "adder_arg" },
      "right_arg": 1,
      "inplace_arg": "left",
      "output_name": "adder_result"
    },
    "inputs": { "adder_result": "multiplier_to_adder_uncomp_wire" },
    "outputs": { "adder_arg": "arg_out_wire" },
    "is_inverse": true,
    "release_by_inverse": true
  }
],
"inputs": { "model_arg": "arg_in_wire" },
"outputs": { "model_arg": "arg_out_wire", "result": "multiplier_result_wire" }

}
from classiq import ModelDesigner
from classiq.builtin_functions import Adder, Multiplier
from classiq.interface.generator.arith.register_user_input import RegisterUserInput
from classiq.interface.generator.arith.binary_ops import ArgToInplace

input_size: int = 3
model_arg = RegisterUserInput(size=input_size, name="adder_arg")
adder_params = Adder(
    left_arg=model_arg,
    right_arg=1,
    inplace_arg=ArgToInplace.LEFT,
    output_name="adder_result",
)
multiplier_params = Multiplier(
    left_arg=RegisterUserInput(size=input_size + 1, name="multiplier_arg"),
    right_arg=2,
    output_name="multiplier_result",
)

model_designer = ModelDesigner()
model_inputs = model_designer.create_inputs({"model_arg": model_arg})

adder_outputs = model_designer.Adder(
    params=adder_params, in_wires={"adder_arg": model_inputs["model_arg"]}
)
multiplier_outputs = model_designer.Multiplier(
    params=multiplier_params, in_wires={"multiplier_arg": adder_outputs["adder_result"]}
)
adder_uncomp_outputs = model_designer.Adder(
    params=adder_params,
    in_wires={"adder_result": multiplier_outputs["multiplier_arg"]},
    is_inverse=True,
    release_by_inverse=True,
)

model_designer.set_outputs(
    {
        "model_arg": (adder_uncomp_outputs["adder_arg"], model_arg),
        "result": (multiplier_outputs["multiplier_result"], model_arg),
    }
)

Explicit Qubit Release

Explicit qubit release refers to the release of functional registers, the outputs of any function. To declare that an output register is in the zero-state, simply wire to "0" in the textual model or use ModelDesigner.release_qregs in the SDK.

Wiring zero to an output declares its release. The functionality is available for any function, inverted or not. However, as stressed before, it must be done meticulously.

Below we use explicit outputs to convert a boolean function, with the result coded on qubits, to an oracle that codes the result on phases. We use a CCX oracle as a simple example. The X and H gates create a minus state for a phase kickback.

Usage

{
  "logic_flow": [
    {
      "function": "XGate",
      "function_params": {},
      "outputs": { "TARGET": "minus_state_inner_wire" }
    },
    {
      "function": "HGate",
      "function_params": {},
      "inputs": { "TARGET": "minus_state_inner_wire" },
      "outputs": { "TARGET": "minus_state_wire" }
    },
    {
    "function": "CCXGate",
    "function_params": {},
    "inputs": { "TARGET": "minus_state_wire", "CTRL": "argument_in_wire" },
    "outputs": {
      "TARGET": "minus_state_uncomp_wire",
      "CTRL": "argument_out_wire"
    }
  },
  {
    "function": "HGate",
    "function_params": {},
    "inputs": { "TARGET": "minus_state_uncomp_wire" },
    "outputs": { "TARGET": "minus_state_uncomp_inner_wire" }
  },
  {
    "function": "XGate",
    "function_params": {},
    "inputs": { "TARGET": "minus_state_uncomp_inner_wire" },
    "outputs": { "TARGET": "0" }
  }
],
"inputs": { "ARG": "argument_in_wire" },
"outputs": { "ARG": "argument_out_wire" }
}
from classiq import ModelDesigner, QReg
from classiq.interface.generator.arith.register_user_input import RegisterUserInput
from classiq.builtin_functions.standard_gates import CCXGate, XGate, HGate

model_designer = ModelDesigner()
model_inputs = model_designer.create_inputs({"ARG": RegisterUserInput(size=2)})

x_outputs = model_designer.XGate(params=XGate())
h_outputs = model_designer.HGate(params=HGate(), in_wires=x_outputs)
ccx_params = CCXGate()
ccx_outputs = model_designer.CCXGate(
    params=ccx_params,
    in_wires={"TARGET": h_outputs["TARGET"], "CTRL": model_inputs["ARG"]},
)
h_uncomp_outputs = model_designer.HGate(
    params=HGate(), in_wires={"TARGET": ccx_outputs["TARGET"]}
)
out = QReg(size=1)
model_designer.XGate(
    params=XGate(), in_wires=h_uncomp_outputs, out_wires={"TARGET": out}
)

model_designer.release_qregs(out)

model_designer.set_outputs({"ARG": (ccx_outputs["CTRL"], ccx_params.outputs["CTRL"])})