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
.
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.