Skip to content
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
3651e59
Begin work on making qml.specs Catalyst compatible
jzaia18 Sep 2, 2025
2b516b1
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 2, 2025
36ae865
Add support for better device spoofing
jzaia18 Sep 2, 2025
95567b9
Merge branch 'main' into feature/catalyst-specs
jzaia18 Sep 4, 2025
96227cd
Add gradient info to specs for Catalyst
jzaia18 Sep 4, 2025
f132144
Reduce pylint warnings
jzaia18 Sep 4, 2025
4b41f7c
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 5, 2025
738b45a
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 5, 2025
c6fe125
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 5, 2025
496f686
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 9, 2025
f80f034
Touch ups from insights gained when using benchmark suite
jzaia18 Sep 11, 2025
ed41ddd
Merge branch 'feature/catalyst-specs' of github.com:PennyLaneAI/penny…
jzaia18 Sep 11, 2025
b709bbd
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 11, 2025
45f2997
Update changelog
jzaia18 Sep 11, 2025
2e0f39a
Update specs based on PR comments
jzaia18 Sep 11, 2025
dd7561b
Update pennylane/resource/specs.py
jzaia18 Sep 11, 2025
c63c20d
Slim down top level qml.specs
jzaia18 Sep 11, 2025
0df720e
Slim down top level qml.specs
jzaia18 Sep 11, 2025
28c3dce
Fix botched merge
jzaia18 Sep 11, 2025
758f3cb
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 11, 2025
1e01f1a
Streamlining from PR comments
jzaia18 Sep 11, 2025
b02088d
Add most test for non-qnode inputs
jzaia18 Sep 11, 2025
2a1fa7a
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 12, 2025
72bf927
Move device spoofing directly into null qubit
jzaia18 Sep 12, 2025
1e66e3d
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 12, 2025
f1442ec
Cleanup
jzaia18 Sep 12, 2025
860f790
Fix type in test
jzaia18 Sep 12, 2025
75ce447
Merge branch 'main' into feature/catalyst-specs
jzaia18 Sep 12, 2025
f8e74cc
Remove resource tracking from PennyLane
jzaia18 Sep 12, 2025
2400a88
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 12, 2025
81484c8
Update specs to allow TorchLayer
jzaia18 Sep 12, 2025
a7a79b9
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 12, 2025
3a99e8d
Remove unused imports
jzaia18 Sep 15, 2025
3b710d3
Merge branch 'feature/catalyst-specs' of github.com:PennyLaneAI/penny…
jzaia18 Sep 15, 2025
a3f1ee1
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 15, 2025
ec5ccf0
Fix formatting issues
jzaia18 Sep 15, 2025
29ccf95
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 15, 2025
a36a9f3
Further fix formatting
jzaia18 Sep 15, 2025
fe2c52e
Skip codecov for specs_qjit
jzaia18 Sep 15, 2025
9e5ea01
Update pennylane/devices/null_qubit.py
jzaia18 Sep 15, 2025
22790fb
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 15, 2025
ab754c7
Format
jzaia18 Sep 15, 2025
3cc5156
Merge branch 'main' into feature/catalyst-specs
jzaia18 Sep 16, 2025
100116d
Update changelog
jzaia18 Sep 16, 2025
fed59d1
Update doc/releases/changelog-dev.md
jzaia18 Sep 16, 2025
6d2a37b
Wrap file cleanup in a try-finally
jzaia18 Sep 16, 2025
962005e
Merge branch 'feature/catalyst-specs' of github.com:PennyLaneAI/penny…
jzaia18 Sep 16, 2025
b06f8eb
Better coverage for null.qubit
jzaia18 Sep 16, 2025
d4ac924
More codecov skips
jzaia18 Sep 16, 2025
0f39f80
Fix mistake in cov
jzaia18 Sep 16, 2025
6356842
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 16, 2025
fc95d5b
Steal target from NQ is given as its own target
jzaia18 Sep 18, 2025
7a5aeb8
Update pennylane/resource/specs.py
jzaia18 Sep 18, 2025
d9deaa4
Unit tests for null qubit non-default-targetting
jzaia18 Sep 18, 2025
dabef2c
Merge branch 'feature/catalyst-specs' of github.com:PennyLaneAI/penny…
jzaia18 Sep 18, 2025
554a807
Merge branch 'main' into feature/catalyst-specs
jzaia18 Sep 18, 2025
20c4b69
Merge branch 'master' into feature/catalyst-specs
JerryChen97 Sep 18, 2025
9f42c1b
Merge branch 'master' into feature/catalyst-specs
JerryChen97 Sep 18, 2025
7562974
Merge branch 'master' into feature/catalyst-specs
JerryChen97 Sep 18, 2025
ab19e9e
Add test for taking NQ underlying target
jzaia18 Sep 19, 2025
ffeeba1
Merge branch 'feature/catalyst-specs' of github.com:PennyLaneAI/penny…
jzaia18 Sep 19, 2025
96c8d77
Merge branch 'master' into feature/catalyst-specs
jzaia18 Sep 19, 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
26 changes: 25 additions & 1 deletion doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,37 @@

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

* The `qml.specs` function can now support code that has been compiled with `qml.qjit`.
This new feature only supports getting circuit specs at the `device` level.
[(#8202)](https://github.com/PennyLaneAI/pennylane/pull/8202)

```python
@qml.qjit
@qml.qnode(qml.device("lightning.qubit", wires=2))
def circuit():
qml.Hadamard(wires=0)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.Z(0) @ qml.Z(1))

print(qml.specs(circuit, level="device")())
```
```
{'resources': Resources(num_wires=2, num_gates=2, gate_types=defaultdict(<class 'int'>, {'CNOT': 1, 'Hadamard': 1}), gate_sizes=defaultdict(<class 'int'>, {2: 1, 1: 1}), depth=2, shots=Shots(total_shots=None, shot_vector=())),
'num_device_wires': 2,
'device_name': 'lightning.qubit',
'level': 'device',
'gradient_options': {},
'interface': 'auto',
'diff_method': 'best'}
```

* The `qml.sample` function can now receive an optional `dtype` parameter
which sets the type and precision of the samples returned by this measurement process.
[(#8189)](https://github.com/PennyLaneAI/pennylane/pull/8189)

* The Resource estimation toolkit was upgraded and has migrated from
:mod:`~.labs` to PennyLane as the :mod:`~.estimator` module.

* The `qml.estimator.WireResourceManager`, `qml.estimator.Allocate`, and `qml.estimator.Deallocate`
classes were added to track auxiliary wires for resource estimation.
[(#8203)](https://github.com/PennyLaneAI/pennylane/pull/8203)
Expand Down
93 changes: 30 additions & 63 deletions pennylane/devices/null_qubit.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,7 @@


import inspect
import json
import logging
import time
from collections import defaultdict
from dataclasses import replace
from functools import lru_cache, singledispatch
from numbers import Number
Expand All @@ -40,7 +37,6 @@
Shots,
StateMP,
)
from pennylane.ops.op_math import Adjoint, Controlled, ControlledOp
from pennylane.tape import QuantumScriptOrBatch
from pennylane.transforms.core import TransformProgram
from pennylane.typing import Result, ResultBatch
Expand All @@ -52,8 +48,6 @@
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())

RESOURCES_FNAME_PREFIX = "__pennylane_resources_data_"


@singledispatch
def zero_measurement(
Expand Down Expand Up @@ -135,49 +129,6 @@ def _interface(config: ExecutionConfig):
return config.interface.get_like() if config.gradient_method == "backprop" else "numpy"


def _simulate_resource_use(circuit, outfile):
num_wires = len(circuit.wires)
gate_types = defaultdict(int)

for op in circuit.operations:
controls = 0
adj = False

while hasattr(op, "base"):
if type(op) in (Controlled, ControlledOp):
# Don't check this with `isinstance` to avoid unrolling ops like CNOT
controls += len(op.control_wires)
elif isinstance(op, Adjoint):
adj = not adj
else:
break # Certain gates have "base" but shouldn't be broken down (like CNOT)
# NOTE: Pow, Exp, Add, etc. intentionally not handled to be compatible with Catalyst
op = op.base

# Barrier is not a gate and takes no resources, so we skip it
if op.name == "Barrier":
# (this confuses codecov, despite being tested)
continue # pragma: no cover

name = op.name
if adj:
name = f"Adj({name})"
if controls:
name = f"{controls if controls > 1 else ''}C({name})"

gate_types[name] += 1
# NOTE: For now, this information is being printed to match the behaviour of catalyst resource tracking.
# In the future it may be better to return this information in a more structured way.
json.dump(
{
"num_wires": num_wires,
"num_gates": sum(gate_types.values()),
"gate_types": gate_types,
},
outfile,
)


@simulator_tracking
@single_tape_support
class NullQubit(Device):
Expand All @@ -190,10 +141,11 @@ class NullQubit(Device):
(``['aux_wire', 'q1', 'q2']``). Default ``None`` if not specified.
shots (int, Sequence[int], Sequence[Union[int, Sequence[int]]]): The default number of shots
to use in executions involving this device.
track_resources (bool): If True, track the number of resources used by the device and save them to a JSON file.
The filename will match ``__pennylane_resources_data_*.json`` where the wildcard (asterisk) is replaced by
the timestamp of when execution began in nanoseconds since Unix EPOCH. This argument is experimental and
subject to change.
track_resources (bool): If True, turn on Catalyst device resource tracking.
resources_filename (string): If set, the static filename to use when saving resource data.
If not set, the filename will match ``__pennylane_resources_data_*`` where the wildcard (asterisk)
is replaced by the timestamp of when execution began in nanoseconds since Unix EPOCH.
compute_depth (bool): If True, compute the circuit depth as part of resource tracking.

**Example:**

Expand Down Expand Up @@ -278,24 +230,34 @@ def name(self):
"""The name of the device."""
return "null.qubit"

def __init__(self, wires=None, shots=None, track_resources=False) -> None:
# pylint: disable=too-many-arguments
def __init__(
self,
wires=None,
shots=None,
track_resources=False,
resources_filename=None,
compute_depth=None,
target_device=None,
) -> None:
super().__init__(wires=wires, shots=shots)
self._debugger = None
self._track_resources = track_resources
self._target_device = target_device

# this is required by Catalyst to toggle the tracker at runtime
self.device_kwargs = {"track_resources": track_resources}
if resources_filename is not None:
self.device_kwargs["resources_filename"] = resources_filename
if compute_depth is not None:
self.device_kwargs["compute_depth"] = compute_depth

if target_device is not None:
self.config_filepath = target_device.config_filepath

def _simulate(self, circuit, interface):
num_device_wires = len(self.wires) if self.wires else len(circuit.wires)
results = []

if self._track_resources:
timestamp = int(time.time() * 1e9) # nanoseconds since epoch
resources_fname = f"{RESOURCES_FNAME_PREFIX}{timestamp}.json"
with open(resources_fname, "x", encoding="utf-8") as f:
_simulate_resource_use(circuit, f)

for s in circuit.shots or [None]:
r = tuple(
zero_measurement(mp, num_device_wires, s, circuit.batch_size, interface)
Expand Down Expand Up @@ -347,8 +309,13 @@ def preprocess(
if execution_config is None:
execution_config = ExecutionConfig()

target = DefaultQubit(wires=self.wires)
program = target.preprocess_transforms(execution_config)
if self._target_device is None:
target = DefaultQubit(wires=self.wires)
else:
target = self._target_device

program, _ = target.preprocess(execution_config)

for t in program:
if t.transform == decompose.transform:
original_stopping_condition = t.kwargs["stopping_condition"]
Expand Down
Loading