Skip to content

Commit 47c7039

Browse files
authored
fixed the compatibility of qiskit and sympy, and updated to able to calculate a controlled unitary operator (#26)
* fix sympy compatibility in tests (#25) * fix qiskit version incompatibility (#23) * updated to adapt some files to able to calculate a controlled unitary operator.
1 parent 7ca67d6 commit 47c7039

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+418
-108
lines changed

quantpy/sympy/__init__.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
# determine which names are imported when
1414
# "from quantpy.sympy import *" is done.
1515

16-
#from .qapply import __all__ as qap_all
17-
#from .qapply import *
18-
#__all__.extend(qap_all)
16+
from .qapply import __all__ as qap_all
17+
from .qapply import *
18+
__all__.extend(qap_all)
1919

20-
#from . import _quantumexecutor
21-
#from ._quantumexecutor import *
22-
#__all__.extend(_quantumexecutor.__all__)
20+
from .gate_extension import __all__ as gate_ext_all
21+
from .gate_extension import *
22+
__all__.extend(gate_ext_all)

quantpy/sympy/executor/_base_quantum_executor.py

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
from abc import abstractmethod
55

66
import sympy
7+
import qiskit
78
from qiskit.qasm._qasmparser import QasmParser
9+
import quantpy.sympy.gate_extension
810

911
class BaseQuantumExecutor:
1012
"""BaseQuantumExecutor Class
@@ -21,13 +23,17 @@ def execute(self, circuit, **options):
2123
"""
2224
return None
2325

24-
def to_qasm(self, sympy_expr):
26+
def to_qasm(self, sympy_expr, **options):
2527
"""QuantumExecutor classes' commom method.
2628
Transform SymPy expression to OpenQASM format descriptions.
2729
2830
@return qasm format string.
2931
"""
30-
qasm = 'OPENQASM 2.0;\ninclude "qelib1.inc";\n'
32+
with_measure = options.get('with_measure', False)
33+
includes = options.get('includes', ['qelib1.inc'])
34+
qasm = 'OPENQASM 2.0;\n'
35+
for f in includes:
36+
qasm += 'include "{0}";\n'.format(f)
3137
assert isinstance(sympy_expr, sympy.mul.Mul), 'Sorry. Now, supported U*U*U*Qubit format'
3238
qubit = sympy_expr.args[-1]
3339
assert isinstance(qubit, sympy.physics.quantum.qubit.Qubit), 'Sorry. Now, supported U*U*U*Qubit format'
@@ -56,8 +62,48 @@ def to_qasm(self, sympy_expr):
5662
qasm += 'cx qr[{}], qr[{}];\n'.format(int(gate.args[0]), int(gate.args[1]))
5763
qasm += 'cx qr[{}], qr[{}];\n'.format(int(gate.args[1]), int(gate.args[0]))
5864
qasm += 'cx qr[{}], qr[{}];\n'.format(int(gate.args[0]), int(gate.args[1]))
65+
elif isinstance(gate, sympy.physics.quantum.gate.CGate):
66+
if isinstance(gate.args[1], sympy.physics.quantum.gate.IdentityGate):
67+
continue
68+
t = gate.args[1].args[0]
69+
if isinstance(gate.args[1], sympy.physics.quantum.gate.XGate):
70+
qasm += 'cx qr[{}], qr[{}];\n'.format(tuple(gate.args[0])[0], t)
71+
elif isinstance(gate.args[1], sympy.physics.quantum.gate.YGate):
72+
qasm += 'sdg qr[{}];\n'.format(t)
73+
qasm += 'cx qr[{}], qr[{}];\n'.format(tuple(gate.args[0])[0], t)
74+
qasm += 's qr[{}];\n'.format(t)
75+
elif isinstance(gate.args[1], sympy.physics.quantum.gate.ZGate):
76+
qasm += 'h qr[{}];\n'.format(t)
77+
qasm += 'cx qr[{}], qr[{}];\n'.format(tuple(gate.args[0])[0], t)
78+
qasm += 'h qr[{}];\n'.format(t)
79+
elif isinstance(gate, sympy.physics.quantum.gate.HadamardGate):
80+
qasm += 'sdg qr[{}];\n'.format(t)
81+
qasm += 'h qr[{}];\n'.format(t)
82+
qasm += 'tdg qr[{}];\n'.format(t)
83+
qasm += 'cx qr[{}], qr[{}];\n'.format(tuple(gate.args[0])[0], t)
84+
qasm += 't qr[{}];\n'.format(t)
85+
qasm += 'h qr[{}];\n'.format(t)
86+
qasm += 's qr[{}];\n'.format(t)
87+
elif isinstance(gate.args[1], sympy.physics.quantum.gate.PhaseGate):
88+
c = tuple(gate.args[0])[0]
89+
qasm += 't qr[{}];\n'.format(t)
90+
qasm += 'cx qr[{}], qr[{}];\n'.format(c, t)
91+
qasm += 'tdg qr[{}];\n'.format(t)
92+
qasm += 'cx qr[{}], qr[{}];\n'.format(c, t)
93+
qasm += 't qr[{}];\n'.format(c)
94+
elif isinstance(gate.args[1], sympy.physics.quantum.gate.TGate):
95+
c = tuple(gate.args[0])[0]
96+
qasm += 'cu1(pi/4) qr[{}], qr[{}];\n'.format(c, t)
97+
elif isinstance(gate.args[1], quantpy.sympy.Rk):
98+
c = tuple(gate.args[0])[0]
99+
r = gate.args[1]
100+
k = r.k
101+
qasm += 'cu1(pi/{}) qr[{}], qr[{}];\n'.format(k, c, t)
102+
else:
103+
assert False, '{} it is not a gate operator, nor is a supported operator'.format(repr(gate))
59104
else:
60105
assert False, '{} it is not a gate operator, nor is a supported operator'.format(repr(gate))
61-
for i in range(len(qubit)):
62-
qasm += 'measure qr[{0}] -> cr[{0}];\n'.format(i)
106+
if with_measure:
107+
for i in range(len(qubit)):
108+
qasm += 'measure qr[{0}] -> cr[{0}];\n'.format(i)
63109
return qasm

quantpy/sympy/executor/classical_simulation_executor.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22
"""definition of ClassicalSimulationExecutor class
33
"""
44

5-
import qiskit
65
import numpy as np
6+
from qiskit import transpiler
7+
from qiskit.backends.local import QasmSimulatorPy
8+
from qiskit.wrapper._circuittoolkit import circuit_from_qasm_string
79

8-
import qiskit._openquantumcompiler as openquantumcompiler
910
from quantpy.sympy.executor._base_quantum_executor import BaseQuantumExecutor
1011
from quantpy.sympy.executor.simulator.numpy_simulator import NumpySimulator
1112

13+
1214
class ClassicalSimulationExecutor(BaseQuantumExecutor):
1315

1416
def __init__(self):
1517
super().__init__()
1618
self.simulator = None
1719

20+
1821
def execute(self, circuit, **options):
1922
"""
2023
Execute sympy-circuit with classical simulator
@@ -25,9 +28,8 @@ def execute(self, circuit, **options):
2528
self.simulator = NumpySimulator()
2629
basis_gates_str = (",".join(self.simulator.basis_gates)).lower()
2730
# the following one-line compilation ignores basis_gates, and returnes "u2" for "h".
28-
#json = openquantumcompiler.compile(qasm,basis_gates=basis_gates_str,format="json")
29-
circuit_dag = openquantumcompiler.compile(qasm,basis_gates=basis_gates_str)
30-
json = openquantumcompiler.dag2json(circuit_dag,basis_gates=basis_gates_str)
31+
quantum_circuit = circuit_from_qasm_string(qasm)
32+
json = transpiler.compile(quantum_circuit, basis_gates=basis_gates_str, backend=QasmSimulatorPy())["circuits"][0]["compiled_circuit"]
3133
self.simulate(json)
3234
return str(self.simulator)
3335

quantpy/sympy/executor/ibmq_executor.py

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,23 @@ def __init__(self, **options):
4444

4545
def execute(self, circuit, **options):
4646
"""
47+
The following options are valid:
48+
49+
* ``with_measure``: qapply with measure flag
50+
(default: True).
4751
"""
48-
qasm = self.to_qasm(circuit)
52+
with_measure = options.get('with_measure', True)
53+
qasm = self.to_qasm(circuit, with_measure = with_measure)
4954
name = self.qp.load_qasm_text(qasm)
50-
qobj = self.qp.compile(name, backend=self.backend, shots=self.shots)
51-
cnt = self.qp.run(qobj).get_counts(name)
52-
self.qp.destroy_circuit(name)
53-
for reg in list(self.qp.get_quantum_register_names()):
54-
self.qp.destroy_quantum_register(reg)
55-
for reg in list(self.qp.get_classical_register_names()):
56-
self.qp.destroy_classical_register(reg)
57-
return cnt
55+
try:
56+
qobj = self.qp.compile(name, backend=self.backend, shots=self.shots)
57+
cnt = self.qp.run(qobj).get_counts(name)
58+
self.qp.destroy_circuit(name)
59+
for reg in list(self.qp.get_quantum_register_names()):
60+
self.qp.destroy_quantum_register(reg)
61+
for reg in list(self.qp.get_classical_register_names()):
62+
self.qp.destroy_classical_register(reg)
63+
return cnt
64+
except qiskit._resulterror.ResultError as ex:
65+
print("error:", ex.args)
66+
return

quantpy/sympy/executor/sympy_executor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"""definition of SymPyExdecutor class
33
"""
44
from sympy.physics.quantum.qapply import qapply as sympy_qapply
5-
from quantpy.sympy.executor._base_quantum_executor import BaseQuantumExecutor
5+
from ._base_quantum_executor import BaseQuantumExecutor
66

77
class SymPyExecutor(BaseQuantumExecutor):
88
"""SymPyExecutor Class for transparently executing sympy's qapply.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# -*- coding:utf-8 -*-
2+
3+
import textwrap
4+
from sympy.physics.quantum.gate import H, X, Y, Z, CNOT, SWAP, CPHASE, CGate, CGateS
5+
from sympy.physics.quantum.gate import IdentityGate, UGate
6+
from sympy.physics.quantum.qubit import Qubit, QubitBra
7+
from quantpy.sympy.executor._base_quantum_executor import BaseQuantumExecutor
8+
9+
def test_new_instanse():
10+
executor = BaseQuantumExecutor()
11+
assert executor != None
12+
13+
def test_execute():
14+
executor = BaseQuantumExecutor()
15+
c = H(0)*Qubit('0')
16+
assert executor.execute(c) == None
17+
18+
def test_to_qasm_with_no_options():
19+
executor = BaseQuantumExecutor()
20+
c = H(0)*Qubit('0')
21+
qasm = executor.to_qasm(c)
22+
assert qasm.strip() == textwrap.dedent('''
23+
OPENQASM 2.0;
24+
include "qelib1.inc";
25+
qreg qr[1];
26+
creg cr[1];
27+
h qr[0];
28+
''').strip()
29+
30+
def test_to_qasm_with_includes():
31+
executor = BaseQuantumExecutor()
32+
c = H(0)*Qubit('0')
33+
qasm = executor.to_qasm(c,includes=['qelib2.inc','qelib3.inc'])
34+
assert qasm.strip() == textwrap.dedent('''
35+
OPENQASM 2.0;
36+
include "qelib2.inc";
37+
include "qelib3.inc";
38+
qreg qr[1];
39+
creg cr[1];
40+
h qr[0];
41+
''').strip()

quantpy/sympy/expr_extension.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,26 @@ def _expr_rshift_as_multiplication_of_reverse_order(lhs, rhs):
77
"""
88
return rhs * lhs
99

10-
1110
def sympy_expr_add_operators():
11+
"""oprators of the Expr instanse will be overrided by Local functions.
12+
"""
13+
sympy_expr_add_rshift()
14+
15+
def sympy_expr_remove_rshift():
16+
"""remove __rshift__ attribute of the Expr instanse
17+
"""
18+
if hasattr(Expr, '__rshift__'):
19+
del Expr.__rshift__
20+
21+
def sympy_expr_add_rshift():
1222
"""__rshift__ of the Expr instanse will be overrided by Local function expr_rshift_as_multiplication_of_reverse_order.
1323
"""
1424
Expr.__rshift__ = _expr_rshift_as_multiplication_of_reverse_order
25+
26+
def sympy_expr_toggle_rshift():
27+
"""toggle __rshift__ attribute of the Expr instanse.
28+
"""
29+
if hasattr(Expr, '__rshift__'):
30+
sympy_expr_remove_rshift()
31+
else:
32+
sympy_expr_add_rshift()

quantpy/sympy/gate_extension.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
"""Gate Extension
2+
"""
3+
4+
from sympy.core.compatibility import is_sequence
5+
from sympy import cos, exp, expand, I, Matrix, pi, S, sin, sqrt, Sum, symbols
6+
from sympy.external import import_module
7+
from sympy.physics.quantum.qexpr import QuantumError, QExpr
8+
from sympy.physics.quantum.gate import Gate, UGate, OneQubitGate
9+
from sympy.physics.quantum.gate import _validate_targets_controls
10+
from sympy.physics.quantum.qft import RkGate, Rk
11+
# from sympy.physics.quantum.circuitplot import Mz, Mx
12+
13+
from sympy import Expr, Matrix, exp, I, pi, Integer, Symbol
14+
from sympy.functions import sqrt
15+
16+
__all__ = [
17+
'Mz',
18+
'Mx',
19+
'Rx',
20+
'Ry',
21+
'Rz',
22+
'RkGate',
23+
'Rk',
24+
]
25+
26+
class Rx(UGate):
27+
"""Rx(theta) gate.
28+
= Exp{-i*theta*XGate/2}
29+
"""
30+
gate_name='Rx'
31+
gate_name_latex=u'Rx'
32+
33+
#-------------------------------------------------------------------------
34+
# Initialization
35+
#-------------------------------------------------------------------------
36+
37+
@classmethod
38+
def _eval_args(cls, args):
39+
targets = args[0]
40+
theta = args[1]
41+
mat = Matrix([[cos(theta/2), -I*sin(theta/2)], [-I*sin(theta/2), cos(theta/2)]])
42+
return UGate._eval_args([targets, mat])
43+
44+
class Ry(UGate):
45+
"""Ry(theta) gate.
46+
= Exp{-i*theta*ZGate/2}
47+
"""
48+
gate_name='Ry'
49+
gate_name_latex=u'Ry'
50+
51+
#-------------------------------------------------------------------------
52+
# Initialization
53+
#-------------------------------------------------------------------------
54+
55+
@classmethod
56+
def _eval_args(cls, args):
57+
targets = args[0]
58+
theta = args[1]
59+
mat = Matrix([[cos(theta/2), -sin(theta/2)], [sin(theta/2), cos(theta/2)]])
60+
return UGate._eval_args([targets, mat])
61+
62+
class Rz(UGate):
63+
"""Rz(theta) gate.
64+
= Exp{-i*theta*ZGate/2}
65+
"""
66+
gate_name='Rz'
67+
gate_name_latex=u'Rz'
68+
69+
#-------------------------------------------------------------------------
70+
# Initialization
71+
#-------------------------------------------------------------------------
72+
73+
@classmethod
74+
def _eval_args(cls, args):
75+
targets = args[0]
76+
theta = args[1]
77+
mat = Matrix([[exp(-I*theta/2), 0], [0, exp(I*theta/2)]])
78+
return UGate._eval_args([targets, mat])
79+
80+
class Mz(OneQubitGate):
81+
"""Mock-up of a z measurement gate.
82+
83+
This is in circuitplot rather than gate.py because it's not a real
84+
gate, it just draws one.
85+
"""
86+
measurement = True
87+
gate_name='Mz'
88+
gate_name_latex=u'M_z'
89+
90+
def __new__(cls, *args):
91+
args = cls._eval_args(args)
92+
inst = Expr.__new__(cls, *args)
93+
inst.hilbert_space = cls._eval_hilbert_space(args)
94+
return inst
95+
96+
@classmethod
97+
def _eval_args(cls, args):
98+
# Fall back to this, because Gate._eval_args assumes that args is
99+
# all targets and can't contain duplicates.
100+
return QExpr._eval_args(args)
101+
102+
@property
103+
def gate_name_plot(self):
104+
return self.gate_name_latex
105+
106+
class Mx(OneQubitGate):
107+
"""Mock-up of an x measurement gate.
108+
109+
This is in circuitplot rather than gate.py because it's not a real
110+
gate, it just draws one.
111+
"""
112+
measurement = True
113+
gate_name='Mx'
114+
gate_name_latex=u'M_x'
115+
116+
def __new__(cls, *args):
117+
args = cls._eval_args(args)
118+
inst = Expr.__new__(cls, *args)
119+
inst.hilbert_space = cls._eval_hilbert_space(args)
120+
return inst
121+
122+
@classmethod
123+
def _eval_args(cls, args):
124+
# Fall back to this, because Gate._eval_args assumes that args is
125+
# all targets and can't contain duplicates.
126+
return QExpr._eval_args(args)
127+
128+
@property
129+
def gate_name_plot(self):
130+
return self.gate_name_latex
131+

quantpy/sympy/qapply.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
"""Logic for applying operators to states.
33
"""
44

5+
__all__ = ['qapply']
6+
57
from quantpy.sympy.executor.sympy_executor import SymPyExecutor
68

79
def qapply(circuit, **options):

quantpy/sympy/tests/sympy/physics/quantum/test_anticommutator.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
from sympy.physics.quantum.anticommutator import AntiCommutator as AComm
55
from sympy.physics.quantum.operator import Operator
66

7-
from quantpy.sympy.qapply import qapply
87

98
a, b, c = symbols('a,b,c')
109
A, B, C, D = symbols('A,B,C,D', commutative=False)

0 commit comments

Comments
 (0)