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.38k stars 607 forks source link

[BUG] Quantum Fisher with Different Devices #5381

Closed sabrinaherbst closed 8 months ago

sabrinaherbst commented 8 months ago

Expected behavior

I was trying to calculate quantum fisher information matrices using different devices. As far as I understand the documentation (https://docs.pennylane.ai/en/stable/code/api/pennylane.qinfo.transforms.quantum_fisher.html), whether the function uses adjoint_metric_tensor and metric_tensor should automatically change depending on the exact hardware used and the respective available operations. I would expect the function to return the QFIM or at least a meaningful error message saying that the function is not supported with these devices for now.

Actual behavior

I get a MatrixUndefinedError when using a different simulator than default.qubit, which is what happens when you call adjoint_metric_tensor with any other simulator than default.qubit.

Additional information

No response

Source code

import pennylane as qml
from pennylane import numpy as np

from sklearn import datasets as ds

NUM_WIRES = 4

def get_circuit(data, parameters):
    qml.IQPEmbedding(data, wires=range(NUM_WIRES), n_repeats=1)
    for i in range(NUM_WIRES):
        qml.RX(parameters[i], wires=i)
    for j in range(NUM_WIRES-1):
        qml.CNOT(wires=[j, j+1])
    return qml.expval(qml.PauliZ(0))

# use different devices here, i.e. lightning.qubit, default.qubit, lightning.kokkos
dev = qml.device("default.mixed", wires=NUM_WIRES)

def qfim(X_train, parameters):
    circuit = qml.QNode(get_circuit, dev)
    data = np.array(X_train[0], requires_grad=False)
    return qml.qinfo.transforms.quantum_fisher(circuit)(data, parameters)

X = ds.load_iris().data
parameters = np.random.random(size=NUM_WIRES, requires_grad=True)
print(qfim(X, parameters))

Tracebacks

Traceback (most recent call last):
  File "/home/path/mwe_simulator.py", line 26, in <module>
    print(qfim(X, parameters))
  File "/home/path/mwe_simulator.py", line 22, in qfim
    return qml.adjoint_metric_tensor(circuit)(data, parameters)
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/workflow/qnode.py", line 1048, in __call__
    res = qml.execute(
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/workflow/execution.py", line 680, in execute
    return post_processing(tapes)
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/workflow/execution.py", line 677, in post_processing
    return program_post_processing(program_pre_processing(results))
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/transforms/core/transform_program.py", line 86, in _apply_postprocessing_stack
    results = postprocessing(results)
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/transforms/core/transform_program.py", line 56, in _batch_postprocessing
    return tuple(fn(results[sl]) for fn, sl in zip(individual_fns, slices))
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/transforms/core/transform_program.py", line 56, in <genexpr>
    return tuple(fn(results[sl]) for fn, sl in zip(individual_fns, slices))
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/gradients/adjoint_metric_tensor.py", line 184, in processing_fn
    psi = qml.devices.qubit.apply_operation(op, psi)
  File "/home/path/miniconda3/lib/python3.10/functools.py", line 889, in wrapper
    return dispatch(args[0].__class__)(*args, **kw)
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/devices/qubit/apply_operation.py", line 204, in apply_operation
    return _apply_operation_default(op, state, is_state_batched, debugger)
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/devices/qubit/apply_operation.py", line 215, in _apply_operation_default
    return apply_operation_tensordot(op, state, is_state_batched=is_state_batched)
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/devices/qubit/apply_operation.py", line 117, in apply_operation_tensordot
    mat = op.matrix()
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/operation.py", line 791, in matrix
    canonical_matrix = self.compute_matrix(*self.parameters, **self.hyperparameters)
  File "/home/path/venv/lib/python3.10/site-packages/pennylane/operation.py", line 760, in compute_matrix
    raise MatrixUndefinedError
pennylane.operation.MatrixUndefinedError

System information

Name: PennyLane
Version: 0.35.1
Summary: 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.
Home-page: https://github.com/PennyLaneAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /home/path/venv/lib/python3.10/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane_Lightning, PennyLane_Lightning_Kokkos

Platform info:           Linux-6.7.7-200.fc39.x86_64-x86_64-with-glibc2.38
Python version:          3.10.10
Numpy version:           1.26.2
Scipy version:           1.11.4
Installed devices:
- lightning.qubit (PennyLane_Lightning-0.35.1)
- default.clifford (PennyLane-0.35.1)
- default.gaussian (PennyLane-0.35.1)
- default.mixed (PennyLane-0.35.1)
- default.qubit (PennyLane-0.35.1)
- default.qubit.autograd (PennyLane-0.35.1)
- default.qubit.jax (PennyLane-0.35.1)
- default.qubit.legacy (PennyLane-0.35.1)
- default.qubit.tf (PennyLane-0.35.1)
- default.qubit.torch (PennyLane-0.35.1)
- default.qutrit (PennyLane-0.35.1)
- null.qubit (PennyLane-0.35.1)
- lightning.kokkos (PennyLane_Lightning_Kokkos-0.35.1)

Existing GitHub issues

trbromley commented 8 months ago

Thanks @sabrinaherbst! This does look like a potential bug which seems to be coming from the presence of qml.IQPEmbedding. We'll get back to you soon on the best way to resolve this.

albi3ro commented 8 months ago

So I'm a little confused now. I see in the documentation that we say:

 a device with finite shots is used, the hardware compatible transform :func:`~.pennylane.metric_tensor` is used.

but then is source-code we use the metric_tensor when:

if device.shots and isinstance(device, (DefaultQubitLegacy, DefaultQubit)):

AKA only when it's a numpy simulator. Potentially we just mean not isinstance instead?

josh146 commented 8 months ago

@albi3ro yeah seems like a bug/typo 🤔

Although perhaps it should be

if (device.shots is not None) or (device is not defaultqubit):

or equivalently

if not (device.shots is None and device is defaultqubit):

since the adjoint metric tensor can only be used if:

Also, treating device.shots (None or int) as a boolean could be problematic in edge cases?


On second thought, inverting the if statement would make this easier to read:

if device.shots is None and isinstance(device, defaultqubit):
    return adjoint_metric_tensor

return hw_compatible_metric_tensor