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

Add reference.qubit for testing and reference #6181

Merged
merged 80 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
15247a9
Implement ReferenceQubit
astralcai Sep 3, 2024
6060ad6
make isort happy
astralcai Sep 4, 2024
f1f6557
Merge branch 'master' into mini-dev-new
astralcai Sep 4, 2024
5a0bea6
Fix bug in reference.qubit
astralcai Sep 4, 2024
2068b19
Add `reference.qubit` to autograd tests
astralcai Sep 4, 2024
66b3769
add reference.qubit to autograd qnode tests
astralcai Sep 5, 2024
db63bb6
xfail unsupported test
astralcai Sep 5, 2024
5b7b10b
Add reference.qubit to test_jax
astralcai Sep 5, 2024
74bc82b
fix bug
dwierichs Sep 5, 2024
a7a53de
-a
dwierichs Sep 5, 2024
d78e4e1
changelog
dwierichs Sep 5, 2024
a699575
Merge branch 'fix-jvp-shots' of https://github.com/PennyLaneAI/pennyl…
astralcai Sep 5, 2024
91c198d
more test cases in test_jax
astralcai Sep 5, 2024
0fdcc03
update test_jax_qnode
astralcai Sep 5, 2024
4cf6c28
add reference.qubit to test_jax_jit_qnode
astralcai Sep 5, 2024
f08d9df
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
astralcai Sep 5, 2024
df4c2a1
fix test_spsa_gradient after autograd change
astralcai Sep 5, 2024
54b46e6
fix more spsa test
astralcai Sep 5, 2024
7d48f9a
add reference.qubit to tensorflow tests
astralcai Sep 5, 2024
0025281
fix failing test in test_lightning_qubit
astralcai Sep 5, 2024
c62ce82
Add _to_autograd to autograd execute
astralcai Sep 5, 2024
58e5d52
move changes to a different branch
astralcai Sep 5, 2024
6bb842d
revert lightning test change
astralcai Sep 5, 2024
26111be
stop treating numpy as autograd internally
astralcai Sep 5, 2024
5e8740b
Merge branch 'master' into autograd-bug
astralcai Sep 5, 2024
26d16bc
bug fixes
astralcai Sep 6, 2024
c45b750
uncomment line
astralcai Sep 6, 2024
dfacd7f
Merge branch 'master' into autograd-bug
astralcai Sep 6, 2024
8fd09d7
Merge branch 'master' into mini-dev-new
astralcai Sep 6, 2024
48f73c4
fix changelog
astralcai Sep 6, 2024
0633dc0
fix tiny bug
astralcai Sep 6, 2024
f1213cb
Merge branch 'master' into autograd-bug
astralcai Sep 6, 2024
e1a1fc5
Apply suggestions from code review
dwierichs Sep 9, 2024
c529637
Merge branch 'master' into fix-jvp-shots
dwierichs Sep 9, 2024
5bd7176
minor updates
astralcai Sep 9, 2024
ffb9d3c
more fix
astralcai Sep 9, 2024
e2a67d2
Merge branch 'fix-jvp-shots' of https://github.com/PennyLaneAI/pennyl…
astralcai Sep 9, 2024
a11ef48
changelog
astralcai Sep 9, 2024
2ecd8ab
Merge branch 'master' into mini-dev-new
astralcai Sep 9, 2024
8333c19
format
astralcai Sep 9, 2024
3ad1921
fix isort
astralcai Sep 9, 2024
5d6e8eb
fix bug
astralcai Sep 9, 2024
6776c01
fix tests
astralcai Sep 9, 2024
eca0f14
fix black
astralcai Sep 9, 2024
33f4f63
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
astralcai Sep 9, 2024
2e4f55c
revert change
astralcai Sep 9, 2024
8c4a72a
add sparse matrix to Hermitian
astralcai Sep 9, 2024
3c8a691
bug fix
astralcai Sep 9, 2024
71561f9
bug fix
astralcai Sep 9, 2024
676d85b
bug fix
astralcai Sep 9, 2024
63525a2
Merge branch 'master' into autograd-bug
astralcai Sep 9, 2024
b34b48c
clean up handling of interface
astralcai Sep 10, 2024
9810b68
Merge branch 'master' into autograd-bug
astralcai Sep 10, 2024
c21eee3
fix isort
astralcai Sep 10, 2024
ffd41a9
update
astralcai Sep 10, 2024
cee3976
fix some tests
astralcai Sep 10, 2024
b7ac5e7
Merge branch 'master' into mini-dev-new
astralcai Sep 10, 2024
087dc22
fix tests
astralcai Sep 10, 2024
535e66b
make pylint happy
astralcai Sep 10, 2024
18c5fa6
update name
astralcai Sep 10, 2024
7fde693
fix isort
astralcai Sep 10, 2024
9e86111
fix tests
astralcai Sep 10, 2024
c4415a8
Update pennylane/workflow/qnode.py
astralcai Sep 11, 2024
6ac5315
Merge branch 'master' into autograd-bug
astralcai Sep 11, 2024
125e06c
Add reference.qubit to torch tests
astralcai Sep 11, 2024
b91fc7b
add reference.qubit to more tensorflow tests
astralcai Sep 11, 2024
b6ad427
changelog
astralcai Sep 12, 2024
c41572e
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
astralcai Sep 12, 2024
fee0af9
Merge branch 'master' into autograd-bug
astralcai Sep 12, 2024
23e8142
Merge branch 'autograd-bug' of https://github.com/PennyLaneAI/pennyla…
astralcai Sep 12, 2024
8fe18d1
fix test
astralcai Sep 12, 2024
5b6427f
Update pennylane/devices/reference_qubit.py
astralcai Sep 13, 2024
52caafc
Update pennylane/devices/reference_qubit.py
astralcai Sep 13, 2024
b6fadc6
update skip message
astralcai Sep 13, 2024
d31c0c7
add tape assertion
astralcai Sep 13, 2024
34fe756
Merge branch 'master' of https://github.com/PennyLaneAI/pennylane int…
astralcai Sep 16, 2024
bf4b212
Update pennylane/devices/reference_qubit.py
astralcai Sep 16, 2024
449a0cd
add ReferenceQubit to devices
astralcai Sep 16, 2024
ba1560a
Merge branch 'master' into mini-dev-new
astralcai Sep 16, 2024
d7d9861
Merge branch 'master' into mini-dev-new
astralcai Sep 17, 2024
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
22 changes: 14 additions & 8 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,14 @@
<h3>Improvements 🛠</h3>

* `qml.qchem.excitations` now optionally returns fermionic operators.
[(#6171)](https://github.com/PennyLaneAI/pennylane/pull/6171)
[(#6171)](https://github.com/PennyLaneAI/pennylane/pull/6171)

* The `diagonalize_measurements` transform now uses a more efficient method of diagonalization
when possible, based on the `pauli_rep` of the relevant observables.
[#6113](https://github.com/PennyLaneAI/pennylane/pull/6113/)

<h4>Capturing and representing hybrid programs</h4>

* Differentiation of hybrid programs via `qml.grad` can now be captured into plxpr.
When evaluating a captured `qml.grad` instruction, it will dispatch to `jax.grad`,
which differs from the Autograd implementation of `qml.grad` itself.
[(#6120)](https://github.com/PennyLaneAI/pennylane/pull/6120)
* The `Hermitian` operator now has a `compute_sparse_matrix` implementation.
[(#6225)](https://github.com/PennyLaneAI/pennylane/pull/6225)

<h4>Capturing and representing hybrid programs</h4>

Expand Down Expand Up @@ -50,6 +46,9 @@
unique representation of the object.
[(#6167)](https://github.com/PennyLaneAI/pennylane/pull/6167)

* A `ReferenceQubit` is introduced for testing purposes and as a reference for future plugin development.
[(#6181)](https://github.com/PennyLaneAI/pennylane/pull/6181)

* The `to_mat` methods for `FermiWord` and `FermiSentence` now optionally return
a sparse matrix.
[(#6173)](https://github.com/PennyLaneAI/pennylane/pull/6173)
Expand Down Expand Up @@ -102,12 +101,19 @@
* The ``qml.Qubitization`` template now orders the ``control`` wires first and the ``hamiltonian`` wires second, which is the expected according to other templates.
[(#6229)](https://github.com/PennyLaneAI/pennylane/pull/6229)

* <h3>Contributors ✍️</h3>
* Fixes a bug where a circuit using the `autograd` interface sometimes returns nested values that are not of the `autograd` interface.
[(#6225)](https://github.com/PennyLaneAI/pennylane/pull/6225)

* Fixes a bug where a simple circuit with no parameters or only builtin/numpy arrays as parameters returns autograd tensors.
[(#6225)](https://github.com/PennyLaneAI/pennylane/pull/6225)

<h3>Contributors ✍️</h3>

This release contains contributions from (in alphabetical order):

Guillermo Alonso,
Utkarsh Azad,
Astral Cai,
Lillian M. A. Frederiksen,
Christina Lee,
William Maxwell,
Expand Down
6 changes: 3 additions & 3 deletions pennylane/devices/execution_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from dataclasses import dataclass, field
from typing import Optional, Union

from pennylane.workflow import SUPPORTED_INTERFACES
from pennylane.workflow import SUPPORTED_INTERFACE_NAMES


@dataclass
Expand Down Expand Up @@ -110,9 +110,9 @@ def __post_init__(self):

Note that this hook is automatically called after init via the dataclass integration.
"""
if self.interface not in SUPPORTED_INTERFACES:
if self.interface not in SUPPORTED_INTERFACE_NAMES:
raise ValueError(
f"Unknown interface. interface must be in {SUPPORTED_INTERFACES}, got {self.interface} instead."
f"Unknown interface. interface must be in {SUPPORTED_INTERFACE_NAMES}, got {self.interface} instead."
)

if self.grad_on_execution not in {True, False, None}:
Expand Down
18 changes: 9 additions & 9 deletions pennylane/devices/legacy_facade.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import pennylane as qml
from pennylane.measurements import MidMeasureMP, Shots
from pennylane.transforms.core.transform_program import TransformProgram
from pennylane.workflow.execution import INTERFACE_MAP

from .device_api import Device
from .execution_config import DefaultExecutionConfig
Expand Down Expand Up @@ -328,18 +329,18 @@ def _create_temp_device(self, batch):
if interface == "numpy":
return self._device

mapped_interface = qml.workflow.execution.INTERFACE_MAP.get(interface, interface)
interface = INTERFACE_MAP.get(interface, interface)

backprop_interface = self._device.capabilities().get("passthru_interface", None)
if mapped_interface == backprop_interface:
if interface == backprop_interface:
return self._device

backprop_devices = self._device.capabilities().get("passthru_devices", None)

if backprop_devices is None:
raise qml.DeviceError(f"Device {self} does not support backpropagation.")

if backprop_devices[mapped_interface] == self._device.short_name:
if backprop_devices[interface] == self._device.short_name:
return self._device

if self.target_device.short_name != "default.qubit.legacy":
Expand Down Expand Up @@ -367,7 +368,7 @@ def _create_temp_device(self, batch):
)
# we already warned about backprop device switching
new_device = qml.device(
backprop_devices[mapped_interface],
backprop_devices[interface],
wires=self._device.wires,
shots=self._device.shots,
).target_device
Expand Down Expand Up @@ -396,25 +397,24 @@ def _validate_backprop_method(self, tape):
return False
params = tape.get_parameters(trainable_only=False)
interface = qml.math.get_interface(*params)
if interface != "numpy":
interface = INTERFACE_MAP.get(interface, interface)

if tape and any(isinstance(m.obs, qml.SparseHamiltonian) for m in tape.measurements):
return False
if interface == "numpy":
interface = None
mapped_interface = qml.workflow.execution.INTERFACE_MAP.get(interface, interface)

# determine if the device supports backpropagation
backprop_interface = self._device.capabilities().get("passthru_interface", None)

if backprop_interface is not None:
# device supports backpropagation natively
return mapped_interface in [backprop_interface, "Numpy"]
return interface in [backprop_interface, "numpy"]
# determine if the device has any child devices that support backpropagation
backprop_devices = self._device.capabilities().get("passthru_devices", None)

if backprop_devices is None:
return False
return mapped_interface in backprop_devices or mapped_interface == "Numpy"
return interface in backprop_devices or interface == "numpy"

def _validate_adjoint_method(self, tape):
# The conditions below provide a minimal set of requirements that we can likely improve upon in
Expand Down
4 changes: 2 additions & 2 deletions pennylane/devices/qubit/simulate.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,7 +922,7 @@ def _(original_measurement: ExpectationMP, measures): # pylint: disable=unused-
for v in measures.values():
if not v[0] or v[1] is tuple():
continue
cum_value += v[0] * v[1]
cum_value += qml.math.multiply(v[0], v[1])
total_counts += v[0]
return cum_value / total_counts

Expand All @@ -935,7 +935,7 @@ def _(original_measurement: ProbabilityMP, measures): # pylint: disable=unused-
for v in measures.values():
if not v[0] or v[1] is tuple():
continue
cum_value += v[0] * v[1]
cum_value += qml.math.multiply(v[0], v[1])
total_counts += v[0]
return cum_value / total_counts

Expand Down
154 changes: 154 additions & 0 deletions pennylane/devices/reference_qubit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# Copyright 2018-2024 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.
"""
Contains the ReferenceQubit device, a minimal device that can be used for testing
and plugin development purposes.
"""

import numpy as np

import pennylane as qml

from .device_api import Device
from .execution_config import DefaultExecutionConfig
from .modifiers import simulator_tracking, single_tape_support
from .preprocess import decompose, validate_device_wires, validate_measurements


def sample_state(state: np.ndarray, shots: int, seed=None):
"""Generate samples from the provided state and number of shots."""

probs = np.imag(state) ** 2 + np.real(state) ** 2
basis_states = np.arange(len(probs))

num_wires = int(np.log2(len(probs)))

rng = np.random.default_rng(seed)
basis_samples = rng.choice(basis_states, shots, p=probs)

# convert basis state integers to array of booleans
bin_strings = (format(s, f"0{num_wires}b") for s in basis_samples)
return np.array([[int(val) for val in s] for s in bin_strings])


def simulate(tape: qml.tape.QuantumTape, seed=None) -> qml.typing.Result:
"""Simulate a tape and turn it into results.

Args:
tape (.QuantumTape): a representation of a circuit
seed (Any): A seed to use to control the generation of samples.

"""
# 1) create the initial state
state = np.zeros(2 ** len(tape.wires))
state[0] = 1.0

# 2) apply all the operations
for op in tape.operations:
op_mat = op.matrix(wire_order=tape.wires)
state = qml.math.matmul(op_mat, state)

# 3) perform measurements
# note that shots are pulled from the tape, not from the device
if tape.shots:
samples = sample_state(state, shots=tape.shots.total_shots, seed=seed)
# Shot vector support
results = []
for lower, upper in tape.shots.bins():
sub_samples = samples[lower:upper]
results.append(
tuple(mp.process_samples(sub_samples, tape.wires) for mp in tape.measurements)
)
if len(tape.measurements) == 1:
results = [res[0] for res in results]
if not tape.shots.has_partitioned_shots:
results = results[0]
else:
results = tuple(results)
else:
results = tuple(mp.process_state(state, tape.wires) for mp in tape.measurements)
if len(tape.measurements) == 1:
results = results[0]

return results


operations = frozenset({"PauliX", "PauliY", "PauliZ", "Hadamard", "CNOT", "CZ", "RX", "RY", "RZ"})


def supports_operation(op: qml.operation.Operator) -> bool:
"""This function used by preprocessing determines what operations
are natively supported by the device.

While in theory ``simulate`` can support any operation with a matrix, we limit the target
gate set for improved testing and reference purposes.

"""
return getattr(op, "name", None) in operations


@simulator_tracking # update device.tracker with some relevant information
@single_tape_support # add support for device.execute(tape) in addition to device.execute((tape,))
class ReferenceQubit(Device):
"""A slimmed down numpy-based simulator for reference and testing purposes.

Args:
wires (int, Iterable[Number, str]): Number of wires present on the device, or iterable that
contains unique labels for the wires as numbers (i.e., ``[-1, 0, 2]``) or strings
(``['ancilla', 'q1', 'q2']``). Default ``None`` if not specified. While this device allows
astralcai marked this conversation as resolved.
Show resolved Hide resolved
for ``wires`` to be unspecified at construction time, other devices may make this argument
mandatory. Devices can also implement additional restrictions on the possible wires.
shots (int, Sequence[int], Sequence[Union[int, Sequence[int]]]): The default number of shots
to use in executions involving this device. Note that during execution, shots
are pulled from the circuit, not from the device.
seed (Union[str, None, int, array_like[int], SeedSequence, BitGenerator, Generator, jax.random.PRNGKey]): A
seed-like parameter matching that of ``seed`` for ``numpy.random.default_rng``. This is an optional
keyword argument added to follow recommend NumPy best practices. Other devices do not need
this parameter if it does not make sense for them.

"""

name = "reference.qubit"

def __init__(self, wires=None, shots=None, seed=None):
super().__init__(wires=wires, shots=shots)

# seed and rng not necessary for a device, but part of recommended
# numpy practices to use a local random number generator
self._rng = np.random.default_rng(seed)

def preprocess(self, execution_config=DefaultExecutionConfig):

# Here we convert an arbitrary tape into one natively supported by the device
program = qml.transforms.core.TransformProgram()
program.add_transform(validate_device_wires, wires=self.wires, name="reference.qubit")
program.add_transform(qml.defer_measurements)
program.add_transform(qml.transforms.split_non_commuting)
program.add_transform(qml.transforms.diagonalize_measurements)
program.add_transform(
decompose,
stopping_condition=supports_operation,
skip_initial_state_prep=False,
name="reference.qubit",
)
program.add_transform(validate_measurements, name="reference.qubit")
program.add_transform(qml.transforms.broadcast_expand)

# no need to preprocess the config as the device does not support derivatives
return program, execution_config

def execute(self, circuits, execution_config=DefaultExecutionConfig):
for tape in circuits:
assert all(supports_operation(op) for op in tape.operations)
return tuple(simulate(tape, seed=self._rng) for tape in circuits)
4 changes: 4 additions & 0 deletions pennylane/ops/qubit/observables.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ def compute_matrix(A: TensorLike) -> TensorLike: # pylint: disable=arguments-di
Hermitian._validate_input(A)
return A

@staticmethod
def compute_sparse_matrix(A) -> csr_matrix: # pylint: disable=arguments-differ
return csr_matrix(Hermitian.compute_matrix(A))

@property
def eigendecomposition(self) -> dict[str, TensorLike]:
"""Return the eigendecomposition of the matrix specified by the Hermitian observable.
Expand Down
2 changes: 1 addition & 1 deletion pennylane/workflow/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,6 @@

"""
from .construct_batch import construct_batch, get_transform_program
from .execution import INTERFACE_MAP, SUPPORTED_INTERFACES, execute
from .execution import INTERFACE_MAP, SUPPORTED_INTERFACE_NAMES, execute
from .qnode import QNode, qnode
from .set_shots import set_shots
Loading
Loading