Issue with QFIM for time evolved mixed state

Hi PennyLane Team,

I am attempting to calculate the Quantum Fisher Information (QFI) for a mixed state. I have prepared a Bell state and introduced a phase damping channel on both qubits. I have encoded certain theta parameters into the noisy Bell state through a time evolution governed by a Hamiltonian.
However, I am encountering an error message when attempting to calculate the QFI. In the absence of noise, the calculation is successful, but the presence of noise leads to an error. I suspect that the ApproxTimeEvolution function is causing this issue, because I realised if I just encode theta by a simple parametric rotation that solves the problem.

Could you please assist me in identifying the error and providing a solution?

import pennylane as qml
from pennylane import numpy as pnp
import numpy as np

dev = qml.device("default.mixed", wires=3)

# Parameter values
theta_values = pnp.array([np.pi / 4, np.pi / 2], requires_grad=True)  # One theta per qubit
coeff_z_values = pnp.array([[1.], [1.]],requires_grad=False)  # One Z coefficient per qubit

@qml.qnode(dev)
def bell_state_circuit(theta_values, coeff_z_values, p):
    """
    Quantum circuit to generate a 2-qubit Bell state and encode Theta by a time evolution according to a Hamiltonian.
    """
    # Apply Hadamard gate to the first qubit
    qml.Hadamard(wires=0)

    # Apply CNOT gate to entangle qubits
    qml.CNOT(wires=[0, 1])

    # Step 2: Apply Phase Damping (Dephasing) Noise
    qml.PhaseDamping(p, wires=[0])  # Apply phase damping to qubit 0
    qml.PhaseDamping(p, wires=[1])  # Apply phase damping to qubit 1

    #Encoding dynamics
    for i in range(2):
        # Starting wire of the i-th qubit
        start_wire = i

        # Get parameters for this qubit
        theta = theta_values[i]
        coeff_z = coeff_z_values[i]

        # Construct Hamiltonians 
        H_Z = qml.Hamiltonian(coeff_z, [qml.PauliZ(start_wire + j) for j in range(1)])
        
        # Apply time evolution: exp(-i theta * H_Z) and exp(-i H_X)
        qml.ApproxTimeEvolution(H_Z, theta, 1)  # Theta scales only H_Z

    # Return state

    return qml.density_matrix(wires=[0, 1])

qfi_matrix = qml.gradients.quantum_fisher(bell_state_circuit)(theta_values, coeff_z_values, 0.1)
print(qfi_matrix)

This is the full error message:

/usr/local/lib/python3.11/dist-packages/pennylane/transforms/decompose.py:337: UserWarning: Operator PhaseDamping does not define a decomposition and was not found in the target gate set. To remove this warning, add the operator name (PhaseDamping) or type (<class 'pennylane.ops.channel.PhaseDamping'>) to the gate set.
  warnings.warn(
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-103-804e5894b083> in <cell line: 0>()
     39     return qml.density_matrix(wires=[0, 1])
     40 
---> 41 qfi_matrix = qml.gradients.quantum_fisher(bell_state_circuit)(theta_values, coeff_z_values, 0.1)
     42 print(qfi_matrix)

11 frames
/usr/local/lib/python3.11/dist-packages/numpy/core/numeric.py in tensordot(a, b, axes)
   1097                 axes_b[k] += ndb
   1098     if not equal:
-> 1099         raise ValueError("shape-mismatch for sum")
   1100 
   1101     # Move the axes to sum over to the end of "a"

ValueError: shape-mismatch for sum

The PennyLane version:

Name: PennyLane
Version: 0.40.0
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: /usr/local/lib/python3.11/dist-packages
Requires: appdirs, autograd, autoray, cachetools, diastatic-malt, networkx, numpy, packaging, pennylane-lightning, requests, rustworkx, scipy, tomlkit, typing-extensions
Required-by: PennyLane_Lightning

Platform info:           Linux-6.1.85+-x86_64-with-glibc2.35
Python version:          3.11.11
Numpy version:           1.26.4
Scipy version:           1.14.1
Installed devices:
- lightning.qubit (PennyLane_Lightning-0.40.0)
- default.clifford (PennyLane-0.40.0)
- default.gaussian (PennyLane-0.40.0)
- default.mixed (PennyLane-0.40.0)
- default.qubit (PennyLane-0.40.0)
- default.qutrit (PennyLane-0.40.0)
- default.qutrit.mixed (PennyLane-0.40.0)
- default.tensor (PennyLane-0.40.0)
- null.qubit (PennyLane-0.40.0)
- reference.qubit (PennyLane-0.40.0)

Hi @a.rozgonyi96,

We currently have some issues with metric_tensor (see PennyLane issue #7072), which is used for QFIM under the hood. I’m thinking this might be the cause for the behaviour you’re seeing but I’m not sure. I’ll let you know if I have any more info but for now my best guess is that you’ll need to wait for a fix.

Thank you for pointing this out. I’ll follow the discussion on it.

Hi @a.rozgonyi96 ,

Unfortunately it does look like the cause is the issue with metric_tensor :cry: . We’ll keep you updated on any progress but this is a tricky one so it might take a while.

Hi @CatalinaAlbornoz ,
Thank you for letting me know. I would appreciate any further hints that might potentially help me solve my question.

Hi @a.rozgonyi96 ,

Unfortunately at the moment I don’t know of any alternatives. However our team is working on fixing this issue and hopefully it will be ready in the next few weeks.

Are you able to advance by working without noise in the meantime?

1 Like

Thank you @CatalinaAlbornoz ! Of course, no worries, I appreciate your help.

1 Like

Dear @CatalinaAlbornoz ,

I’d like to kindly ask if you have any improvements in this matter? Could you suggest an alternative method for time evolution, apart from ApproxTimeEvolution?

Hi @a.rozgonyi96 , thank you for following up.

Unfortunately our team hasn’t been able to resolve this bug yet so it won’t make it to next week’s PennyLane release.

I’ve let the team know that you’re still waiting for the fix, so this is good feedback that can help us prioritize this bug.

1 Like

Hi @a.rozgonyi96 ,

I wanted to let you know that the metric_tensor issue was closed last week! :partying_face:
Would you please be able to test your code with the master version of PennyLane and let us know if you’re still experiencing issues?

Thanks!

1 Like

Dear @CatalinaAlbornoz ,

Thank you for reaching out. I’m using the updated version of PennyLane. However, it doesn’t solve the error "ValueError: shape-mismatch for sum” that arises during the calculation of the Quantum Fisher Information (QFI) matrix using qml.gradients.quantum_fisher.

However, I’ve added diff_method=“parameter-shift” to the @qml.qnode decorator, and then the QFIM got successfully computed.

dev = qml.device("default.mixed", wires=3)

# Parameter values
num_qubit = 2 
theta_values = pnp.array([np.pi / 4, np.pi / 2], requires_grad=True)  # One theta per qubit
p_value = pnp.array(0.5, requires_grad=False)
TOTAL_EVOLUTION = 1.0  # total evolution parameter
TROTTER_STEPS = 1    # number of Trotter steps for the approximation

@qml.qnode(dev, diff_method="parameter-shift")
def bell_state_circuit(theta_values, p):
    """
    Quantum circuit to generate a 2-qubit Bell state and a time evolution according to a Hamiltonian.
    """
    # Apply Hadamard gate to the first qubit
    qml.Hadamard(wires=0)

    # Apply CNOT gate to entangle qubits
    qml.CNOT(wires=[0, 1])

    # Step 2: Apply Phase Damping (Dephasing) Noise
    qml.PhaseDamping(p, wires=[0])  # Apply phase damping to qubit 0
    qml.PhaseDamping(p, wires=[1])  # Apply phase damping to qubit 1

    #Encoding dynamics
    for i in range(num_qubit):
        # Starting wire of the i-th qubit
        start_wire = i

        # Get parameters for this qubit
        theta = theta_values[i]

        # Construct Hamiltonians 
        H_Z = qml.Hamiltonian([theta], [qml.PauliZ(start_wire)])
        H_X = qml.Hamiltonian([theta], [qml.PauliX(start_wire)])
        
        # Apply time evolution: exp(-i * H_Z + H_X)
        qml.ApproxTimeEvolution(H_Z+H_X, TOTAL_EVOLUTION, TROTTER_STEPS) 
        #qml.adjoint(qml.TrotterProduct(H_Z+H_X, time=TOTAL_EVOLUTION, order=1, n=TROTTER_STEPS))   
    
    # Return state
    return qml.density_matrix(wires=[0, 1])

qfi_matrix = qml.gradients.quantum_fisher(bell_state_circuit)(theta_values, p_value)

For some reason, it only works if I set Trotter_steps=1; in any other case, if it is greater than 1, it fails and produces the following error message:

/usr/local/lib/python3.11/dist-packages/pennylane/transforms/decompose.py:684: UserWarning: Operator PhaseDamping does not define a decomposition and was not found in the target gate set. To remove this warning, add the operator name (PhaseDamping) or type (<class 'pennylane.ops.channel.PhaseDamping'>) to the gate set.
  warnings.warn(
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-31-d1ceb20beb92> in <cell line: 0>()
----> 1 qfi_matrix = qml.gradients.quantum_fisher(bell_state_circuit)(theta_values, p_value)
      2 print(qfi_matrix)

12 frames
/usr/local/lib/python3.11/dist-packages/pennylane/circuit_graph.py in ancestors(self, ops, sort)
    262             )
    263         if any(len(self._inds_for_objs[WrappedObj(op)]) > 1 for op in ops):
--> 264             raise ValueError(
    265                 "Cannot calculate ancestors for an operator that occurs multiple times."
    266             )

ValueError: Cannot calculate ancestors for an operator that occurs multiple times.

Also, I tried using the TrotterProduct function, but it fails as well, even for a single trotter step. (See the commented line in the code, below ApproxTimeEvolution). It produces the original “shape-mismatch for sum” error message again.

The PennyLane version:

Name: PennyLane
Version: 0.41.0
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: /usr/local/lib/python3.11/dist-packages
Requires: appdirs, autograd, autoray, cachetools, diastatic-malt, networkx, numpy, packaging, pennylane-lightning, requests, rustworkx, scipy, tomlkit, typing-extensions
Required-by: PennyLane_Lightning

Platform info:           Linux-6.1.123+-x86_64-with-glibc2.35
Python version:          3.11.12
Numpy version:           2.0.2
Scipy version:           1.14.1
Installed devices:
- lightning.qubit (PennyLane_Lightning-0.41.0)
- default.clifford (PennyLane-0.41.0)
- default.gaussian (PennyLane-0.41.0)
- default.mixed (PennyLane-0.41.0)
- default.qubit (PennyLane-0.41.0)
- default.qutrit (PennyLane-0.41.0)
- default.qutrit.mixed (PennyLane-0.41.0)
- default.tensor (PennyLane-0.41.0)
- null.qubit (PennyLane-0.41.0)
- reference.qubit (PennyLane-0.41.0)

Could you suggest a method for calculating QFIM for more trotter steps?
Thank you for your help in advance!

Oh no, thanks for letting us know about this @a.rozgonyi96 .

I’ve shared this with the team to investigate.

This is just a wild guess, but what happens if you use shots? Do the errors change or are they still the same?

Dear @CatalinaAlbornoz ,

Using finite shots while setting trotter_steps=2 still generates the same error message.

Thanks for confirming @a.rozgonyi96 .

It looks like there may be multiple issues involved here.
My colleague David opened this PR which should fix the original main issue. You can try using that branch of PennyLane to see if this solves your problem. Let me know in case you don’t know how to use a specific branch.

On the other hand this error seems to be unrelated.

ValueError: Cannot calculate ancestors for an operator that occurs multiple times.

Are you able to share a smaller (but self-contained) code that reproduces the same issue?

Thanks @CatalinaAlbornoz . There is a smaller version (no underlying physical meaning). For me it seems the applied kind of diff_method causes the error for trotter steps being set greater than 1.

dev = qml.device("default.mixed", wires=3)

# Parameter values
theta_values = pnp.array([np.pi / 4], requires_grad=True)
TROTTER_STEPS = 2    # number of Trotter steps for the approximation

@qml.qnode(dev, diff_method="parameter-shift")
def circuit(theta_values):
    
    # Construct Hamiltonians 
    H_Z = qml.Hamiltonian([theta_values[0]], [qml.PauliZ(0)])
        
    # Apply time evolution: exp(-i * H_Z)
    qml.ApproxTimeEvolution(H_Z, time=1, n=TROTTER_STEPS)   
    
    # Return state
    return qml.density_matrix(wires=[0, 1])

qfi_matrix = qml.gradients.quantum_fisher(circuit)(theta_values)
print(qfi_matrix)

Recap: I had to set diff_method=“parameter-shift” or 'finite-diff' to solve the

“shape-mismatch for sum”

issue related to using ApproxTimeEvolution and PhaseDamping simultaneously. However, when I set the hyperparameter n>1, I encounter the

“Cannot calculate ancestors for an operator that occurs multiple times.”

Thanks for sharing this @a.rozgonyi96 .

My colleagues are reworking the fix for the original issue. If this gets fixed it means you may be able to avoid using diff_method=“parameter-shift”, and this may solve the other problem. We’ll keep you updated soon.

1 Like

Hi @a.rozgonyi96 ,

I just wanted to add a reference to the GitHub issue and the Pull Request that should fix the original issue. The PR is still under review but you can already use the branch to see if this solves your original problem. Let us know if the solution does or doesn’t work for you, it can help us adapt the solution.