##################### Ket Programming Guide ##################### Qubits ====== `Qubits `_ are the basic quantum computation units. As the the state of a qubit is only available to the quantum computer, the classical computer can only operate with its reference. In Ket, :class:`~ket.base.Quant` is a list-like type that stores references to qubits, which can be used to manipulate their state in the quantum computer. To allocate qubits, instantiate a :class:`~ket.base.Process` and then call its method :meth:`~ket.base.Process.alloc`, passing the number of qubits to be allocated as the argument. The only operations available for the classical computer that can change the quantum state are the quantum :mod:`~ket.gates` and the :func:`~ket.operations.measure` function, making it impossible to violate any principle of quantum mechanics, like the `no-cloning theorem `_. Example of qubit allocation and indexing: .. code-block:: py from ket import * p = Process() a = q.alloc() # Allocate 1 qubits |0⟩ b = q.alloc(4) # Allocate 4 qubits |0000⟩ ab = a+b # Concatenate |a⟩|b⟩ q0, q1, q2, q3 = ab # Unpack qubits # Indexing Qubits first, tail = ab[0], ab[1:] init, last = ab[:-1], ab[-1] Quantum Operations ================== Ket provides two kinds of operations to manipulate the quantum state. Quantum gates operate only on the qubits state and have no classical side effect. And the :func:`~ket.operations.measure` function, which collapses the quantum superposition and generates classical information. To entangle two or more qubits, Ket allows adding control qubits to any quantum gate. Moreover, as quantum gates are unitary operations, Ket provides ways to apply the inverse of a quantum gate. .. note:: You can see a list of the available quantum gates in :mod:`ket.gates`. Every quantum gate in Ket returns the reference for the operated qubits, allowing the concatenation of quantum gates. Also, you can use the :func:`~ket.operations.cat` and :func:`~ket.operations.kron` to create a new quantum gate from the concatenation and tensor product, respectively. For example: .. code-block:: py qubit = H(X(qubit)) # Prepare |+⟩ bell = cat(kron(H, I), CNOT) # Create bell gate Controlled Gates ---------------- Ket provides ways to add control qubits to any quantum gate or function that encapsulates quantum gates. For example, we can create a `Toffoli gate `_ by adding a control qubits gate to a Pauli X gate: .. code-block:: py def toffoli(c0 : Quant, c1 : Quant, t : Quant): ctrl(c0+c1, X)(t) equivalent to .. code-block:: py def toffoli(c0 : Quant, c1 : Quant, t : Quant): with control(c0, c1): X(t) The function :func:`~ket.operations.ctrl` adds control qubits to a call. The :class:`~ket.operations.control` statement opens a controlled scope, adding control qubits to every quantum gate and function call. By default, the controlled operations are applied in the superposition when the control qubits are in state :math:`\left|1\right>`. To change this behavior, use the :func:`~ket.gates.flip_to_control` function. No operation on the quantum computer other than quantum gate application is allowed inside a controlled scope or call. For example, calling a function that allocates or measures qubits will raise an error. .. note:: See the Ket API documentation for more examples and information on :func:`~ket.operations.ctrl` and :class:`~ket.operations.control`. Inverse Gates ------------- Ket provides three ways to call the inverse of quantum gate or function encapsulating quantum gates. The :func:`~ket.operations.adj` function returns the inverse of a quantum gate or function. Similarly, the :class:`~ket.operations.inverse` statement opens a scope that is executed backwards in the quantum computer. For example, given the implementation of a Quantum Fourier Transformation, its inverse can be defined as follows: .. code-block:: py def iqft(q: quant): adj(qft)(q) equivalent to .. code-block:: py iqft = adj(qft) equivalent to .. code-block:: py def iqft(process: Process, q : Quant): with inverse(): head, *tail = q while True: H(head) for i in range(len(tail)): ctrl(tail[i], PHASE(2*pi/2**(i+2)))(head) if not tail: break head, *tail = tail for i in range(len(q)//2): SWAP(q[i], q[len(q)-i-1]) .. admonition:: Quantum Fourier Transformation in Ket .. code-block:: py def qft(q : Quant, invert = True): head, *tail = q H(head) for i in range(len(tail)): ctrl(tail[i], PHASE(2*pi/2**(i+2)))(head) if tail: qft(tail, invert=False) if invert: for i in range(len(q)//2): SWAP(q[i], q[len(q)-i-1]) The :class:`~ket.operations.around` statement wraps a quantum operation :math:`U` between a quantum gate :math:`V` and its inverse :math:`V^\dagger`, a structure used in several quantum algorithms. For example, the decomposition of the :math:`R_{yy}` gate: .. math:: R_{yy}(\theta) = \overbrace{\left[R_x(\frac{\pi}{2})^{\otimes2}\right] \text{CNOT}}^V \underbrace{\left[I\otimes R_x(\theta) \right]}_U \overbrace{\text{CNOT} \left[R_x(\frac{-\pi}{2})^{\otimes2}\right]}^{V^\dagger} .. code-block:: py def ryy(theta : float, a : Quant, b : Quant): from math import pi with around(cat(kron(RX(pi/2), RX(pi/2)), CNOT), a, b): RZ(theta, b) The same way as a controlled operation, no quantum operation other than gate application is allowed inside an inverse scope or call. .. note:: See the Ket API documentation for more examples and information on :func:`~ket.operations.adj`, :class:`~ket.operations.inverse`, and :class:`~ket.operations.around`. Measurement ----------- The only operation that affects both classical and quantum states is quantum measurement. Ket allows the measurement of several qubits, storing the result in an unsigned integer, each bit representing the measurement of a qubit. The :func:`~ket.operations.measure` function accepts a :class:`~ket.base.Quant` as a parameter and returns a :class:`~ket.base.Measurement` variable. Reading the :attr:`~ket.base.Measurement.value` attribute of a :class:`~ket.base.Measurement` variable returns the measurement result from the quantum computer. Example of measurement in Ket: .. code-block:: py from ket import * p = Process a, b = p.alloc(2) CNOT(H(a), b) # Measure qubits measurement = measure(a+b) # Get value from the quantum computer result = measurement.value Quantum Dump ============ In simulated quantum execution, Ket allows dumping the quantum state to the classical computer. This operation has no side effect in the quantum simulation. With a :func:`~ket.base.QuantumState` instance, created using the :func:`~ket.operations.dump` function, a quantum state can be iterated over. The attribute :attr:`~ket.base.QuantumState.states` stores all the information of a quantum state, with which, for example, you can print its superposition: .. code-block:: py from ket import * p = Process() q = H(p.alloc(3)) d = dump(q) for state, amp in d.states.items(): print(f'{amp}|{state:0{len(q)}b}⟩') # (0.35355339059327384+0j)|000⟩ # (0.35355339059327384+0j)|001⟩ # (0.35355339059327384+0j)|010⟩ # (0.35355339059327384+0j)|011⟩ # (0.35355339059327384+0j)|100⟩ # (0.35355339059327384+0j)|101⟩ # (0.35355339059327384+0j)|110⟩ # (0.35355339059327384+0j)|111⟩ The basis states can repeat if the :class:`~ket.base.QuantumState` variable does not cover all qubits in the quantum system. The result of dumping parts of a quantum system does not necessarily correspond with its partial trace.