Skip to content

Commit 4510995

Browse files
Pennylane-Qualtran interoperability (#1559)
* bloq as op * basic gates (most of them) * identity gate * rotation gates * address comments * some more gates * fix typing * change imports around * import paths change * undo * import paths change * import paths change * tests * formatting * fix mypy, pylint, etc * extra space * fix * fix typo in the documentation of CModMulK (#1594) * trigger ci * fix back * fix * add tests for matrix comparisons --------- Co-authored-by: Noureldin <noureldinyosri@google.com>
1 parent 4517c28 commit 4510995

33 files changed

Lines changed: 363 additions & 2 deletions

dev_tools/conf/mypy.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ follow_imports = silent
2121
ignore_missing_imports = true
2222

2323
# Non-Google
24-
[mypy-sympy.*,matplotlib.*,proto.*,pandas.*,scipy.*,freezegun.*,mpl_toolkits.*,networkx.*,ply.*,astroid.*,pytest.*,_pytest.*,pylint.*,setuptools.*,qiskit.*,quimb.*,pylatex.*,filelock.*,sortedcontainers.*,tqdm.*,plotly.*,dash.*,tensorflow_docs.*,fxpmath.*,ipywidgets.*,cachetools.*,pydot.*,nbformat.*,nbconvert.*,openfermion.*]
24+
[mypy-sympy.*,matplotlib.*,proto.*,pandas.*,scipy.*,freezegun.*,mpl_toolkits.*,networkx.*,ply.*,astroid.*,pytest.*,_pytest.*,pylint.*,setuptools.*,qiskit.*,quimb.*,pylatex.*,filelock.*,sortedcontainers.*,tqdm.*,plotly.*,dash.*,tensorflow_docs.*,fxpmath.*,ipywidgets.*,cachetools.*,pydot.*,nbformat.*,nbconvert.*,openfermion.*,pennylane.*]
2525
follow_imports = silent
2626
ignore_missing_imports = true
2727

qualtran/_infra/bloq.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@
2121
if TYPE_CHECKING:
2222
import cirq
2323
import networkx as nx
24+
import pennylane as qml
2425
import quimb.tensor as qtn
2526
import sympy
2627
from numpy.typing import NDArray
28+
from pennylane.operation import Operation
29+
from pennylane.wires import Wires
2730

2831
from qualtran import (
2932
AddControlledT,
@@ -466,6 +469,25 @@ def as_cirq_op(
466469
bloq=self, cirq_quregs=cirq_quregs, qubit_manager=qubit_manager
467470
)
468471

472+
def as_pl_op(self, wires: 'Wires') -> 'Operation':
473+
"""Override this method to support conversion to a PennyLane operation.
474+
475+
If this method is not overriden, the default implementation will wrap this bloq
476+
in a `FromBloq` shim.
477+
478+
Args:
479+
wires: the wires that the op acts on
480+
481+
Returns:
482+
~.Operation: A PennyLane operation corresponding to this bloq acting on the
483+
provided wires or None. This method should return None if and only if the bloq
484+
instance truly should not be included in the PennyLane circuit (e.g. for reshaping
485+
bloqs). A bloq with no PennyLane equivalent should raise an exception instead.
486+
"""
487+
from pennylane.io import FromBloq
488+
489+
return FromBloq(bloq=self, wires=wires)
490+
469491
def on(self, *qubits: 'cirq.Qid') -> 'cirq.Operation':
470492
"""A `cirq.Operation` of this bloq operating on the given qubits.
471493

qualtran/_infra/bloq_test.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ def test_bloq():
3434
tb.decompose_bloq()
3535

3636

37+
def test_as_pl_op():
38+
import pennylane as qml
39+
40+
tb = TestTwoBitOp()
41+
42+
assert tb.as_pl_op(wires=[0, 1]) == qml.FromBloq(TestTwoBitOp(), wires=[0, 1])
43+
44+
3745
def test_as_composite_bloq():
3846
tb = TestAtom()
3947
with pytest.raises(DecomposeTypeError):

qualtran/bloqs/basic_gates/cnot.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
if TYPE_CHECKING:
3939
import cirq
4040
import quimb.tensor as qtn
41+
from pennylane.operation import Operation
42+
from pennylane.wires import Wires
4143

4244
from qualtran.cirq_interop import CirqQuregT
4345
from qualtran.simulation.classical_sim import ClassicalValT
@@ -122,6 +124,11 @@ def as_cirq_op(
122124
(target,) = target
123125
return cirq.CNOT(ctrl, target), {'ctrl': np.array([ctrl]), 'target': np.array([target])}
124126

127+
def as_pl_op(self, wires: 'Wires') -> 'Operation':
128+
import pennylane as qml
129+
130+
return qml.CNOT(wires=wires)
131+
125132
def wire_symbol(self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()) -> 'WireSymbol':
126133
if reg is None:
127134
return Text('')

qualtran/bloqs/basic_gates/cnot_test.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import cirq
1616
import numpy as np
17+
import pennylane as qml
1718
import pytest
1819

1920
from qualtran import BloqBuilder, Signature
@@ -39,6 +40,19 @@ def test_cnot_tensor():
3940
np.testing.assert_allclose(should_be, matrix)
4041

4142

43+
def test_cnot_vs_pl():
44+
bloq = CNOT()
45+
matrix = qml.FromBloq(bloq, wires=[0, 1]).matrix()
46+
# fmt: off
47+
should_be = np.array([
48+
[1, 0, 0, 0],
49+
[0, 1, 0, 0],
50+
[0, 0, 0, 1],
51+
[0, 0, 1, 0]])
52+
# fmt: on
53+
np.testing.assert_allclose(should_be, matrix)
54+
55+
4256
def test_cnot_cbloq_tensor_vs_cirq():
4357
bb, soqs = BloqBuilder.from_signature(Signature.build(c=1, t=1))
4458
c, t = bb.add(CNOT(), ctrl=soqs['c'], target=soqs['t'])

qualtran/bloqs/basic_gates/global_phase.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535

3636
if TYPE_CHECKING:
3737
import quimb.tensor as qtn
38+
from pennylane.operation import Operation
39+
from pennylane.wires import Wires
3840

3941

4042
@frozen(kw_only=True)
@@ -78,6 +80,12 @@ def from_coefficient(
7880
def cirq_gate(self) -> cirq.Gate:
7981
return cirq.GlobalPhaseGate(self.coefficient)
8082

83+
def as_pl_op(self, wires: 'Wires') -> 'Operation':
84+
import numpy as np
85+
import pennylane as qml
86+
87+
return qml.GlobalPhase(phi=self.exponent * np.pi, wires=wires)
88+
8189
def decompose_bloq(self) -> 'CompositeBloq':
8290
raise DecomposeTypeError(f"{self} is atomic")
8391

qualtran/bloqs/basic_gates/global_phase_test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import cirq
1616
import numpy as np
17+
import pennylane as qml
1718
import pytest
1819

1920
from qualtran import CtrlSpec
@@ -54,6 +55,22 @@ def test_cirq_interop():
5455
assert cirq_gate_to_bloq(gate) == bloq
5556

5657

58+
def test_pl_interop():
59+
bloq = GlobalPhase(exponent=0.5)
60+
pl_op_from_bloq = bloq.as_pl_op(wires=[0])
61+
pl_op = qml.GlobalPhase(phi=0.5 * np.pi, wires=[0])
62+
assert pl_op_from_bloq == pl_op
63+
64+
matrix = pl_op_from_bloq.matrix()
65+
# fmt: off
66+
should_be = np.array([
67+
[6.123234e-17-1.j, 0.000000e+00+0.j],
68+
[0.000000e+00+0.j, 6.123234e-17-1.j]
69+
])
70+
# fmt: on
71+
np.testing.assert_allclose(should_be, matrix)
72+
73+
5774
def test_t_complexity():
5875
assert GlobalPhase(exponent=0.5).t_complexity() == TComplexity()
5976

qualtran/bloqs/basic_gates/hadamard.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
if TYPE_CHECKING:
3838
import cirq
3939
import quimb.tensor as qtn
40+
from pennylane.operation import Operation
41+
from pennylane.wires import Wires
4042

4143
from qualtran.cirq_interop import CirqQuregT
4244
from qualtran.resource_counting import CostKey
@@ -105,6 +107,11 @@ def as_cirq_op(
105107
(q,) = q
106108
return cirq.H(q), {'q': np.array([q])}
107109

110+
def as_pl_op(self, wires: 'Wires') -> 'Operation':
111+
import pennylane as qml
112+
113+
return qml.Hadamard(wires=wires)
114+
108115
def wire_symbol(self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()) -> 'WireSymbol':
109116
if reg is None:
110117
return Text('')

qualtran/bloqs/basic_gates/hadamard_test.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,19 @@ def test_cirq_interop():
7070
assert cirq_gate_to_bloq(op.gate) == CHadamard()
7171

7272

73+
def test_pl_interop():
74+
import pennylane as qml
75+
76+
bloq = Hadamard()
77+
pl_op_from_bloq = bloq.as_pl_op(wires=[0])
78+
pl_op = qml.Hadamard(wires=[0])
79+
assert pl_op_from_bloq == pl_op
80+
81+
matrix = pl_op.matrix()
82+
should_be = bloq.tensor_contract()
83+
np.testing.assert_allclose(should_be, matrix)
84+
85+
7386
def test_active_chadamard_is_hadamard():
7487
bb = BloqBuilder()
7588
q = bb.add_register('q', 1)

qualtran/bloqs/basic_gates/identity.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
if TYPE_CHECKING:
3939
import cirq
4040
import quimb.tensor as qtn
41+
from pennylane.operation import Operation
42+
from pennylane.wires import Wires
4143

4244
from qualtran.cirq_interop import CirqQuregT
4345
from qualtran.simulation.classical_sim import ClassicalValT
@@ -88,6 +90,11 @@ def as_cirq_op(
8890

8991
return cirq.IdentityGate(self.bitsize).on(*q), {'q': q}
9092

93+
def as_pl_op(self, wires: 'Wires') -> 'Operation':
94+
import pennylane as qml
95+
96+
return qml.Identity(wires=wires)
97+
9198
def wire_symbol(self, reg: Optional[Register], idx: Tuple[int, ...] = tuple()) -> 'WireSymbol':
9299
if reg is None:
93100
return Text('')

0 commit comments

Comments
 (0)