{
"cells": [
{
"cell_type": "markdown",
"id": "d26e3b44",
"metadata": {},
"source": [
"# Decomposing gate-based circuits: qiskit and myQLM"
]
},
{
"cell_type": "markdown",
"id": "f5994d25",
"metadata": {},
"source": [
"In this notebook, we show how circuits from Qiskit and myQLM can be converted into Perceval circuits. To do so, we take 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": "markdown",
"id": "3ed1c79e",
"metadata": {},
"source": [
"As usual, we start by importing the needed libraries. Note that this notebook requires the installation of Qiskit and MyQLM (which can be easily done with `pip install qiskit` and `pip install myqlm`)."
]
},
{
"cell_type": "code",
"execution_count": 191,
"id": "9dea723b",
"metadata": {},
"outputs": [],
"source": [
"import perceval as pcvl\n",
"from perceval.components import catalog\n",
"from perceval.converters import QiskitConverter, MyQLMConverter\n",
"from perceval.algorithm import Analyzer, Sampler\n",
"\n",
"from qiskit import QuantumCircuit\n",
"from qiskit.quantum_info import Statevector\n",
"\n",
"import qat.lang.AQASM as qataqasm"
]
},
{
"cell_type": "markdown",
"id": "61e8fe05",
"metadata": {},
"source": [
"## Conversion from Qiskit Circuit"
]
},
{
"cell_type": "markdown",
"id": "1fa22c1b",
"metadata": {
"collapsed": false
},
"source": [
"## GHZ State generation in Qiskit"
]
},
{
"cell_type": "markdown",
"id": "57423a74",
"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": 192,
"id": "aef291d3",
"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": 192,
"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": "cca607ca",
"metadata": {},
"source": [
"We display the final state when starting from the input state $|000\\rangle$."
]
},
{
"cell_type": "code",
"execution_count": 193,
"id": "7edaa2b3",
"metadata": {},
"outputs": [
{
"data": {
"text/latex": [
"$$\\frac{\\sqrt{2}}{2} |000\\rangle+\\frac{\\sqrt{2}}{2} |111\\rangle$$"
],
"text/plain": [
""
]
},
"execution_count": 193,
"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": "9a4a941f",
"metadata": {},
"source": [
"## Conversion of Qiskit circuit to Perceval"
]
},
{
"cell_type": "markdown",
"id": "9d6365aa",
"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' paragraph in the 'Basics' section of the 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 [4 ancillary modes](https://doi.org/10.1073/pnas.1018839108) while post-selection employs [2 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+2$ ancillary modes. The option `use_postselection=False` only implements heralded CNOTs. Here it would mean $4+4$ ancillary modes. Note: the `use_postselection` option is `True` by default."
]
},
{
"cell_type": "code",
"execution_count": 194,
"id": "dccbd8c9",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 194,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"qiskit_converter = QiskitConverter(catalog, 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": "88ff50bd",
"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": 195,
"id": "8ddb1457",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
state
probability
\n",
"\n",
"\n",
"
|1,0,1,0,1,0>
1/2
\n",
"
|0,1,0,1,0,1>
1/2
\n",
"
|1,0,0,1,1,0>
0
\n",
"
|0,1,1,0,0,1>
0
\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": "842d5bd0",
"metadata": {
"collapsed": false
},
"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": 196,
"id": "f6b0b67d",
"metadata": {},
"outputs": [],
"source": [
"# use either quantum_processor (after Qiskit above) or converted_processor (after myqlm above)\n",
"u = quantum_processor.linear_circuit().compute_unitary(use_symbolic=False)"
]
},
{
"cell_type": "code",
"execution_count": 197,
"id": "81c2c816",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 197,
"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=\"triangle\")\n",
"pcvl.pdisplay(pc_norm, compact=True, render_size=0.5)"
]
},
{
"cell_type": "markdown",
"id": "ebba1fad",
"metadata": {
"collapsed": false
},
"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 or directly the CZ gate of myqlm 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": 198,
"id": "b5ed598b",
"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": 198,
"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": "f466bf5d",
"metadata": {
"collapsed": false
},
"source": [
"Then we call the converter like the previous example"
]
},
{
"cell_type": "code",
"execution_count": 199,
"id": "c2887058",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 199,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"state = Statevector.from_int(0, 2**3)\n",
"state = state.evolve(qiskit_circuit)\n",
"\n",
"qiskit_converter = QiskitConverter(catalog, 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": 200,
"id": "d04d8ffd",
"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": "4c76b332",
"metadata": {
"collapsed": false
},
"source": [
"This is the truth table of a CNOT gate"
]
},
{
"cell_type": "markdown",
"id": "6389b91b",
"metadata": {
"collapsed": false
},
"source": [
"## Conversion from MyQLM Circuit"
]
},
{
"cell_type": "markdown",
"id": "97c0f951",
"metadata": {
"collapsed": false
},
"source": [
"Analogous to `QiskitConverter`, employ a `MyQLMConverter` object to convert a MyQLM circuit to a Perceval processor with each qubit of the circuit represented by 2 modes and additional modes for ancillary photons to perform deterministically two-qubit gates. (Read above in section for qiskit converter for a discussion on how ancillary modes are set)."
]
},
{
"cell_type": "markdown",
"id": "57045947",
"metadata": {
"collapsed": false
},
"source": [
"## Circuit to generate GHZ State in MyQLM"
]
},
{
"cell_type": "code",
"execution_count": 201,
"id": "5c02732a",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Create a myqlm program\n",
"qprog = qataqasm.Program()\n",
"\n",
"# Allocate qbits to the Program\n",
"qbits = qprog.qalloc(3)\n",
"\n",
"# Add gates\n",
"qprog.apply(qataqasm.H, qbits[0])\n",
"qprog.apply(qataqasm.CNOT, qbits[0], qbits[1])\n",
"qprog.apply(qataqasm.CNOT, qbits[0], qbits[2])\n",
"\n",
"# Convert program to myqlm circuit\n",
"myqlm_circuit = qprog.to_circ()\n",
"\n",
"myqlm_circuit.display()"
]
},
{
"cell_type": "markdown",
"id": "a9614fd9",
"metadata": {
"collapsed": false
},
"source": [
"## Conversion of Myqlm circuit to Perceval"
]
},
{
"cell_type": "code",
"execution_count": 202,
"id": "8ded845f",
"metadata": {},
"outputs": [
{
"data": {
"image/svg+xml": [
"\n",
""
],
"text/plain": [
""
]
},
"execution_count": 202,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"myqlm_converter = MyQLMConverter(catalog, backend_name=\"Naive\")\n",
"converted_processor = myqlm_converter.convert(myqlm_circuit, use_postselection=True)\n",
"pcvl.pdisplay(converted_processor, recursive=True)"
]
},
{
"cell_type": "code",
"execution_count": 205,
"id": "63570ae7",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
state
probability
\n",
"\n",
"\n",
"
|1,0,1,0,1,0>
1/2
\n",
"
|0,1,0,1,0,1>
1/2
\n",
"
|1,0,0,1,1,0>
0
\n",
"
|0,1,1,0,0,1>
0
\n",
"\n",
"
"
],
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# computing results with converted processor from myqlm\n",
"converted_processor.with_input(pcvl.LogicalState([0,0,0]))\n",
"\n",
"sampler = Sampler(converted_processor)\n",
"\n",
"output_distribution_myqlm_pcvl = sampler.probs()[\"results\"]\n",
"pcvl.pdisplay(output_distribution_myqlm_pcvl, precision=1e-2, max_v = 4)"
]
},
{
"cell_type": "markdown",
"id": "9779e5e5",
"metadata": {
"collapsed": false
},
"source": [
"Note : The result is exactly the same as previously obtained from converter from Qiskit and is also the expected result."
]
},
{
"cell_type": "markdown",
"id": "d477274e",
"metadata": {
"collapsed": false
},
"source": [
"## Few remarks\n",
"- Controlflow operations such as measurement operator in the qiskit ciruit 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 1-Qubit gates and the following 2-Qubits gates - CNOT, CSIGN(/CZ), and, SWAP from MyQLM are supported."
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 5
}