# 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

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.

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.