Skip to content
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

specs transform #1245

Merged
merged 40 commits into from
Jun 11, 2021
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
a3c5160
initial resource estimation
albi3ro Apr 21, 2021
60b61eb
initial resource estimation
albi3ro Apr 21, 2021
358eac4
black
albi3ro Apr 21, 2021
dc2de57
properties, ps num executions, qnode methods
albi3ro May 18, 2021
fe9e845
Merge branch 'master' into resource_estimation
albi3ro May 19, 2021
116b528
change I forgot to add
albi3ro May 19, 2021
f713ecd
Merge branch 'master' into resource_estimation
albi3ro Jun 1, 2021
535dfa6
specs transform
albi3ro Jun 4, 2021
7e51f07
docstrings
albi3ro Jun 4, 2021
ca8a56b
Merge branch 'master' into resource_estimation
albi3ro Jun 7, 2021
b1cba49
some progress
albi3ro Jun 8, 2021
0d473f1
tape specs tests, total_observables
albi3ro Jun 9, 2021
b11571f
Merge branch 'master' into resource_estimation
albi3ro Jun 9, 2021
e62c740
parametershift exeuctions on tape tested
albi3ro Jun 9, 2021
c410204
qnode tests
albi3ro Jun 10, 2021
b0b874a
transform tests
albi3ro Jun 10, 2021
cb5c7e1
Merge branch 'master' into resource_estimation
albi3ro Jun 10, 2021
18b5d1f
changelog
albi3ro Jun 10, 2021
5e2ee88
remove duplicated function
albi3ro Jun 10, 2021
572ab7f
black
albi3ro Jun 10, 2021
b0c8b20
fix test
albi3ro Jun 10, 2021
927622a
docstrings and test coverage
albi3ro Jun 10, 2021
76d70d5
maybe fixing docs?
albi3ro Jun 10, 2021
5d704a3
Merge branch 'master' into resource_estimation
albi3ro Jun 10, 2021
1e87cb8
black
albi3ro Jun 10, 2021
4d9e9c2
more docstring fixes
albi3ro Jun 10, 2021
c70766c
Merge branch 'resource_estimation' of https://github.com/PennyLaneAI/…
albi3ro Jun 10, 2021
2912d49
Apply suggestions from code review
albi3ro Jun 11, 2021
1c87df3
code review comments updated
albi3ro Jun 11, 2021
86196d2
test updates
albi3ro Jun 11, 2021
73cdc8b
Merge branch 'master' into resource_estimation
albi3ro Jun 11, 2021
c42aa14
updated tests, review comments
albi3ro Jun 11, 2021
6da7a2c
Merge branch 'resource_estimation' of https://github.com/PennyLaneAI/…
albi3ro Jun 11, 2021
6a4fbae
black and docstrings
albi3ro Jun 11, 2021
6e5637c
docstring
albi3ro Jun 11, 2021
82fde2b
black
albi3ro Jun 11, 2021
8e8bcbb
Merge branch 'master' into resource_estimation
albi3ro Jun 11, 2021
dce091e
Apply suggestions from code review
albi3ro Jun 11, 2021
6f19e42
total to num
albi3ro Jun 11, 2021
e6b6e92
total to num
albi3ro Jun 11, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,48 @@

<h3>New features since last release</h3>

* The `specs` QNode transform creates a function that produces the specifications for a circuit
at given arguments and keywords. The QNode property `specs` can also provide this information
after QNode execution. The tape property `specs` provides a subset of the same quantities.
The tape functions `get_resources` and `get_depth` are supereded by `specs` and will be
deprecated after one release cycle.
[(#1245)](https://github.com/PennyLaneAI/pennylane/pull/1245)

For example:

```python
dev = qml.device('default.qubit', wires=4)

@qml.qnode(dev, diff_method='parameter-shift')
def circuit(x, y):
qml.RX(x[0], wires=0)
qml.Toffoli(wires=(0, 1, 2))
qml.CRY(x[1], wires=(0, 1))
qml.Rot(x[2], x[3], y, wires=2)
trbromley marked this conversation as resolved.
Show resolved Hide resolved
return qml.expval(qml.PauliZ(0)), qml.expval(qml.PauliZ(1))
trbromley marked this conversation as resolved.
Show resolved Hide resolved

x = np.array([0.05, 0.1, 0.2, 0.3], requires_grad=True)
y = np.array(0.4, requires_grad=False)

specs_func = qml.specs(circuit)
info = specs_func(x, y)
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
```

```pycon
>>> info
{'by_size': defaultdict(int, {1: 2, 3: 1, 2: 1}),
Copy link
Contributor

@mariaschuld mariaschuld Jun 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would gate_sizes and gate_types not be much clearer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. I'm switching to that.

'by_name': defaultdict(int, {'RX': 1, 'Toffoli': 1, 'CRY': 1, 'Rot': 1}),
'total_operations': 4,
'total_observables': 2,
'num_tape_wires': 3,
Copy link
Contributor

@mariaschuld mariaschuld Jun 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not talk about tapes, since this is not user facing at all. Can we say num_used_wires then?

'depth': 3,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about "circuit_depth"?

'num_trainable_params': 4,
trbromley marked this conversation as resolved.
Show resolved Hide resolved
'num_parameter_shift_executions': 7,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this include the forward pass execution? E.g., if you set requires_grad=False for x, this number is 1.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, how is this number 7? Since we have 4 params shouldn't it be 9?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this make sense as num_device_executions?

'num_device_wires': 4,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: it'd be great to finally get rid of device wires for simulators, and only use as many wires as there are in the tape!

'device_name': 'default.qubit',
'diff_method': 'parameter-shift'}
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
```

albi3ro marked this conversation as resolved.
Show resolved Hide resolved
* The ``argnum`` keyword argument can now be specified for a QNode to define a
subset of trainable parameters used to estimate the Jacobian.
[(#1371)](https://github.com/PennyLaneAI/pennylane/pull/1371)
Expand Down
1 change: 1 addition & 0 deletions pennylane/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
ctrl,
measurement_grouping,
metric_tensor,
specs,
qfunc_transform,
single_tape_transform,
quantum_monte_carlo,
Expand Down
57 changes: 57 additions & 0 deletions pennylane/qnode.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,63 @@ def circuit():
charset=charset, wire_order=wire_order, show_all_wires=show_all_wires
)

@property
def specs(self):
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think it might be nicer to have a single-sentence summary here (e.g., same as what you have below), and then in the Returns: it can just be a word or two. Currently the docs renders the summary weirdly:
image

Returns:
A dictionary of information about qnode structure
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

**Example**

.. code-block:: python3

dev = qml.device('default.qubit', wires=2)
@qml.qnode(dev)
def circuit(x):
qml.RX(x[0], wires=0)
qml.RY(x[1], wires=1)
qml.CNOT(wires=(0,1))
return qml.probs(wires=(0,1))

x = np.array([0.1, 0.2])
res = circuit(x)

>>> circuit.specs
{'by_size': defaultdict(int, {1: 2, 2: 1}),
'by_name': defaultdict(int, {'RX': 1, 'RY': 1, 'CNOT': 1}),
'total_operations': 3,
'total_observables': 1,
'num_tape_wires': 2,
'depth': 2,
'num_device_wires': 2,
'device_name': 'default.qubit.autograd',
'diff_method': 'backprop'}

"""
if self.qtape is None:
raise qml.QuantumFunctionError(
"The QNode specifications can only be calculated after its quantum tape has been constructed."
)

info = self.qtape.specs.copy()

info["num_device_wires"] = self.device.num_wires
info["device_name"] = self.device.short_name

# TODO: use self.diff_method when that value gets updated
if self.diff_method != "best":
info["diff_method"] = self.diff_method
else:
info["diff_method"] = self.qtape.jacobian_options["method"]

# tape's do not accurately track parameters for backprop
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
# TODO: calculate number of trainable parameters in backprop
# find better syntax for determining if backprop
if info["diff_method"] == "backprop":
del info["num_trainable_params"]
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, probably answers my question above.


return info

def to_tf(self, dtype=None):
"""Apply the TensorFlow interface to the internal quantum tape.

Expand Down
19 changes: 19 additions & 0 deletions pennylane/tape/qubit_param_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,22 @@ def processing_fn(results):
return np.apply_along_axis(dot, 0, results)

return tapes, processing_fn

@property
def specs(self):
trbromley marked this conversation as resolved.
Show resolved Hide resolved

if "grad_method" not in self._par_info[0]:
self._update_gradient_info()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder why this is needed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for if someone asks for specs before actually having evaluated the gradient?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep.


# Initialize with the forward pass execution
num_executions = 1
# Loop over all variables
for _, info in self._par_info.items():

if info["grad_method"] == "A":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about finite diff and CV circuits? Maybe not too important for now, but might be worth considering later.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've thought about it a little and will probably try to add them in later.

num_executions += len(info["op"].get_parameter_shift(info["p_idx"]))

info = super().specs
info["num_parameter_shift_executions"] = num_executions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure I understand what is going on in this function. Maybe some more explicit comments could help?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any better?


return info
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realised specs doesn't work so well with circuits that include templates:

import pennylane as qml
from pennylane import numpy as np

x = np.array([0.1, 0.2])

with qml.tape.QubitParamShiftTape() as tape:
    qml.templates.StronglyEntanglingLayers(np.random.random((3, 2, 3)), wires=range(2))

tape.specs

Gives

{'by_size': defaultdict(int, {2: 1}),
 'by_name': defaultdict(int, {'StronglyEntanglingLayers': 1}),
 'total_operations': 1,
 'total_observables': 0,
 'num_tape_wires': 2,
 'depth': 1,
 'num_trainable_params': 1,
 'num_parameter_shift_executions': 1}

Though in practice, there are many trainable params and there will be many param shift executions 🤔.
There may not be an easy fix for this though. And also things work nicely on the QNode level as expansion will have happened.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this is something that needs fixing for this PR? Most users are not going to working with tapes, and there are multiple shortcomings to a tape with an unexpanded template.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, maybe something we can think about as we improve the Operation class. (I believe this subtlety is due to moving templates to operations, since they'd be automatically expanded before?)

83 changes: 71 additions & 12 deletions pennylane/tape/tape.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@
This module contains the base quantum tape.
"""
# pylint: disable=too-many-instance-attributes,protected-access,too-many-branches,too-many-public-methods
from collections import Counter, deque
from collections import Counter, deque, defaultdict
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
import contextlib
import copy
from threading import RLock
import warnings

import numpy as np

Expand Down Expand Up @@ -317,7 +318,7 @@ def __init__(self, name=None, do_queue=True):

self._trainable_params = set()
self._graph = None
self._resources = None
self._specs = None
self._depth = None
self._output_dim = 0

Expand Down Expand Up @@ -500,7 +501,7 @@ def _update_trainable_params(self):
def _update(self):
"""Update all internal tape metadata regarding processed operations and observables"""
self._graph = None
self._resources = None
self._specs = None
self._depth = None
self._update_circuit_info()
self._update_par_info()
Expand Down Expand Up @@ -1002,23 +1003,23 @@ def get_resources(self):

>>> tape.get_resources()
{'Hadamard': 2, 'RZ': 1, 'CNOT': 2, 'Rot': 1}

"""
if self._resources is None:
self._resources = {}

for op in self.operations:
if op.name not in self._resources.keys():
self._resources[op.name] = 1
else:
self._resources[op.name] += 1
warnings.warn(
"``tape.get_resources`` will be deprecated after v0.16 "
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to say when it will be removed?
E.g.
"tape.get_resources is now deprecated and will be removed in v0.17"

"Please use the more general ``tape.specs`` instead.",
UserWarning,
)

return self._resources
return self.specs["by_name"]

def get_depth(self):
"""Depth of the quantum circuit.

Returns:
int: Circuit depth, computed as the longest path in the circuit's directed acyclic graph representation.
int: Circuit depth, computed as the longest path in the
circuit's directed acyclic graph representation.

**Example**

Expand All @@ -1036,12 +1037,70 @@ def get_depth(self):

>>> tape.get_depth()
4

"""

warnings.warn(
"``tape.get_depth`` will be deprecated after v0.16 "
"Please use the more general ``tape.specs`` instead.",
UserWarning,
)

if self._depth is None:
self._depth = self.graph.get_depth()

return self._depth

@property
def specs(self):
"""Resource requirements of a quantum circuit.
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

Returns:
dict[str, Union[defaultdict,int]]: how many times constituent operations are applied
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

**Example**

.. code-block:: python3

with qml.tape.QuantumTape() as tape:
qml.Hadamard(wires=0)
qml.RZ(0.26, wires=1)
qml.CNOT(wires=[1, 0])
qml.Rot(1.8, -2.7, 0.2, wires=0)
qml.Hadamard(wires=1)
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0) @ qml.PauliZ(1))

Asking for the resources produces a dictionary as shown below:
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

>>> tape.specs['by_size']
defaultdict(int, {1: 4, 2: 2})
>>> tape.specs['by_name']
defaultdict(int, {'Hadamard': 2, 'RZ': 1, 'CNOT': 2, 'Rot': 1})

As `defaultdict` objects, any key not present in the dictionary returns 0.
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

>>> tape.specs['by_name']['RX']
0
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

"""
if self._specs is None:
self._specs = {"by_size": defaultdict(int), "by_name": defaultdict(int)}

for op in self.operations:
# don't use op.num_wires to allow for flexible gate classes like hermitian
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
self._specs["by_size"][len(op.wires)] += 1
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this was decided on previously, but the names "by_size" and "by_name" do not seem completely descriptive, how about pretending "ops_" to them? e.g., "ops_by_size", etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to Maria's suggestion, we are now using "gate_sizes" and "gate_types"


albi3ro marked this conversation as resolved.
Show resolved Hide resolved
self._specs["by_name"][op.name] += 1

self._specs["total_operations"] = len(self.operations)
self._specs["total_observables"] = len(self.observables)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe another point could be adding information about the diagonalizing gates (if any).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

self._specs["num_tape_wires"] = self.num_wires
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the "num_all_wires" or simply "all_wires" here? "num_tape_wires" assumes knowledge about having quantum tapes represent quantum circuits. When querying a QNode, a user might not possess the knowledge that there's an underlying quantum tape in a QNode and might become confused.

self._specs["depth"] = self.graph.get_depth()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: while all other information seems to be fairly cheap to get, here we need to construct the computational graph if it was not constructed already. Could it be worth having a flag for specs that is something like with_circuit_depth=False and if the user is curious about the circuit depth, they could explicitly mark the flag to be True?

Might not be a too common case, but could potentially add some unexpected overhead if specs is being during an optimization.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would have to change from properties to functions.

self._specs["num_trainable_params"] = self.num_params

return self._specs

def draw(self, charset="unicode", wire_order=None, show_all_wires=False):
"""Draw the quantum tape as a circuit diagram.

Expand Down
2 changes: 2 additions & 0 deletions pennylane/transforms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
~transforms.classical_jacobian
~draw
~metric_tensor
~specs

Transforms that act on quantum functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down Expand Up @@ -81,5 +82,6 @@
from .invisible import invisible
from .measurement_grouping import measurement_grouping
from .metric_tensor import metric_tensor, metric_tensor_tape
from .specs import specs
from .qfunc_transforms import make_tape, single_tape_transform, qfunc_transform
from .qmc import quantum_monte_carlo
2 changes: 1 addition & 1 deletion pennylane/transforms/draw.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def draw(qnode, charset="unicode", wire_order=None, show_all_wires=False):
show_all_wires (bool): If True, all wires, including empty wires, are printed.

Returns:
A function that has the same arguement signature as ``qnode``. When called,
A function that has the same argument signature as ``qnode``. When called,
the function will draw the QNode.

**Example**
Expand Down
95 changes: 95 additions & 0 deletions pennylane/transforms/specs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Copyright 2018-2021 Xanadu Quantum Technologies Inc.

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Code for resource estimation"""


def specs(qnode, max_expansion=None):
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be good to have a one sentence summary line, otherwise the docs look weird:
image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, and worth including that a QNode construction is required to obtain data here.


Args:
qnode (.QNode): the QNode to calculation the specifications for
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

Keyword Args:
max_expansion=None (int): The number of times the internal circuit should be expanded when
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
executed on a device. Expansion occurs when an operation or measurement is not
supported, and results in a gate decomposition. If any operations in the decomposition
remain unsupported by the device, another expansion occurs. Defaults to
``qnode.max_expansion``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to understand, why do we give access to exactly that one? For example, I would find it much more useful to give user access to the stopping criterion for expansion, if anything...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm just mirroring how QNode's work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wondered 🤔. If we have to expand to depth N for the device to execute, what would happen if max_expansion < N? Wouldn't it just not work on the device? Although I guess you can still access the specs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g.,

dev = qml.device('default.qubit', wires=2)
@qml.qnode(dev)
def circuit():
    qml.templates.StronglyEntanglingLayers(np.random.random((3, 2, 3)), wires=range(2))
    return qml.probs(wires=(0,1))

qml.specs(circuit, max_expansion=0)()

albi3ro marked this conversation as resolved.
Show resolved Hide resolved

Returns:
A function that has the same argument signature as ``qnode``. This function
returns a dictionary of information about qnode structure
albi3ro marked this conversation as resolved.
Show resolved Hide resolved

**Example**

.. code-block:: python3

x = np.array([0.1, 0.2])

dev = qml.device('default.qubit', wires=2)
@qml.qnode(dev)
def circuit(x, add_ry=True):
qml.RX(x[0], wires=0)
qml.CNOT(wires=(0,1))
if add_ry:
qml.RY(x[1], wires=1)
return qml.probs(wires=(0,1))

>>> info = qml.specs(circuit)(x, add_ry=False)
{'by_size': defaultdict(int, {1: 1, 2: 1}),
'by_name': defaultdict(int, {'RX': 1, 'CNOT': 1}),
'total_operations': 2,
'total_observables': 1,
'num_tape_wires': 2,
'depth': 2,
'num_device_wires': 2,
'device_name': 'default.qubit.autograd',
'diff_method': 'backprop'}

"""

def specs_qnode(*args, **kwargs):
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
"""Returns information on the structure and makeup of provided QNode.

Dictionary keys:
* ``"total_operations"``
* ``"total_observables"``
* ``"by_size"``: dictionary mapping gate number of wires to number of occurances
* ``"by_name"``: dictionary mapping gate types to number of occurances
* ``"num_tape_wires"``: number of wires used by the circuit
* ``"num_wires"``: number of wires in device
* ``"depth"``: longest path in directed acyclic graph representation
* ``"dev_short_name"``: name of QNode device
* ``"diff_method"``

Potential Additional Information:
* ``"num_trainable_params"``: number of individual scalars that are trainable
* ``"num_parameter_shift_executions"``: number of times circuit will execute when
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to check, is this up to date? Maybe the diagonalizing gates one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not up to date. Thanks for catching that! Tiny little changes having to get propagated to a million places.

calculating the derivative

Returns:
dict: information about qnode structure
albi3ro marked this conversation as resolved.
Show resolved Hide resolved
"""
if max_expansion is not None:
initial_max_expansion = qnode.max_expansion
qnode.max_expansion = max_expansion

qnode.construct(args, kwargs)

if max_expansion is not None:
qnode.max_expansion = initial_max_expansion

return qnode.specs

return specs_qnode
Loading