{
"cells": [
{
"cell_type": "markdown",
"id": "31758b36465d4ef7",
"metadata": {},
"source": [
"# Qiskit converter"
]
},
{
"cell_type": "markdown",
"id": "435a9b52cecb104b",
"metadata": {},
"source": [
"[Qiskit](https://qiskit.org/) is an opensource quantum development library. A Qiskit ``QuantumCircuit`` can be\n",
"converted to an equivalent Perceval ``Processor`` using ``QiskitConverter``."
]
},
{
"cell_type": "markdown",
"id": "4c9aa13757fd736b",
"metadata": {},
"source": [
"Note that this notebook requires the installation of Qiskit (which can be easily done with `pip install qiskit`).\n",
"This repository can also be installed with the command: `pip install .[Qiskit-bridge]` to automatically install Qiskit."
]
},
{
"cell_type": "markdown",
"id": "9fcd81f929bd21cf",
"metadata": {},
"source": [
"## Minimal code"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "39a45cce878ad4c",
"metadata": {},
"outputs": [],
"source": [
"import qiskit\n",
"from perceval_interop import QiskitConverter"
]
},
{
"cell_type": "markdown",
"id": "87057d8a76a68a3f",
"metadata": {},
"source": [
"Create a Quantum Circuit (the following is pure Qiskit syntax):"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "eef391fefc05fceb",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" ┌───┐ \n",
"q_0: ┤ H ├──■──\n",
" └───┘┌─┴─┐\n",
"q_1: ─────┤ X ├\n",
" └───┘\n"
]
}
],
"source": [
"qc = qiskit.QuantumCircuit(2)\n",
"qc.h(0)\n",
"qc.cx(0, 1)\n",
"print(qc.draw())"
]
},
{
"cell_type": "markdown",
"id": "b2b4cf0ceb29ac18",
"metadata": {},
"source": [
"Then convert the Quantum Circuit with Perceval QiskitConvertor"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "ada711c1119ba799",
"metadata": {},
"outputs": [],
"source": [
"qiskit_convertor = QiskitConverter()\n",
"perceval_processor = qiskit_convertor.convert(qc)"
]
},
{
"cell_type": "markdown",
"id": "248bb4eb1e1fcf45",
"metadata": {},
"source": [
"See also:\n",
"[Qiskit tutorial](https://quantum.cloud.ibm.com/docs/en/guides/hello-world)\n"
]
},
{
"cell_type": "markdown",
"id": "c1ca6b8c1483ebb3",
"metadata": {},
"source": [
"## Decomposing gate-based circuits"
]
},
{
"cell_type": "markdown",
"id": "21f2798a02203d96",
"metadata": {},
"source": [
"In this section, we show how circuits from Qiskit can be converted into Perceval circuits by taking the example of a simple gate-based circuit producing GHZ states. We then show the translation to a linear optical circuit. We also show the equivalence between the two circuits (gate-based and perceval)."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "44de78493a1b8ea1",
"metadata": {},
"outputs": [],
"source": [
"from qiskit import QuantumCircuit\n",
"from qiskit.quantum_info import Statevector\n",
"\n",
"import perceval as pcvl\n",
"from perceval.algorithm import Analyzer, Sampler\n",
"\n",
"from perceval_interop import QiskitConverter"
]
},
{
"cell_type": "markdown",
"id": "e2f25c0e333d40b3",
"metadata": {},
"source": [
"### GHZ State generation in Qiskit"
]
},
{
"cell_type": "markdown",
"id": "d133bb49b9ed4a0e",
"metadata": {},
"source": [
"We first define the circuit generating GHZ states of 3 qubits with Qiskit. To do so, we first act with a Hadamard gate on qubit 0 to put in superposition of state $|0\\rangle$ and $|1\\rangle$. Then we perform two CNOT gates using qubit 0 as control and qubits 1 and 2 as targets."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "dcdd1518d0040aef",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
┌───┐ \n",
"q_0: ┤ H ├──■────■──\n",
" └───┘┌─┴─┐ │ \n",
"q_1: ─────┤ X ├──┼──\n",
" └───┘┌─┴─┐\n",
"q_2: ──────────┤ X ├\n",
" └───┘
"
],
"text/plain": [
" ┌───┐ \n",
"q_0: ┤ H ├──■────■──\n",
" └───┘┌─┴─┐ │ \n",
"q_1: ─────┤ X ├──┼──\n",
" └───┘┌─┴─┐\n",
"q_2: ──────────┤ X ├\n",
" └───┘"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Create a Quantum Circuit acting on the q register\n",
"qiskit_circuit = QuantumCircuit(3)\n",
"\n",
"# Add a H gate on qubit 0\n",
"qiskit_circuit.h(0)\n",
"\n",
"# Add CX (CNOT) gates on control qubit 0 and target qubits 1 and 2\n",
"qiskit_circuit.cx(0, 1)\n",
"qiskit_circuit.cx(0, 2)\n",
"\n",
"# Draw the circuit\n",
"qiskit_circuit.draw()"
]
},
{
"cell_type": "markdown",
"id": "240768254ea6df1a",
"metadata": {},
"source": [
"We display the final state when starting from the input state $|000\\rangle$."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "bdc9bea3abf76bfc",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$$\\frac{\\sqrt{2}}{2} |000\\rangle+\\frac{\\sqrt{2}}{2} |111\\rangle$$"
],
"text/plain": [
""
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Set the initial state of the simulator to the ground state using from_int\n",
"state = Statevector.from_int(0, 2**3)\n",
"\n",
"# Evolve the state by the quantum circuit\n",
"state = state.evolve(qiskit_circuit)\n",
"\n",
"#draw using latex\n",
"state.draw('latex')"
]
},
{
"cell_type": "markdown",
"id": "5f69f0ca43138269",
"metadata": {},
"source": [
"### Conversion of Qiskit circuit to Perceval"
]
},
{
"cell_type": "markdown",
"id": "62c4cf7dc3e5d2ab",
"metadata": {},
"source": [
"With the use of `QiskitConverter`, we can transform the Qiskit circuit into a Perceval circuit. It uses 2 modes per qubit and additional modes for ancillary photons to perform deterministically two-qubit gates. Below the first six modes correspond to the three logical qubits (see the [Spatial Modes encoding](https://perceval.quandela.net/docs/v0.13/basics.html#spatial-modes-encoding) paragraph in the 'Basics' section of the Perceval documentation) of the gate-based circuit above.\n",
"\n",
"The other modes are used to successfully implement two-qubit gates via heralding or post-selection. Heralding employs [2 ancillary modes with 1 photon each](https://arxiv.org/abs/quant-ph/0110144) while post-selection employs [2 empty ancillary modes](https://journals.aps.org/pra/abstract/10.1103/PhysRevA.65.062324). With the option `use_postselection=True` in the method `.convert` on a `QiskitConverter` object, every CNOT but the last is implemented with a heralding scheme. Here it means that it would add $4$ ancillary modes with $2+0$ added photons. The option `use_postselection=False` only implements heralded CNOTs. Here it would mean $4$ ancillary modes with $2+2$ added photons. Note: the `use_postselection` option is `True` by default.\n",
"\n",
"Sometimes, the structure of a given circuit allows the converter to use more than one post-selected two-qubit gate in a single circuit. This is the case here."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "d6fc6db63bc310b3",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"qiskit_converter = QiskitConverter(backend_name=\"Naive\")\n",
"quantum_processor = qiskit_converter.convert(qiskit_circuit, use_postselection=True)\n",
"pcvl.pdisplay(quantum_processor, recursive=True)"
]
},
{
"cell_type": "markdown",
"id": "a54b6c989d3a5085",
"metadata": {},
"source": [
"With this converted circuit, we can now check that the resulting state is the same as before the conversion. By default, the input is the logical state $|000\\rangle_L$. Note that where Qiskit displays state in the order $|q_2q_1q_0\\rangle_L$, Perceval uses the reverse order $|q_0q_1q_2\\rangle_L$, but still shown as Fock states. Here, it doesn't change anything since we end with only $|000\\rangle_L$ and $|111\\rangle_L$ states."
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "f57bce17e8b658d3",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
state
probability
\n",
"\n",
"\n",
"
|0,1,0,1,0,1>
1/2
\n",
"
|1,0,1,0,1,0>
1/2
\n",
"\n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Not necessary here\n",
"quantum_processor.with_input(pcvl.LogicalState([0,0,0]))\n",
"\n",
"sampler = Sampler(quantum_processor)\n",
"\n",
"output_distribution = sampler.probs()[\"results\"]\n",
"pcvl.pdisplay(output_distribution, precision=1e-2, max_v = 4)"
]
},
{
"cell_type": "markdown",
"id": "f1afcbe14c7c59af",
"metadata": {},
"source": [
"This circuit can now be converted using a general interferometer decomposition so it can be implemented on a generic photonic chip."
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "38d8fb866a321b6e",
"metadata": {},
"outputs": [],
"source": [
"# use quantum_processor\n",
"u = quantum_processor.linear_circuit().compute_unitary(use_symbolic=False)"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "a3ed3e686c3cc5ef",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ub = (pcvl.Circuit(2)\n",
" // pcvl.BS(theta=pcvl.Parameter(\"theta\"))\n",
" // (0, pcvl.PS(phi=pcvl.Parameter(\"φ_a\"))))\n",
"\n",
"pc_norm = pcvl.Circuit.decomposition(u, ub, shape=pcvl.InterferometerShape.TRIANGLE)\n",
"pcvl.pdisplay(pc_norm, compact=True, render_size=0.5)"
]
},
{
"cell_type": "markdown",
"id": "b8ff1438ceb51a04",
"metadata": {},
"source": [
"## A cnot based on CZ\n",
"\n",
"Another interesting example we can explore is how to build a cnot from a CZ gate using qiskit then convert it to Perceval. We will apply the following equivalence:\n",
"\n",
"\n",
"\n",
"\n",
"The code in Qiskit:"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "255fac1fc160b556",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"q_0: ──────■──────\n",
" ┌───┐ │ ┌───┐\n",
"q_1: ┤ H ├─■─┤ H ├\n",
" └───┘ └───┘
"
],
"text/plain": [
" \n",
"q_0: ──────■──────\n",
" ┌───┐ │ ┌───┐\n",
"q_1: ┤ H ├─■─┤ H ├\n",
" └───┘ └───┘"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"qiskit_circuit = QuantumCircuit(2)\n",
"\n",
"# Add (CNOT) built using equivalence with H-CZ-H\n",
"qiskit_circuit.h(1)\n",
"qiskit_circuit.cz(0, 1)\n",
"qiskit_circuit.h(1)\n",
"# Draw the circuit\n",
"qiskit_circuit.draw()"
]
},
{
"cell_type": "markdown",
"id": "b93979ce0cb353e0",
"metadata": {},
"source": [
"Then we call the converter like the previous example"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "26350c6aeb3fd56a",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"state = Statevector.from_int(0, 2**3)\n",
"state = state.evolve(qiskit_circuit)\n",
"\n",
"qiskit_converter = QiskitConverter(backend_name=\"SLOS\")\n",
"quantum_processor = qiskit_converter.convert(qiskit_circuit)\n",
"\n",
"pcvl.pdisplay(quantum_processor, recursive=True) # the perceval processor can be displayed at this point if needed\n"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "a50c13671b4ddcd3",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
|1,0,1,0>
|1,0,0,1>
|0,1,1,0>
|0,1,0,1>
\n",
"\n",
"\n",
"
|1,0,1,0>
1
0
0
0
\n",
"
|1,0,0,1>
0
1
0
0
\n",
"
|0,1,1,0>
0
0
0
1
\n",
"
|0,1,0,1>
0
0
1
0
\n",
"\n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"input_states = [pcvl.BasicState([1, 0, 1, 0]), pcvl.BasicState([1, 0, 0, 1]), pcvl.BasicState([0, 1, 1, 0]), pcvl.BasicState([0, 1, 0, 1])]\n",
"analyzer = Analyzer(quantum_processor, input_states)\n",
"pcvl.pdisplay(analyzer)"
]
},
{
"cell_type": "markdown",
"id": "62ea7e71a2574b64",
"metadata": {},
"source": [
"## Few remarks\n",
"- Controlflow operations such as measurement operator in the qiskit circuit or `qiskit.circuit.QuantumCircuit.if_test` are not supported.\n",
"- Custom gates from Qiskit are also not supported at the moment (see [Issue#201](https://github.com/Quandela/Perceval/issues/201)).\n",
"- Only the following gates are supported:\n",
" - 1-Qubit gates\n",
" - 2-Qubits gate: CNOT\n",
" - 3-Qubits gate: Toffoli"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}