Skip to content

Commit

Permalink
Deprecate QauntumScript.output_dim (#6577)
Browse files Browse the repository at this point in the history
**Context:**
This property is now slightly wrong, and is only used by
`parameter_shift_cv.py`.
**Description of the Change:**
- Deprecated the property in the original src,
`qml.tape.qscript.py::QuantumScript`
 - DeprecateWarning the tests in `test_qscript.py`
 - Removed the tests in `test_tape.py` and `test_legacy_device.py`
- Modified the original method and moved to
`qml.gradients.parameter_shift_cv.py` which is the only module using
this property

**Benefits:**
 - Less redundancy

**Possible Drawbacks:**
Potential uncovered usecases of this property inside or outside PL

**Related GitHub Issues:**

**Related Shortcut Stories:**
[sc-74594]

---------

Co-authored-by: Christina Lee <[email protected]>
Co-authored-by: lillian542 <[email protected]>
Co-authored-by: Alex Preciado <[email protected]>
  • Loading branch information
4 people authored Nov 14, 2024
1 parent 51c1436 commit a0e69cc
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 38 deletions.
5 changes: 5 additions & 0 deletions doc/development/deprecations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ deprecations are listed below.
Pending deprecations
--------------------

* The ``output_dim`` property of ``qml.tape.QuantumScript`` has been deprecated. Instead, use method ``shape`` of ``QuantumScript`` or ``MeasurementProcess`` to get the same information.

- Deprecated in v0.40
- Will be removed in v0.41

* The ``QNode.get_best_method`` and ``QNode.best_method_str`` methods have been deprecated.
Instead, use the ``qml.workflow.get_best_diff_method``.

Expand Down
5 changes: 5 additions & 0 deletions doc/releases/changelog-dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@

<h3>Deprecations 👋</h3>

* The `output_dim` property of `qml.tape.QuantumScript` has been deprecated.
Instead, use method `shape` of `QuantumScript` or `MeasurementProcess` to get the
same information.
[(#6577)](https://github.com/PennyLaneAI/pennylane/pull/6577)

* The `QNode.get_best_method` and `QNode.best_method_str` methods have been deprecated.
Instead, use the `qml.workflow.get_best_diff_method` function.
[(#6418)](https://github.com/PennyLaneAI/pennylane/pull/6418)
Expand Down
4 changes: 2 additions & 2 deletions pennylane/gradients/parameter_shift_cv.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ def processing_fn(results):
start = 0

if not results:
results = [np.squeeze(np.zeros([tape.output_dim]))]
results = [np.array(0.0)]

interface = qml.math.get_interface(results[0])
iterator = enumerate(zip(shapes, gradient_values, obs_indices))
Expand Down Expand Up @@ -726,7 +726,7 @@ def _update(data):
fns.append(data[1])

if all(g == "0" for g in method_map.values()):
return [], lambda _: np.zeros([tape.output_dim, len(tape.trainable_params)])
return [], lambda _: np.zeros([1, len(tape.trainable_params)])

var_present = any(isinstance(m, VarianceMP) for m in tape.measurements)

Expand Down
18 changes: 17 additions & 1 deletion pennylane/tape/qscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,23 @@ def batch_size(self) -> Optional[int]:

@property
def output_dim(self) -> int:
"""The (inferred) output dimension of the quantum script."""
"""The (inferred) output dimension of the quantum script.
.. warning::
``QuantumScript.output_dim`` is being deprecated. Instead, considering
using method ``shape`` of ``QuantumScript`` or ``MeasurementProcess``
to get the same information. See ``qml.gradients.parameter_shift_cv.py::_get_output_dim``
for an example.
"""
# pylint: disable=import-outside-toplevel
import warnings

warnings.warn(
"The 'output_dim' property is deprecated and will be removed in version 0.41",
qml.PennyLaneDeprecationWarning,
)
if self._output_dim is None:
self._update_output_dim() # this will set _batch_size if it isn't already
return self._output_dim
Expand Down
1 change: 0 additions & 1 deletion tests/devices/test_legacy_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,6 @@ def test_default_expand_with_initial_state(self, op, decomp):
assert new_tape.shots is tape.shots
assert new_tape.wires == tape.wires
assert new_tape.batch_size == tape.batch_size
assert new_tape.output_dim == tape.output_dim

def test_default_expand_fn_with_invalid_op(self, mock_device_supporting_paulis, recwarn):
"""Test that default_expand_fn works with an invalid op and some measurement."""
Expand Down
63 changes: 53 additions & 10 deletions tests/tape/test_qscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,11 @@ def test_error_inconsistent_batch_sizes(self, x, rot, y):
def test_update_output_dim(self, m, output_dim, ops, factor):
"""Test setting the output_dim property."""
qs = QuantumScript(ops, m)
assert qs.output_dim == output_dim * factor

with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert qs.output_dim == output_dim * factor

def test_lazy_batch_size_and_output_dim(self):
"""Test that batch_size and output_dim are computed lazily."""
Expand All @@ -347,7 +351,11 @@ def test_lazy_batch_size_and_output_dim(self):
# now evaluate it
assert qs.batch_size == 2
assert qs._output_dim is None # setting batch_size didn't set output_dim
assert qs.output_dim == 2

with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert qs.output_dim == 2
copied = qs.copy()
assert qs._batch_size == 2
assert qs._output_dim == 2
Expand All @@ -361,7 +369,10 @@ def test_lazy_setting_output_dim_sets_batch_size(self):
assert qs._batch_size is _UNSET_BATCH_SIZE
assert qs._output_dim is None

assert qs.output_dim == 2 # getting this sets both _output_dim and _batch_size
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert qs.output_dim == 2 # getting this sets both _output_dim and _batch_size
assert qs._output_dim == 2
assert qs._batch_size == 2

Expand Down Expand Up @@ -573,7 +584,10 @@ def test_shallow_copy(self):
assert qs.shots is copied_qs.shots

# check that the output dim is identical
assert qs.output_dim == copied_qs.output_dim
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert qs.output_dim == copied_qs.output_dim

# pylint: disable=unnecessary-lambda
@pytest.mark.parametrize(
Expand Down Expand Up @@ -608,7 +622,10 @@ def test_shallow_copy_with_operations(self, copy_fn):
assert qs.shots is copied_qs.shots

# check that the output dim is identical
assert qs.output_dim == copied_qs.output_dim
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert qs.output_dim == copied_qs.output_dim

def test_deep_copy(self):
"""Test that deep copying a tape works, and copies all constituent data except parameters"""
Expand All @@ -628,7 +645,10 @@ def test_deep_copy(self):
assert copied_qs.shots is qs.shots

# check that the output dim is identical
assert qs.output_dim == copied_qs.output_dim
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert qs.output_dim == copied_qs.output_dim

# The underlying operation data has also been copied
assert copied_qs.operations[0].wires is not qs.operations[0].wires
Expand Down Expand Up @@ -743,7 +763,10 @@ def test_cached_properties_when_updating_operations(self):
tape = QuantumScript(ops, measurements=[qml.counts()], shots=2500, trainable_params=[1])

assert tape.batch_size == 2
assert tape.output_dim == 2
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert tape.output_dim == 2
assert tape.trainable_params == [1]

new_ops = [qml.RX([1.2, 2.3, 3.4], 0)]
Expand All @@ -753,7 +776,10 @@ def test_cached_properties_when_updating_operations(self):
assert new_tape.operations == new_ops

assert new_tape.batch_size == 3
assert new_tape.output_dim == 3
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert new_tape.output_dim == 3
assert new_tape.trainable_params == [0]

def test_cached_properties_when_updating_measurements(self):
Expand All @@ -771,7 +797,10 @@ def test_cached_properties_when_updating_measurements(self):

assert tape.obs_sharing_wires == []
assert tape.obs_sharing_wires_id == []
assert tape.output_dim == 2
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert tape.output_dim == 2
assert tape.trainable_params == [1]

new_measurements = [qml.expval(qml.X(0)), qml.var(qml.Y(0))]
Expand All @@ -780,7 +809,10 @@ def test_cached_properties_when_updating_measurements(self):
assert tape.measurements == measurements
assert new_tape.measurements == new_measurements

assert new_tape.output_dim == 4
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
assert new_tape.output_dim == 4
assert new_tape.obs_sharing_wires == [qml.X(0), qml.Y(0)]
assert new_tape.obs_sharing_wires_id == [0, 1]
assert new_tape.trainable_params == [0, 1]
Expand Down Expand Up @@ -1702,3 +1734,14 @@ def test_jax_pytree_integration(qscript_type):
assert data[3] == 3.4
assert data[4] == 2.0
assert qml.math.allclose(data[5], eye_mat)


@pytest.mark.parametrize("qscript_type", (QuantumScript, qml.tape.QuantumTape))
@pytest.mark.parametrize("shots", [None, 1, 10])
def test_output_dim_is_deprecated(qscript_type, shots):
"""Test that the output_dim property is deprecated."""
with pytest.warns(
qml.PennyLaneDeprecationWarning, match="The 'output_dim' property is deprecated"
):
qscript = qscript_type([], [], shots=shots)
_ = qscript.output_dim
24 changes: 0 additions & 24 deletions tests/tape/test_tape.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,9 @@ def test_qubit_queuing(self, make_tape):
assert len(tape.queue) == 6
assert tape.operations == ops
assert tape.observables == obs
assert tape.output_dim == 5
assert tape.batch_size is None

assert tape.wires == qml.wires.Wires([0, "a", 4])
assert tape._output_dim == len(obs[0].wires) + 2 ** len(obs[1].wires)

def test_observable_processing(self, make_tape):
"""Test that observables are processed correctly"""
Expand Down Expand Up @@ -202,7 +200,6 @@ def test_tensor_process_queuing(self):
assert not tape.operations
assert tape.measurements == [D]
assert tape.observables == [C]
assert tape.output_dim == 1
assert tape.batch_size is None

def test_multiple_contexts(self):
Expand All @@ -225,7 +222,6 @@ def test_multiple_contexts(self):
assert len(tape.queue) == 4
assert tape.operations == ops
assert tape.observables == obs
assert tape.output_dim == 5
assert tape.batch_size is None

assert not any(qml.equal(a, op) or qml.equal(b, op) for op in tape.operations)
Expand Down Expand Up @@ -1245,7 +1241,6 @@ def test_execute_parameters(self, tol):
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0) @ qml.PauliX(1))

assert tape.output_dim == 1
assert tape.batch_size is None

# test execution with no parameters
Expand Down Expand Up @@ -1284,8 +1279,6 @@ def test_single_expectation_value(self, tol):
qml.CNOT(wires=[0, 1])
qml.expval(qml.PauliZ(0) @ qml.PauliX(1))

assert tape.output_dim == 1

res = dev.execute(tape)
assert res.shape == ()

Expand All @@ -1306,8 +1299,6 @@ def test_multiple_expectation_values(self, tol):
qml.expval(qml.PauliZ(0))
qml.expval(qml.PauliX(1))

assert tape.output_dim == 2

res = dev.execute(tape)
assert isinstance(res, tuple)
assert len(res) == 2
Expand All @@ -1329,8 +1320,6 @@ def test_var_expectation_values(self, tol):
qml.expval(qml.PauliZ(0))
qml.var(qml.PauliX(1))

assert tape.output_dim == 2

res = dev.execute(tape)
assert isinstance(res, tuple)
assert len(res) == 2
Expand All @@ -1353,8 +1342,6 @@ def test_prob_expectation_values(self, tol):
qml.expval(qml.PauliZ(0))
qml.probs(wires=[0, 1])

assert tape.output_dim == 5

res = dev.execute(tape)

assert isinstance(res, tuple)
Expand Down Expand Up @@ -1461,8 +1448,6 @@ def test_single_output_value(self):
qml.Beamsplitter(np.pi / 4, 0, wires=[0, 1])
qml.expval(qml.NumberOperator(0))

assert tape.output_dim == 1

res = dev.batch_execute([tape])[0]
assert res.shape == ()

Expand Down Expand Up @@ -1497,9 +1482,6 @@ def test_shallow_copy(self):
assert tape.wires == copied_tape.wires
assert tape.data == copied_tape.data

# check that the output dim is identical
assert tape.output_dim == copied_tape.output_dim

@pytest.mark.parametrize("copy_fn", [lambda tape: tape.copy(copy_operations=True), copy.copy])
def test_shallow_copy_with_operations(self, copy_fn):
"""Test that shallow copying of a tape and operations allows
Expand Down Expand Up @@ -1528,9 +1510,6 @@ def test_shallow_copy_with_operations(self, copy_fn):
assert tape.wires == copied_tape.wires
assert tape.data == copied_tape.data

# check that the output dim is identical
assert tape.output_dim == copied_tape.output_dim

def test_deep_copy(self):
"""Test that deep copying a tape works, and copies all constituent data except parameters"""
with QuantumTape() as tape:
Expand All @@ -1548,9 +1527,6 @@ def test_deep_copy(self):
assert all(o1 is not o2 for o1, o2 in zip(copied_tape.observables, tape.observables))
assert all(m1 is not m2 for m1, m2 in zip(copied_tape.measurements, tape.measurements))

# check that the output dim is identical
assert tape.output_dim == copied_tape.output_dim

# The underlying operation data has also been copied
assert copied_tape.operations[0].wires is not tape.operations[0].wires

Expand Down

0 comments on commit a0e69cc

Please sign in to comment.