Skip to content

Commit 3d44774

Browse files
committed
Add qiskit -> ProjectQ parser using qiskit
1 parent 3977548 commit 3d44774

File tree

4 files changed

+604
-0
lines changed

4 files changed

+604
-0
lines changed

projectq/libs/qasm/__init__.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2020 ProjectQ-Framework (www.projectq.ch)
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
Contains functions/classes to handle OpenQASM
16+
"""
17+
18+
try:
19+
from ._parse_qasm_qiskit import read_qasm_file, read_qasm_str
20+
except ImportError: # pragma: no cover
21+
import warnings
22+
23+
err = ('Unable to import qiskit\n'
24+
'Please install it (e.g. using the command: '
25+
'python -m pip install qiskit')
26+
27+
warnings.warn(err + 'c\n'
28+
'The provided read_qasm_* functions will systematically'
29+
'raise a RuntimeError')
30+
31+
def read_qasm_file(eng, filename):
32+
# pylint: disable=unused-argument
33+
raise RuntimeError(err)
34+
35+
def read_qasm_str(eng, qasm_str):
36+
# pylint: disable=unused-argument
37+
raise RuntimeError(err)
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Copyright 2020 ProjectQ-Framework (www.projectq.ch)
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
""" Define function to read OpenQASM file format (using Qiskit). """
15+
16+
from projectq.ops import All, Measure, ControlledGate, SwapGate
17+
18+
from qiskit.circuit import QuantumCircuit
19+
20+
from ._qiskit_conv import gates_conv_table
21+
22+
# ==============================================================================
23+
24+
25+
def apply_gate(gate, qubits):
26+
"""
27+
Apply a gate to some qubits while separating control and target qubits.
28+
29+
30+
Args:
31+
gate (BasicGate): Instance of a ProjectQ gate
32+
qubits (list): List of ProjectQ qubits the gate applies to.
33+
"""
34+
if isinstance(gate, ControlledGate):
35+
ctrls = qubits[:gate._n]
36+
qubits = qubits[gate._n:]
37+
if isinstance(gate._gate, SwapGate):
38+
assert len(qubits) == 2
39+
gate | (ctrls, qubits[0], qubits[1])
40+
else:
41+
gate | (ctrls, qubits)
42+
elif isinstance(gate, SwapGate):
43+
assert len(qubits) == 2
44+
gate | (qubits[0], qubits[1])
45+
else:
46+
gate | qubits
47+
48+
49+
def apply_op(eng, gate, qubits, bits, bits_map):
50+
"""
51+
Apply a qiskit operation.
52+
53+
This function takes care of converting between qiskit gates and ProjectQ
54+
gates, as well as handling the translation between qiskit's and ProjectQ's
55+
qubit and bits.
56+
57+
Args:
58+
eng (MainEngine): MainEngine to use to the operation(s)
59+
gate (qiskit.Gate): Qiskit gate to apply
60+
qubits (list): List of ProjectQ qubits to apply the gate to
61+
bits (list): List of classical bits to apply the gate to
62+
"""
63+
64+
if bits:
65+
# Only measurement gates have clasical bits
66+
assert len(qubits) == len(bits)
67+
All(Measure) | qubits
68+
eng.flush()
69+
70+
for idx, bit in enumerate(bits):
71+
bits_map[bit.register.name][idx] = bool(qubits[idx])
72+
else:
73+
if gate.name not in gates_conv_table:
74+
if not gate._definition:
75+
return
76+
77+
gate_args = {gate._definition.qregs[0].name: qubits}
78+
79+
for gate_sub, quregs_sub, bits_sub in gate._definition.data:
80+
# OpenQASM 2.0 limitation...
81+
assert gate.name != 'measure' and not bits_sub
82+
apply_op(eng, gate_sub, [
83+
gate_args[qubit.register.name][qubit.index]
84+
for qubit in quregs_sub
85+
], [], bits_map)
86+
else:
87+
if gate.params:
88+
gate_projectq = gates_conv_table[gate.name](*gate.params)
89+
else:
90+
gate_projectq = gates_conv_table[gate.name]
91+
92+
if gate.condition:
93+
# OpenQASM 2.0
94+
cbit, value = gate.condition
95+
96+
# Note: apparently, it is illegal to write if (c0[0] == 1)
97+
# so we always assume that index == 0...
98+
if bits_map[cbit.name][0] == value:
99+
apply_gate(gate_projectq, qubits)
100+
else:
101+
apply_gate(gate_projectq, qubits)
102+
103+
104+
def _convert_qiskit_circuit(eng, qc):
105+
"""
106+
Convert a QisKit circuit and convert it to ProjectQ commands.
107+
108+
This function supports OpenQASM 2.0 (3.0 is experimental)
109+
110+
Args:
111+
eng (MainEngine): MainEngine to use for creating qubits and commands.
112+
qc (qiskit.QuantumCircuit): Quantum circuit to process
113+
114+
Note:
115+
At this time, we support most of OpenQASM 2.0 and some of 3.0,
116+
although the latter is still experimental.
117+
"""
118+
# Create maps between qiskit and ProjectQ for qubits and bits
119+
qubits_map = {
120+
qureg.name: eng.allocate_qureg(qureg.size)
121+
for qureg in qc.qregs
122+
}
123+
bits_map = {bit.name: [None] * bit.size for bit in qc.cregs}
124+
125+
# Convert all the gates to ProjectQ commands
126+
for gate, quregs, bits in qc.data:
127+
apply_op(
128+
eng, gate,
129+
[qubits_map[qubit.register.name][qubit.index]
130+
for qubit in quregs], bits, bits_map)
131+
132+
return qubits_map, bits_map
133+
134+
def read_qasm_str(eng, qasm_str):
135+
"""
136+
Read an OpenQASM (2.0, 3.0 is experimental) string and convert it to
137+
ProjectQ commands.
138+
139+
This version of the function uses Qiskit in order to parse the *.qasm
140+
file.
141+
142+
Args:
143+
eng (MainEngine): MainEngine to use for creating qubits and commands.
144+
filename (string): Path to *.qasm file
145+
146+
Note:
147+
At this time, we support most of OpenQASM 2.0 and some of 3.0,
148+
although the latter is still experimental.
149+
"""
150+
qc = QuantumCircuit.from_qasm_str(qasm_str)
151+
return _convert_qiskit_circuit(eng, qc)
152+
153+
154+
def read_qasm_file(eng, filename):
155+
"""
156+
Read an OpenQASM (2.0, 3.0 is experimental) file and convert it to
157+
ProjectQ commands.
158+
159+
This version of the function uses Qiskit in order to parse the *.qasm
160+
file.
161+
162+
Args:
163+
eng (MainEngine): MainEngine to use for creating qubits and commands.
164+
filename (string): Path to *.qasm file
165+
166+
Note:
167+
At this time, we support most of OpenQASM 2.0 and some of 3.0,
168+
although the latter is still experimental.
169+
"""
170+
171+
qc = QuantumCircuit.from_qasm_file(filename)
172+
173+
return _convert_qiskit_circuit(eng, qc)

0 commit comments

Comments
 (0)