# Advanced operations of quantum circuit¶

In previous tutorial we introduced the basic usage of quantum circuit. In this tutorial, we will introduce how to operator the circuit in high level.

## mindquantum.core.circuit.controlled¶

The controlled method is used to add control qubits (which can be multiple) to any quantum circuit or quantum operator.

For example, let’s build a quantum circuit containing only two qubits and add a control qubit - $$q_2$$ to it by a controlled method:

:

from mindquantum.algorithm.library import qft
from mindquantum.core.circuit import controlled

u1 = qft(range(2))  # Build the quantum circuit
u1.svg()

: :

u2 = controlled(u1)(2)  # Add control qubit q2 to the circuit, returning a new circuit
u2.svg()

: Actually, controlled() returns a function. E.g:

:

u3 = controlled(u1)


We can add control qubits to the initial circuit u1 by calling u3, which has the same effect as above:

:

u4 = u3(2)
u4.svg()

: Note: The added control qubit cannot be the target qubit of a gate that already exists in the circuit, otherwise an error will be reported!

In addition, we can batch add control qubits to the quantum circuit. For example, in the following example we add control qubits - $$q_2$$ and $$q_3$$ to $$q_0$$ and $$q_1$$ respectively:

:

u = controlled(qft)
u = u([2, 3], [0, 1])  # Bulk add control qubits
u.svg()

: ## mindquantum.core.circuit.dagger¶

The dagger method is used to find the Hermitian conjugates of quantum circuits or quantum operators.

The following examples provide two ways of doing dagger operations:

:

from mindquantum.core.circuit import dagger

u1 = qft(range(3))
u1.svg()

: :

u2 = dagger(u1)
u2.svg()

: :

u3 = dagger(qft)
u4 = u3(range(3))
u4.svg()

: It can be seen that the circuits obtained by the two methods are the same.

## mindquantum.core.circuit.apply¶

The apply method is used to apply a quantum circuit or quantum operator to the specified qubits (which can be multiple).

:

from mindquantum.core.circuit import apply

u1 = qft([0, 1])
circuit1 = apply(u1, [1, 0])  # Apply quantum circuit u1 to qubits q1, q0
circuit1.svg()

: :

u2 = apply(qft, [1, 0])  # Apply qft to qubits q0, q1
circuit2 = u2([0, 1])
circuit2.svg()

: The add_prefix method is used to prefix the parameter names of parameterized quantum circuits or parameterized quantum operators. This is useful in neural network layering, for example:

:

from mindquantum.core.circuit import add_prefix, Circuit
from mindquantum.core.gates import RX, H

circ = Circuit().rx("theta", 0)
circ.svg()

: :

# After adding, the parameter "theta" becomes "l0_theta"
circ.svg()

: We can prefix the parameter name of a parameterized qubit after it acts on a specific qubit, or we can add it when it has not yet been applied to the qubit. E.g:

:

u = lambda qubit: Circuit([H.on(0), RX('a').on(qubit)])

u1 = u(0)
u1.svg()

: :

u2 = add_prefix(u, 'ansatz')
u2 = u2(0)
u2.svg()

: ## mindquantum.core.circuit.change_param_name¶

The change_param_name method is used to modify the parameter name of a parameterized quantum circuit or a parameterized quantum operator.

:

from mindquantum.core.circuit import change_param_name, Circuit
from mindquantum.core.gates import RX, H

u = lambda qubit: Circuit([H.on(0), RX('a').on(qubit)])

u1 = u(0)
u1 = change_param_name(u1, {'a': 'b'})
u1.svg()

: :

u2 = change_param_name(u, {'a': 'b'})
u2 = u2(0)
u2.svg()

: ## mindquantum.core.circuit.UN¶

The UN module is used to actuate quantum gates on different target qubits and control qubits.

The general format is as follows: mindquantum.circuit.UN(gate, maps_obj, maps_ctrl=None), the parameter gate is the gate to be executed, maps_obj is the target qubit to be executed, and maps_Ctrl is the control qubit (None if there are no control qubits). If each qubit implements the same quantum gate without parameters, UN(gate, N) can be used directly, where N represents the number of qubits.

Here is a simple example where we apply an H-gate to each qubit in the circuit we build:

:

from mindquantum.core.circuit import change_param_name, Circuit, UN
from mindquantum.core.gates import X, H, SWAP

circuit1 = Circuit()
circuit1 += UN(H, 4)  # Apply the H gate to each qubit
circuit1.svg()

: In the following example, we add a CNOT gate to the qubits $$q_2$$ and $$q_0$$, $$q_3$$ and $$q_1$$ respectively, where $$q_2$$ is the control bit, $$q_0$$ is the target bit; $$q_3$$ is the control bit , $$q_1$$ is the target bit:

:

circuit2 = UN(X, maps_obj=[0, 1], maps_ctrl=[2, 3])
circuit2.svg()

: For SWAP gate

:

circuit3 = UN(SWAP, maps_obj=[[0, 1], [2, 3]]).x(2, 1)
circuit3.svg()

: ## mindquantum.core.circuit.shift¶

The shift method is used to modify the qubit range of a given circuit, and the parameter is the step size to be shifted. Note that the step size cannot be negative. Moreover, it can only move to the last qubit, and cannot cycle to $$q_0$$ (the first qubit).

:

from mindquantum.core.circuit import shift, Circuit
from mindquantum.core.gates import X

circ = Circuit().x(1, 0)
circ.svg()

: :

circ = shift(circ, 1)
circ.svg()  # The qubit acting on the circuit changes from q0, q1 to q1, q2

: ### Case - Iris Classification: Building Encoder¶

The above examples of higher-order operations show the applicability and convenience of higher-order operations in quantum circuits. What magic will happen if we further combine them to build quantum circuits?

Next we will start with Classification of irises by quantum neural network. Build the Encoder part as an example, build the quantum circuit shown in the figure below: :

from mindquantum.core.circuit import shift, add_prefix, Circuit, UN
from mindquantum.core.gates import RZ, X, H

template = Circuit([X.on(1, 0), RZ('alpha').on(1), X.on(1, 0)])
encoder = UN(H, 4) + (RZ(f'{i}_alpha').on(i) for i in range(4)) + sum(add_prefix(shift(template, i), f'{i+4}') for i in range(3))
encoder.summary()

=====================================Circuit Summary=====================================
|Total number of gates  : 17.                                                           |
|Parameter gates        : 7.                                                            |
|with 7 parameters are  : 0_alpha, 1_alpha, 2_alpha, 3_alpha, 4_alpha, 5_alpha, 6_alpha.|
|Number qubit of circuit: 4                                                             |
=========================================================================================

:

encoder.svg()

: In the above code, we use the UN module to apply the parameter-free H gate to the target qubits $$q_0$$~$$q_3$$, and then apply the RZ(i_alpha) gate to the i-th qubit. Next, it is observed that there are three identical modules in the circuit, and they are all composed of a controlled X gate, a RZ gate and a controlled X gate. Therefore, we construct the corresponding template template, which is controlled by $$q_0$$, an X gate acting on $$q_1$$, an RZ gate acting on $$q_1$$, and an X gate controlled by $$q_0$$ acting on $$q_1$$ are formed. Then we use shift(template, i) to change the qubit range that the template acts on, and build three modules with the same structure but the qubit range differs by 1. The qubit ranges involved are $$q_0$$ and $$q_1$$, $$q_1$$ and $$q_2$$, $$q_2$$ and $$q_3$$. Finally, we use the add_prefix method to prefix the parameter names of all parameter quantum gates constructed by shift with a number.

So far, through these advanced operations of quantum circuit provided by MindSpore Quantum, we have built the required Encoder with only two lines of code!