Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
91 commits
Select commit Hold shift + click to select a range
8b675bd
Disentangle this branch from new specs features
jzaia18 Nov 28, 2025
5c37ef3
Update unit tests to match new specs
jzaia18 Nov 28, 2025
d37e027
Formatting
jzaia18 Nov 28, 2025
1ad4919
Make more tests pass
jzaia18 Nov 28, 2025
0799379
Update docs
jzaia18 Nov 29, 2025
f89312e
More tweaks to how resources are returned
jzaia18 Nov 29, 2025
8b0d16e
Fix more unit tests
jzaia18 Nov 29, 2025
3cb11ac
Yet more test tweaks
jzaia18 Nov 29, 2025
335caa4
More updates
jzaia18 Nov 29, 2025
039512a
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 1, 2025
cef960f
Fix last failing test
jzaia18 Dec 1, 2025
336e9c7
Merge branch 'chore/specs-outputs' of github.com:PennyLaneAI/pennylan…
jzaia18 Dec 1, 2025
4eb692d
Docs for SpecsResources
jzaia18 Dec 1, 2025
a63dcb1
Fix yet another test
jzaia18 Dec 1, 2025
592ffc0
Add docs for SpecsResult
jzaia18 Dec 1, 2025
243692d
These tests Q-Q
jzaia18 Dec 1, 2025
3f0c23a
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 1, 2025
3dfc168
Unit tests for SpecsResources
jzaia18 Dec 1, 2025
88eaec0
Merge branch 'chore/specs-outputs' of github.com:PennyLaneAI/pennylan…
jzaia18 Dec 1, 2025
7f833cd
Re-add tests for _count_resources
jzaia18 Dec 1, 2025
d2e5569
Re-add tests for _count_resources
jzaia18 Dec 1, 2025
53aa47d
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 1, 2025
ba0514c
Test to_dict
jzaia18 Dec 1, 2025
321d605
Tests for SpecsResult
jzaia18 Dec 1, 2025
ef97962
Merge branch 'chore/specs-outputs' of github.com:PennyLaneAI/pennylan…
jzaia18 Dec 1, 2025
cd117ba
Docs updates
jzaia18 Dec 1, 2025
e448e77
Update changelog
jzaia18 Dec 1, 2025
8f09c21
Update pennylane/devices/_qubit_device.py
jzaia18 Dec 1, 2025
04285d0
Rename SpecsResult to CircuitSpecs
jzaia18 Dec 1, 2025
d8266ac
Missed one!
jzaia18 Dec 1, 2025
b4238ed
Update how multi-controlled gates are displayed in specs
jzaia18 Dec 1, 2025
b7ab2e9
Docs updates
jzaia18 Dec 2, 2025
93535e7
More docs updates
jzaia18 Dec 2, 2025
9322d20
Update formatting of null.qubit in docs
jzaia18 Dec 2, 2025
397a15e
Sphinx?
jzaia18 Dec 2, 2025
17691de
Indenting got messed up
jzaia18 Dec 2, 2025
817500b
More docs fixes
jzaia18 Dec 2, 2025
fd1e2fb
Merge branch 'main' into chore/specs-outputs
jzaia18 Dec 2, 2025
3081978
Better separation for pretty printing
jzaia18 Dec 2, 2025
60e90af
ipython display
jzaia18 Dec 2, 2025
a034f6d
Magical BLANKLINE incantations
jzaia18 Dec 2, 2025
c20c38a
Streamline resource conversion
jzaia18 Dec 2, 2025
ca47e4b
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 2, 2025
0230d47
Apply suggestions from code review
jzaia18 Dec 2, 2025
c07f560
Format
jzaia18 Dec 2, 2025
970df34
Remove unused import
jzaia18 Dec 2, 2025
b40c925
Remove 'pycon' codeblock
jzaia18 Dec 3, 2025
57a00b1
Add new unit test for qml.marker with qml.specs
jzaia18 Dec 3, 2025
b2bf47b
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 3, 2025
a0012a5
Update how str is made
jzaia18 Dec 3, 2025
1fe81dd
Update docs
jzaia18 Dec 3, 2025
d18c41d
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 3, 2025
f81bae9
Return observable info within measurements for plain PL specs
jzaia18 Dec 4, 2025
5ce0ab4
Merge branch 'main' into chore/specs-outputs
jzaia18 Dec 4, 2025
53edb6d
Update unit tests
jzaia18 Dec 4, 2025
e63fdf4
Update docs
jzaia18 Dec 4, 2025
e1e3af3
Skip coverage on ipython display helper
jzaia18 Dec 4, 2025
b3b2b24
Add hamiltonian tests
jzaia18 Dec 5, 2025
f78be3f
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 5, 2025
b7d54f4
Change how batching is handled
jzaia18 Dec 5, 2025
aba6550
Docs fixes
jzaia18 Dec 5, 2025
78f64ac
Apply suggestions from code review
jzaia18 Dec 5, 2025
7c4994a
Docs changes
jzaia18 Dec 5, 2025
1b31c14
Merge branch 'chore/specs-outputs' of github.com:PennyLaneAI/pennylan…
jzaia18 Dec 5, 2025
54c298a
Minor typo
jzaia18 Dec 5, 2025
bc1ff64
Switch back to list as possible output
jzaia18 Dec 5, 2025
dcfee7b
Fix printing for CircuitSpecs
jzaia18 Dec 5, 2025
77e9543
Fix doctests
jzaia18 Dec 5, 2025
51ca4b1
Clean up specs output
jzaia18 Dec 5, 2025
eebde8d
Placate codefactor
jzaia18 Dec 5, 2025
294f2fb
More thorough testing for new batch output
jzaia18 Dec 5, 2025
5250274
Add more coverage
jzaia18 Dec 5, 2025
f3b7ec3
Apply suggestions from code review
jzaia18 Dec 6, 2025
74cfce3
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 6, 2025
b894da4
Implement Anton's suggestions
jzaia18 Dec 7, 2025
1e8a45a
Apply suggestions from code review
jzaia18 Dec 8, 2025
e80bd40
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 8, 2025
cf9c3b3
Update how hamiltonians are tracked
jzaia18 Dec 8, 2025
8efa45b
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 8, 2025
a694229
Update test
jzaia18 Dec 8, 2025
39e4601
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 8, 2025
ac15ca0
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 8, 2025
4b3ee0c
Standardize how observables are printed
jzaia18 Dec 8, 2025
03045c3
Merge branch 'chore/specs-outputs' of github.com:PennyLaneAI/pennylan…
jzaia18 Dec 8, 2025
c03c44b
Doctests
jzaia18 Dec 8, 2025
fed1a3d
Update pennylane/resource/__init__.py
jzaia18 Dec 8, 2025
ff91811
Fix missing import
jzaia18 Dec 8, 2025
6f797fc
Coverage
jzaia18 Dec 8, 2025
b9fcbf1
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 8, 2025
ccbf8c6
Merge branch 'master' into chore/specs-outputs
jzaia18 Dec 9, 2025
7e3809b
Codecov
jzaia18 Dec 9, 2025
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
16 changes: 7 additions & 9 deletions pennylane/devices/_qubit_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,14 @@
SampleMeasurement,
SampleMP,
ShadowExpvalMP,
Shots,
StateMeasurement,
StateMP,
VarianceMP,
VnEntropyMP,
)
from pennylane.operation import Operation, Operator, operation_derivative
from pennylane.ops import MeasurementValue, MidMeasure, Rot, X, Y, Z, adjoint
from pennylane.resource import Resources
from pennylane.resource import SpecsResources
from pennylane.tape import QuantumScript
from pennylane.wires import Wires

Expand Down Expand Up @@ -314,16 +313,15 @@
self._num_executions += 1

if self.tracker.active:
shots_from_dev = self._shots if not self.shot_vector else self._raw_shot_sequence

Check notice on line 316 in pennylane/devices/_qubit_device.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/devices/_qubit_device.py#L316

Unused variable 'shots_from_dev' (unused-variable)
tape_resources = circuit.specs["resources"]

resources = Resources( # temporary until shots get updated on tape !
tape_resources.num_wires,
tape_resources.num_gates,
tape_resources.gate_types,
tape_resources.gate_sizes,
tape_resources.depth,
Shots(shots_from_dev),
resources = SpecsResources( # temporary until shots get updated on tape !
num_allocs=tape_resources["num_allocs"],
gate_types=tape_resources["gate_types"],
gate_sizes=tape_resources["gate_sizes"],
measurements=tape_resources["measurements"],
depth=tape_resources["depth"],
)
self.tracker.update(
executions=1, shots=self._shots, results=results, resources=resources
Expand Down
2 changes: 2 additions & 0 deletions pennylane/resource/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,8 @@ def circuit(theta):
from .resource import (
Resources,
ResourcesOperation,
SpecsResources,
SpecsResult,
add_in_series,
add_in_parallel,
mul_in_series,
Expand Down
168 changes: 131 additions & 37 deletions pennylane/resource/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
import copy
from abc import abstractmethod
from collections import defaultdict
from dataclasses import dataclass, field
from dataclasses import asdict, dataclass, field, fields
from typing import Any

from pennylane.measurements import Shots, add_shots
Expand All @@ -29,21 +29,6 @@
from .error import _compute_algo_error


class SpecsDict(dict):
"""A special dictionary for storing the specs of a circuit. Used to customize ``KeyError`` messages."""

def __getitem__(self, __k):
if __k == "num_diagonalizing_gates":
raise KeyError(
"num_diagonalizing_gates is no longer in specs due to the ambiguity of the definition "
"and extreme performance costs."
)
try:
return super().__getitem__(__k)
except KeyError as e:
raise KeyError(f"key {__k} not available. Options are {set(self.keys())}") from e


@dataclass(frozen=True)
class Resources:
r"""Contains attributes which store key resources such as number of gates, number of wires, shots,
Expand Down Expand Up @@ -105,9 +90,9 @@

num_wires: int = 0
num_gates: int = 0
gate_types: dict = field(default_factory=dict)
gate_sizes: dict = field(default_factory=dict)
depth: int = 0
gate_types: dict[str, int] = field(default_factory=dict)
gate_sizes: dict[int, int] = field(default_factory=dict)
depth: int | None = 0
shots: Shots = field(default_factory=Shots)

def __add__(self, other: Resources):
Expand Down Expand Up @@ -233,6 +218,111 @@
print(str(self))


# TODO: Would be better to have SpecsResources inherit from Resources directly, but there are too
# many extra fields that are unwanted. Would be worth refactoring in the future.
@dataclass(frozen=True)
class SpecsResources:

Check notice on line 224 in pennylane/resource/resource.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/resource/resource.py#L224

Missing class docstring (missing-class-docstring)
gate_types: dict[str, int]
gate_sizes: dict[int, int]
measurements: dict[str, int]
num_allocs: int
depth: int | None = None

def __post_init__(self):
assert sum(self.gate_types.values()) == sum(self.gate_sizes.values())

@property
def num_gates(self) -> int:

Check notice on line 235 in pennylane/resource/resource.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/resource/resource.py#L235

Missing function or method docstring (missing-function-docstring)
return sum(self.gate_types.values())

def to_pretty_str(self, preindent: int = 0) -> str:
"""
Pretty string representation of the SpecsResources object.
"""
prefix = " " * preindent
s = f"{prefix}Total qubit allocations: {self.num_allocs}\n"
s += f"{prefix}Total gates: {self.num_gates}\n"
s += (
f"{prefix}Circuit depth: {self.depth if self.depth is not None else 'Not computed'}\n\n"
)

s += f"{prefix}Gate types:\n"
if not self.gate_types:
s += prefix + " No gates.\n"
else:
for gate, count in self.gate_types.items():
s += f"{prefix} {gate}: {count}\n"

s += f"\n{prefix}Measurements:\n"
if not self.measurements:
s += prefix + " No measurements.\n"
else:
for meas, count in self.measurements.items():
s += f"{prefix} {meas}: {count}\n"

return s.rstrip("\n")

# Leave repr and str methods separate for simple and pretty printing
def __str__(self) -> str:
return self.to_pretty_str()


@dataclass(frozen=True)
class SpecsResult:

Check notice on line 271 in pennylane/resource/resource.py

View check run for this annotation

codefactor.io / CodeFactor

pennylane/resource/resource.py#L271

Missing class docstring (missing-class-docstring)
resources: SpecsResources | dict[int | str, SpecsResources] = None
shots: Shots = None
device_name: str = None
num_device_wires: int = None
level: Any = None

def to_dict(self) -> dict[str, Any]:
"""Convert the SpecsResources to a dictionary."""
return asdict(self)

def __getitem__(self, key):
if key in (field.name for field in fields(self)):
return getattr(self, key)

match key:
# Fields that used to be included in specs output prior to PL version 0.44
case "num_observables":
raise KeyError(
"num_observables is no longer in top-level specs and has instead been absorbed into the 'measurements' attribute of the specs's resources."
)
case "interface" | "diff_method" | "errors" | "num_tape_wires":
raise KeyError(f"key '{key}' is no longer included in specs.")
case (
"gradient_fn"
| "gradient_options"
| "num_gradient_executions"
| "num_trainable_params"
):
raise KeyError(
f"key '{key}' is no longer included in specs, as it no longer gathers gradient information."
)
raise KeyError(
f"key '{key}' not available. Options are {[field.name for field in fields(self)]}"
)

# Separate str and repr methods for simple and pretty printing
def __str__(self):
s = f"Device: {self.device_name}\n"
s += f"Device wires: {self.num_device_wires}\n"
s += f"Shots: {self.shots}\n"
s += f"Level: {self.level}\n\n"

s += "Resource specifications:\n"
if isinstance(self.resources, SpecsResources):
s += self.resources.to_pretty_str(preindent=2)
else:
for level, res in self.resources.items():
s += f"Level = {level}:\n"
s += res.to_pretty_str(preindent=2)
s += "\n" + "-" * 60 + "\n\n"

return s.rstrip("\n-")


class ResourcesOperation(Operation):
r"""Base class that represents quantum gates or channels applied to quantum
states and stores the resource requirements of the quantum gate.
Expand Down Expand Up @@ -621,7 +711,9 @@
# The reason why this function is not a method of the QuantumScript class is
# because we don't want a core module (QuantumScript) to depend on an auxiliary module (Resource).
# The `QuantumScript.specs` property will eventually be deprecated in favor of this function.
def specs_from_tape(tape: QuantumScript, compute_depth: bool = True) -> SpecsDict[str, Any]:
def resources_from_tape(
tape: QuantumScript, compute_depth: bool = True, compute_errs: bool = False
) -> SpecsResources | tuple[SpecsResources, dict[str, Any]]:
"""
Extracts the resource information from a quantum circuit (tape).

Expand All @@ -639,16 +731,12 @@
(.SpecsDict): The specifications extracted from the workflow
"""
resources = _count_resources(tape, compute_depth=compute_depth)
algo_errors = _compute_algo_error(tape)

return SpecsDict(
{
"resources": resources,
"errors": algo_errors,
"num_observables": len(tape.observables),
"num_trainable_params": tape.num_params,
}
)

if compute_errs:
algo_errors = _compute_algo_error(tape)
return resources, algo_errors

return resources


def _combine_dict(dict1: dict, dict2: dict):
Expand All @@ -675,7 +763,7 @@
return combined_dict


def _count_resources(tape: QuantumScript, compute_depth: bool = True) -> Resources:
def _count_resources(tape: QuantumScript, compute_depth: bool = True) -> SpecsResources:
"""Given a quantum circuit (tape), this function
counts the resources used by standard PennyLane operations.

Expand All @@ -687,12 +775,12 @@
Returns:
(.Resources): The total resources used in the workflow
"""

num_wires = len(tape.wires)
shots = tape.shots
depth = tape.graph.get_depth() if compute_depth else None

num_gates = 0
gate_types = defaultdict(int)
measurements = defaultdict(int)
gate_sizes = defaultdict(int)
for op in tape.operations:
if isinstance(op, ResourcesOperation):
Expand All @@ -703,11 +791,17 @@
for n in op_resource.gate_sizes:
gate_sizes[n] += op_resource.gate_sizes[n]

num_gates += sum(op_resource.gate_types.values())

else:
gate_types[op.name] += 1
gate_sizes[len(op.wires)] += 1
num_gates += 1

return Resources(num_wires, num_gates, gate_types, gate_sizes, depth, shots)
for meas in tape.measurements:
measurements[meas._shortname] += 1 # pylint: disable=protected-access

return SpecsResources(
gate_types=dict(gate_types),
gate_sizes=dict(gate_sizes),
measurements=dict(measurements),
num_allocs=num_wires,
depth=depth,
)
Loading
Loading