PennyLaneAI / pennylane

PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
https://pennylane.ai
Apache License 2.0
2.34k stars 602 forks source link

[BUG] Issue with expval of SparseH not building correct sized matrix #3272

Closed mlxd closed 2 years ago

mlxd commented 2 years ago

Expected behavior

The following example should run to completion:

import pennylane as qml
dev = qml.device("default.qubit", wires=4)

@qml.qnode(dev, diff_method="parameter-shift")
def circuit(p):
    qml.PauliX(0)
    return qml.expval(qml.SparseHamiltonian( qml.utils.sparse_hamiltonian( 1.1 * qml.PauliX(wires=0) @ qml.PauliZ(wires=2) ), wires=range(4),))
circuit()

Actual behavior

The internal representation does not build a sparse H of the correct size for application to the dense statevector:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In [1], line 8
      6     qml.PauliX(0)
      7     return qml.expval(qml.SparseHamiltonian( qml.utils.sparse_hamiltonian( 1.1 * qml.PauliX(wires=0) @ qml.PauliZ(wires=2) ), wires=range(4),))
----> 8 circuit()

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/qnode.py:661, in QNode.__call__(self, *args, **kwargs)
    657             res = tuple(res)
    659     return res
--> 661 res = qml.execute(
    662     [self.tape],
    663     device=self.device,
    664     gradient_fn=self.gradient_fn,
    665     interface=self.interface,
    666     gradient_kwargs=self.gradient_kwargs,
    667     override_shots=override_shots,
    668     **self.execute_kwargs,
    669 )
    671 if autograd.isinstance(res, (tuple, list)) and len(res) == 1:
    672     # If a device batch transform was applied, we need to 'unpack'
    673     # the returned tuple/list to a float.
   (...)
    680     # TODO: find a more explicit way of determining that a batch transform
    681     # was applied.
    683     res = res[0]

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/interfaces/execution.py:443, in execute(tapes, device, gradient_fn, interface, mode, gradient_kwargs, cache, cachesize, max_diff, override_shots, expand_fn, max_expansion, device_batch_transform)
    437 except ImportError as e:
    438     raise qml.QuantumFunctionError(
    439         f"{mapped_interface} not found. Please install the latest "
    440         f"version of {mapped_interface} to enable the '{mapped_interface}' interface."
    441     ) from e
--> 443 res = _execute(
    444     tapes, device, execute_fn, gradient_fn, gradient_kwargs, _n=1, max_diff=max_diff, mode=_mode
    445 )
    447 return batch_fn(res)

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/interfaces/autograd.py:66, in execute(tapes, device, execute_fn, gradient_fn, gradient_kwargs, _n, max_diff, mode)
     60 # pylint misidentifies autograd.builtins as a dict
     61 # pylint: disable=no-member
     62 parameters = autograd.builtins.tuple(
     63     [autograd.builtins.list(t.get_parameters()) for t in tapes]
     64 )
---> 66 return _execute(
     67     parameters,
     68     tapes=tapes,
     69     device=device,
     70     execute_fn=execute_fn,
     71     gradient_fn=gradient_fn,
     72     gradient_kwargs=gradient_kwargs,
     73     _n=_n,
     74     max_diff=max_diff,
     75 )[0]

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/autograd/tracer.py:48, in primitive.<locals>.f_wrapped(*args, **kwargs)
     46     return new_box(ans, trace, node)
     47 else:
---> 48     return f_raw(*args, **kwargs)

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/interfaces/autograd.py:110, in _execute(parameters, tapes, device, execute_fn, gradient_fn, gradient_kwargs, _n, max_diff)
     89 """Autodifferentiable wrapper around ``Device.batch_execute``.
     90 
     91 The signature of this function is designed to work around Autograd restrictions.
   (...)
    107 understand the consequences!
    108 """
    109 with qml.tape.Unwrap(*tapes):
--> 110     res, jacs = execute_fn(tapes, **gradient_kwargs)
    112 for i, r in enumerate(res):
    114     if any(
    115         m.return_type in (qml.measurements.Counts, qml.measurements.AllCounts)
    116         for m in tapes[i].measurements
    117     ):

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/interfaces/execution.py:197, in cache_execute.<locals>.wrapper(tapes, **kwargs)
    193         return (res, []) if return_tuple else res
    195 else:
    196     # execute all unique tapes that do not exist in the cache
--> 197     res = fn(execution_tapes.values(), **kwargs)
    199 final_res = []
    201 for i, tape in enumerate(tapes):

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/interfaces/execution.py:122, in cache_execute.<locals>.fn(tapes, **kwargs)
    120 def fn(tapes, **kwargs):  # pylint: disable=function-redefined
    121     tapes = [expand_fn(tape) for tape in tapes]
--> 122     return original_fn(tapes, **kwargs)

File /usr/lib/python3.10/contextlib.py:79, in ContextDecorator.__call__.<locals>.inner(*args, **kwds)
     76 @wraps(func)
     77 def inner(*args, **kwds):
     78     with self._recreate_cm():
---> 79         return func(*args, **kwds)

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/_qubit_device.py:586, in QubitDevice.batch_execute(self, circuits)
    583     self.reset()
    585     # TODO: Insert control on value here
--> 586     res = self.execute(circuit)
    587     results.append(res)
    589 if self.tracker.active:

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/_qubit_device.py:330, in QubitDevice.execute(self, circuit, **kwargs)
    328     results = self._collect_shotvector_results(circuit, counts_exist)
    329 else:
--> 330     results = self.statistics(circuit.observables, circuit=circuit)
    332 if not circuit.is_sampled:
    334     if len(circuit.measurements) == 1:

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/_qubit_device.py:740, in QubitDevice.statistics(self, observables, shot_range, bin_size, circuit)
    736 for obs in observables:
    737     # Pass instances directly
    738     if obs.return_type is Expectation:
    739         # Appends a result of shape (num_bins,) if bin_size is not None, else a scalar
--> 740         results.append(self.expval(obs, shot_range=shot_range, bin_size=bin_size))
    742     elif obs.return_type is Variance:
    743         # Appends a result of shape (num_bins,) if bin_size is not None, else a scalar
    744         results.append(self.var(obs, shot_range=shot_range, bin_size=bin_size))

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/pennylane/devices/default_qubit.py:595, in DefaultQubit.expval(self, observable, shot_range, bin_size)
    583         res = qml.math.array(
    584             [
    585                 csr_matrix.dot(
   (...)
    590             ]
    591         )
    592     else:
    593         res = csr_matrix.dot(
    594             csr_matrix(self._conj(state)),
--> 595             csr_matrix.dot(Hmat, csr_matrix(state[..., None])),
    596         ).toarray()[0]
    598 if observable.name == "Hamiltonian":
    599     res = qml.math.squeeze(res)

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/scipy/sparse/_base.py:416, in spmatrix.dot(self, other)
    414     return self * other
    415 else:
--> 416     return self @ other

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/scipy/sparse/_base.py:630, in spmatrix.__matmul__(self, other)
    627 if isscalarlike(other):
    628     raise ValueError("Scalar operands are not allowed, "
    629                      "use '*' instead")
--> 630 return self._mul_dispatch(other)

File /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages/scipy/sparse/_base.py:540, in spmatrix._mul_dispatch(self, other)
    538 if issparse(other):
    539     if self.shape[1] != other.shape[0]:
--> 540         raise ValueError('dimension mismatch')
    541     return self._mul_sparse_matrix(other)
    543 # If it's a list or whatever, treat it like a matrix

ValueError: dimension mismatch

Additional information

This is in the most recent released and master branches of PennyLane.

Source code

No response

Tracebacks

No response

System information

Name: PennyLane
Version: 0.26.0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /tmp/pennylane-lightning-gpu/pyenv/lib/python3.10/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, retworkx, scipy, semantic-version, toml
Required-by: PennyLane-Lightning

Platform info:           Linux-5.19.13-arch1-1-x86_64-with-glibc2.36
Python version:          3.10.7
Numpy version:           1.23.4
Scipy version:           1.9.3
Installed devices:
- lightning.qubit (PennyLane-Lightning-0.26.1)
- default.gaussian (PennyLane-0.26.0)
- default.mixed (PennyLane-0.26.0)
- default.qubit (PennyLane-0.26.0)
- default.qubit.autograd (PennyLane-0.26.0)
- default.qubit.jax (PennyLane-0.26.0)
- default.qubit.tf (PennyLane-0.26.0)
- default.qubit.torch (PennyLane-0.26.0)
- default.qutrit (PennyLane-0.26.0)

Existing GitHub issues

albi3ro commented 2 years ago

If wires aren't passed to qml.utils.sparse_hamiltonian, it assumes that the only wires are those in the Hamiltonian, which in this case is only two.

Providing wires to that function fixes the problem:

import pennylane as qml
dev = qml.device("default.qubit", wires=4)

Hmat = qml.utils.sparse_hamiltonian( 1.1 * qml.PauliX(wires=0) @ qml.PauliZ(wires=2) , wires=(0,1,2,3))

@qml.qnode(dev, diff_method="parameter-shift")
def circuit():
    qml.PauliX(0)
    return qml.expval(qml.SparseHamiltonian(Hmat, wires=(0,1,2,3)))
circuit()

I'll add some validation in qml.SparseHamiltonian to check for this kind of error earlier.

albi3ro commented 2 years ago

Now validated.