Phase
The phase statement is used to compute and encode the result of some arithmetic computation in the phase of the respective quantum states. It applies a relative phase to computational-basis states of quantum variables proportional to the value of a specified expression over these variables. The phase statement can also specify a fixed rotation angle, i.e., a "global" phase, using an expression with no quantum variables. When a fixed phase rotation occurs under a controlled context, it affects only the controlling states. Otherwise, a fixed phase statement is undetectable.
phase statements with a quantum expression are often used to compute the cost of an optimization problem. Applying a fixed phase is useful in expressing phase oracles and reflections.
Syntax
phase ( phase-expression [ , coefficient ] )
def phase(phase_expr: SymbolicExpr, coefficient: float = 1.0) -> None:
pass
Semantics
- phase-expression may consist of quantum scalar variables, numeric constant literals, and classical scalar variables, composed using arithmetic operators. See below the set of supported operators.
- The coefficient expression is optional, and may include an execution parameter. Note that an execution parameter cannot occur in phase-expression if it contains quantum variables. If not provided, the default coefficient value is 1.0.
- The operation rotates each computational basis state about the Z axis by an angle equal to the value of phase-expression, multiplied by coefficient if specified.
- For phase-expression over quantum variable \(x_1, x_2, \ldots, x_n\) that computes the function \(f(x_1, x_2, \ldots, x_n)\) and coefficient \(=\theta\), the operation performed by the statement is \(|x\rangle \rightarrow e^{i\theta f(x_1, x_2, \ldots, x_n)} |x\rangle\).
- For phase-expression without quantum variables that evaluates to \(\theta\), the operation performed by the statement is \(|x\rangle \rightarrow e^{i\theta} |x\rangle\).
- The expression must be a polynomial in the quantum variables. It is compiled into an Ising-model Hamiltonian, which is evolved per the specified coefficient.
The following operators are supported:
- Add:
+
- Subtract:
-
(binary) - Negate:
-
(unary) - Multiply:
*
- Divide:
/
(by a classical value) - Power:
**
(quantum base, positive classical integer exponent)
The following operators are supported only when applied to QBit
, QNum[1]
,
and the classical integers 0
and 1
:
- Bitwise Or:
|
- Bitwise And:
&
- Bitwise Xor:
^
- Bitwise Not:
~
Note that when the expression consists of a single one-qubit variable, phase statement
is equivalent to the core-library function PHASE()
.
Examples
Example 1
In the following model phase \(x^2\) is applied to variable \(x\) with the coefficient \(\frac{\pi}{4}\). \(x\) is initialized to a superposition of the values 0, 1, 2, and 3. After the phase statement, state 1 is in phase \(\frac{\pi}{4}\) relative to state 0, state 2 is rotated \(\pi\) relative to state 0. State 3 is rotated \(\frac{\pi}{4}\), which is a full \(2\pi\) + \(\frac{\pi}{4}\) rotation, that is, the same phase as state 1.
qfunc main(output x: qnum) {
allocate(2, x);
hadamard_transform(x);
phase (x**2, pi/4);
}
from classiq import qfunc, Output, QNum, allocate, hadamard_transform, phase
from classiq.qmod.symbolic import pi
@qfunc
def main(x: Output[QNum]):
allocate(2, x)
hadamard_transform(x)
phase(x**2, pi / 4)
Visualizing the synthesized quantum program, you can see how Z-rotations and controlled Z-rotations are used to achieve the required rotation.
When executing this model using a state-vector simulator, the relative phases of the different states can be observed. In
Example 2
The following example demonstrates the use of phase statement to encode the cost of a
max-cut problem, as the implementation of the cost-layer in a QAOA ansatz. The qubit
array v
represents the partition of the set of vertices in a graph into two, and
the expression inside the phase statement computes the number of edges that cross the
partition. Executing this model with the right set of parameter values will yield with
high probability an optimal solution.
qfunc main(gammas: real[4], betas: real[4], output v: qbit[3]) {
allocate(v);
hadamard_transform(v);
repeat (i: 4) {
phase(
(v[0] * (1 - v[1]) + v[1] * (1 - v[0])) // edge 0-1
+ (v[0] * (1 - v[2]) + v[2] * (1 - v[0])), // edge 0-2
gammas[i]
);
apply_to_all(lambda(q) {
RX(betas[i], q);
}, v);
}
}
from classiq import *
@qfunc
def main(
gammas: CArray[CReal, 4],
betas: CArray[CReal, 4],
v: Output[QArray[QBit, 3]],
):
allocate(v)
hadamard_transform(v)
for i in range(4):
phase(
(v[0] * (1 - v[1]) + v[1] * (1 - v[0])) # edge 0-1
+ (v[0] * (1 - v[2]) + v[2] * (1 - v[0])), # edge 0-2
gammas[i],
)
apply_to_all(lambda q: RX(betas[i], q), v)
Example 3
The following model applies phase
with an angle specified as a classical
expression, thus inserting a fixed phase under controlled contexts. In each
case, the states that satisfy the control condition rotate by \(\frac{\pi}{4}\)
relative to those that do not.
qfunc main(output qarr: qbit[2]) {
allocate(qarr);
hadamard_transform(qarr);
control (qarr[0]) {
phase (pi / 4);
}
control (qarr) {
phase (pi / 4);
}
}
from classiq import *
from classiq.qmod.symbolic import pi
@qfunc
def main(qarr: Output[QArray[2]]):
allocate(qarr)
hadamard_transform(qarr)
control(qarr[0], lambda: phase(pi / 4))
control(qarr, lambda: phase(pi / 4))
The cumulative result of both statements revealed by running a state-vector simulation is a uniform superposition of the four states with the following phases: