-
Notifications
You must be signed in to change notification settings - Fork 575
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[TAPE] Adds circuit drawing functionality to the quantum tape #824
Changes from all commits
e714e54
27c2354
b50a412
2ae8ea6
8722350
b080cbf
2dd98e5
9b71cd6
0f81545
d007001
236fea7
e1e5e40
1020dd3
b66e3e5
ebe9c27
a03ca1e
9623b09
660ffc9
4ab202d
a02331e
0179d9f
fa3d988
ffcc32f
1cd9fdd
b0bf502
067e4bb
74c98db
813148a
7f3d0f1
ae92e4a
fb17b10
6b87f5c
b1eb83d
c6c77bd
ffef111
45ede30
bb19718
fc8353b
a35daaa
a249a8c
a7c0d8b
9e00ac1
56fda5b
ae0d61e
bb7df64
038ce3f
5aa79fd
4e2280f
f111ff0
1ae072f
ca91993
2e4cca9
cf0cf55
2a7d18c
2fa257e
def4fa6
4fec39e
e72f9c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ | |
""" | ||
import networkx as nx | ||
|
||
import pennylane as qml | ||
from pennylane import CircuitGraph | ||
|
||
|
||
|
@@ -29,6 +30,12 @@ class TapeCircuitGraph(CircuitGraph): | |
def __init__(self, ops, obs, wires): | ||
self._operations = ops | ||
self._observables = obs | ||
Comment on lines
31
to
32
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. They are, but I didn't want to alter the signature while still inheriting from the old circuit graph! Something to keep in mind when we decide to drop backcompatibility |
||
|
||
for m in self._observables: | ||
if m.return_type is qml.operation.State: | ||
# state measurements are applied to all device wires | ||
m._wires = wires # pylint: disable=protected-access | ||
Comment on lines
+34
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this have something to do with allowing state measurements to be applied without declaring on which wires? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep! exactly |
||
|
||
super().__init__(ops + obs, variable_deps={}, wires=wires) | ||
|
||
@property | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -459,6 +459,51 @@ def __call__(self, *args, **kwargs): | |
|
||
return __import__(res_type_namespace).squeeze(res) | ||
|
||
def draw(self, charset="unicode"): | ||
"""Draw the quantum tape as a circuit diagram. | ||
|
||
Consider the following circuit as an example: | ||
|
||
.. code-block:: python3 | ||
|
||
@qml.qnode(dev) | ||
def circuit(a, w): | ||
qml.Hadamard(0) | ||
qml.CRX(a, wires=[0, 1]) | ||
qml.Rot(*w, wires=[1]) | ||
qml.CRX(-a, wires=[0, 1]) | ||
return qml.expval(qml.PauliZ(0) @ qml.PauliZ(1)) | ||
|
||
We can draw the QNode after execution: | ||
|
||
>>> result = circuit(2.3, [1.2, 3.2, 0.7]) | ||
>>> print(circuit.draw()) | ||
0: ──H──╭C────────────────────────────╭C─────────╭┤ ⟨Z ⊗ Z⟩ | ||
1: ─────╰RX(2.3)──Rot(1.2, 3.2, 0.7)──╰RX(-2.3)──╰┤ ⟨Z ⊗ Z⟩ | ||
>>> print(circuit.draw(charset="ascii")) | ||
0: --H--+C----------------------------+C---------+| <Z @ Z> | ||
1: -----+RX(2.3)--Rot(1.2, 3.2, 0.7)--+RX(-2.3)--+| <Z @ Z> | ||
|
||
Args: | ||
charset (str, optional): The charset that should be used. Currently, "unicode" and | ||
"ascii" are supported. | ||
|
||
Raises: | ||
ValueError: if the given charset is not supported | ||
.QuantumFunctionError: drawing is impossible because the underlying | ||
quantum tape has not yet been constructed | ||
|
||
Returns: | ||
str: the circuit representation of the tape | ||
|
||
""" | ||
if self.qtape is None: | ||
raise qml.QuantumFunctionError( | ||
"The QNode can only be drawn after its quantum tape has been constructed." | ||
) | ||
|
||
return self.qtape.draw(charset=charset) | ||
|
||
def to_tf(self, dtype=None): | ||
"""Apply the TensorFlow interface to the internal quantum tape. | ||
|
||
|
@@ -467,7 +512,7 @@ def to_tf(self, dtype=None): | |
output. If not provided, the default is ``tf.float64``. | ||
|
||
Raises: | ||
qml.QuantumFunctionError: if TensorFlow >= 2.1 is not installed | ||
.QuantumFunctionError: if TensorFlow >= 2.1 is not installed | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this usually how it's written? 🤔 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this was originally written by another contributor (and was copied from the old core), but I quite like how it avoids the redundancy of saying "raises a QuantumFunctionError if TensorFlow >= 2.1 is not installed". |
||
""" | ||
# pylint: disable=import-outside-toplevel | ||
try: | ||
|
@@ -484,11 +529,11 @@ def to_tf(self, dtype=None): | |
if self.qtape is not None: | ||
TFInterface.apply(self.qtape, dtype=tf.as_dtype(self.dtype)) | ||
|
||
except ImportError: | ||
except ImportError as e: | ||
raise qml.QuantumFunctionError( | ||
"TensorFlow not found. Please install the latest " | ||
"version of TensorFlow to enable the 'tf' interface." | ||
) | ||
) from e | ||
|
||
def to_torch(self, dtype=None): | ||
"""Apply the Torch interface to the internal quantum tape. | ||
|
@@ -498,7 +543,7 @@ def to_torch(self, dtype=None): | |
output. If not provided, the default is ``torch.float64``. | ||
|
||
Raises: | ||
qml.QuantumFunctionError: if PyTorch >= 1.3 is not installed | ||
.QuantumFunctionError: if PyTorch >= 1.3 is not installed | ||
""" | ||
# pylint: disable=import-outside-toplevel | ||
try: | ||
|
@@ -518,11 +563,11 @@ def to_torch(self, dtype=None): | |
if self.qtape is not None: | ||
TorchInterface.apply(self.qtape, dtype=self.dtype) | ||
|
||
except ImportError: | ||
except ImportError as e: | ||
raise qml.QuantumFunctionError( | ||
"PyTorch not found. Please install the latest " | ||
"version of PyTorch to enable the 'torch' interface." | ||
) | ||
) from e | ||
|
||
def to_autograd(self): | ||
"""Apply the Autograd interface to the internal quantum tape.""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is required since tape parameters can now be TF tensors or Torch tensors in addition to floats/NumPy arrays. Unfortunately
round()
doesn't work with Torch tensors, so we instead use string formatting.The multiplication by
1.0
is to convert a TF Variable into a TF tensor without importing tensorflow. This is because TensorFlow Variables do not support string formatting for some reason 😢