Skip to content

Quantum Operators

A number of basic quantum operations are provided as builtin operators. They are - inverting a unitary, raising a unitary to an integer power, and applying a unitary conditionally based on some quantum condition.

Syntax

control ( control ) { statements }

control ( condition-expression ) { statements }

invert { statements }

power ( power ) { statements }

@overload
def control(
    ctrl: Union[QBit, QArray[QBit]], operand: Union[QCallable, Callable[[], None]]
) -> None:
    pass


@overload
def control(ctrl: SymbolicExpr, operand: Union[QCallable, Callable[[], None]]) -> None:
    pass


def invert(operand: QCallable) -> None:
    pass


def power(power: CInt, operand: QCallable) -> None:
    pass

Control Semantics

  • The first Control argument (ctrl) may be a single variable (control) or a a Boolean expression (condition-expression).
  • The control argument is a qubit array of length one or more. The Control statement applies the statements block (operand) if all qubits are in state \(|1\rangle\).
  • The condition-expression is a logical expression over a quantum variable, whose value serves as the control for the application of the statements block (operand).
  • Currently, expressions are restricted to the form <var> == <classical-expression>, where both <var> and <classical-expression> are integer types.

Examples

In the following example, the 3 builtin quantum operators are used in function main. Note the last line, where a call to operator power is nested under a call to operator control.

qfunc foo(qb: qbit) {
  RX<0.25 * pi>(qb);
}

qfunc main() {
  qb1: qbit;
  qb2: qbit;
  allocate<1>(qb1);
  allocate<1>(qb2);
  invert {
    foo(qb1);
  }
  control (qb1) {
    power (2) {
      foo(qb2);
    }
  };
}
from sympy import pi
from classiq import RX, QBit, qfunc, allocate, invert, control, power


@qfunc
def foo(qb: QBit) -> None:
    RX(0.25 * pi, qb)


@qfunc
def main() -> None:
    qb1 = QBit("qb1")
    qb2 = QBit("qb2")
    allocate(1, qb1)
    allocate(1, qb2)
    invert(lambda: foo(qb1))
    control(qb1, power(2, lambda: foo(qb2)))

Synthesizing this model creates the quantum program shown below. You can see that the call to foo on qb1 is inverted, and the call on qb2 is applied twice, under the control of qb1.

builtin_operators.png

The following example demonstrates the use of control to rotate the state of a qubit by an angle determined by another quantum variable. Note how the condition of the control is comparing the quantum variable with the repeat index.

qfunc switch_rx(x: qnum, target: qbit) {
  repeat (i: 4) {
    control (x == i) {
      RX<pi / (2 ** i)>(target);
    }
  }
}

qfunc main(output res: qbit) {
  allocate<1>(res);
  x: qnum;
  prepare_int<2>(x);
  switch_rx(x, res);
}
from classiq import (
    qfunc,
    QNum,
    allocate,
    control,
    QBit,
    repeat,
    RX,
    Output,
    prepare_int,
)
from classiq.qmod.symbolic import pi


@qfunc
def switch_rx(x: QNum, target: QBit) -> None:
    repeat(4, lambda i: control(x == i, lambda: RX(pi / 2**i, target)))


@qfunc
def main(res: Output[QBit]):
    allocate(1, res)
    x = QNum("x")
    prepare_int(2, x)
    switch_rx(x, res)

Synthesizing this model creates the following quantum program. Note how control is implemented as positive and negative controls in the respective numeric qubits.

control_value.png