How do I operate between to different measurement processes for my loss function?

Hello! I’m trying to calculate a certain quantity for my loss function. This quantity involves operating between two different measurement processes. For example, my loss function is

S_x = (1/2)*sum([qml.PauliX(i) for i in range(qnn_layer.n_qubits)])
S_z = (1/2)*sum([qml.PauliZ(i) for i in range(qnn_layer.n_qubits)])

def get_loss_function(qnn_layer, ts, S_x, S_z, dev):

    @jax.jit
    @qml.qnode(dev, interface="jax")
    def loss(param_vector):

        # hamitlonian_params = dictionary_to_hamiltonian_parameters(params_dict)

        param_list = qnn_layer.vector_to_hamiltonian_parameters(param_vector)

        qml.evolve(qnn_layer.H)(param_list, ts)

        return (-1) * qnn_layer.n_qubits * (qml.var(S_z) / qml.expval(S_x))

    return loss

# Getting the loss_function
loss = get_loss_function(qnn_layer, ts, S_x, S_z, dev)

# Initializing parameters
param_vector  = qnn_layer.get_random_parameter_vector(seed)

print(f'Initial parameters: {param_vector}')

print(f'Initial loss: {loss(param_vector)}')

However, I get an error that tells me that I cannot operate (in this case, divide) between two different measurement processes, namely ‘VarianceMP’ and ‘ExpectationMP’.

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[17], line 9
      5 param_vector  = qnn_layer.get_random_parameter_vector(seed)
      7 print(f'Initial parameters: {param_vector}')
----> 9 print(f'Initial loss: {loss(param_vector)}')

    [... skipping hidden 11 frame]

File ~/.../pennylane/workflow/qnode.py:1164, in QNode.__call__(self, *args, **kwargs)
   1162 if qml.capture.enabled():
   1163     return qml.capture.qnode_call(self, *args, **kwargs)
-> 1164 return self._impl_call(*args, **kwargs)

File ~/.../pennylane/workflow/qnode.py:1144, in QNode._impl_call(self, *args, **kwargs)
   1141     override_shots = kwargs["shots"]
   1143 # construct the tape
-> 1144 self.construct(args, kwargs)
   1146 original_grad_fn = [self.gradient_fn, self.gradient_kwargs, self.device]
   1147 self._update_gradient_fn(shots=override_shots, tape=self._tape)

File ~/.../pennylane/logging/decorators.py:61, in log_string_debug_func.<locals>.wrapper_entry(*args, **kwargs)
     54     s_caller = "::L".join(
     55         [str(i) for i in inspect.getouterframes(inspect.currentframe(), 2)[1][1:3]]
     56     )
     57     lgr.debug(
     58         f"Calling {f_string} from {s_caller}",
     59         **_debug_log_kwargs,
     60     )
---> 61 return func(*args, **kwargs)

File ~/.../pennylane/workflow/qnode.py:966, in QNode.construct(self, args, kwargs)
    964 with pldb_device_manager(self.device):
    965     with qml.queuing.AnnotatedQueue() as q:
--> 966         self._qfunc_output = self.func(*args, **kwargs)
    968 self._tape = QuantumScript.from_queue(q, shots)
    970 params = self.tape.get_parameters(trainable_only=False)

Cell In[14], line 13
      9 param_list = qnn_layer.vector_to_hamiltonian_parameters(param_vector)
     11 qml.evolve(qnn_layer.H)(param_list, ts)
--> 13 return (-1) * qnn_layer.n_qubits * (qml.var(S_z) / qml.expval(S_x))

TypeError: unsupported operand type(s) for /: 'VarianceMP' and 'ExpectationMP'

How do I make this sort of quantity of interest feasible in my loss function? Thanks! My qml.about() is as follows:

Version: 0.37.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: /.../python3.11/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, packaging, pennylane-lightning, requests, rustworkx, scipy, semantic-version, toml, typing-extensions
Required-by: PennyLane_Lightning

Platform info:           macOS-14.5-x86_64-i386-64bit
Python version:          3.11.7
Numpy version:           1.26.4
Scipy version:           1.14.0
Installed devices:
- lightning.qubit (PennyLane_Lightning-0.37.0)
- default.clifford (PennyLane-0.37.0)
- default.gaussian (PennyLane-0.37.0)
- default.mixed (PennyLane-0.37.0)
- default.qubit (PennyLane-0.37.0)
- default.qubit.autograd (PennyLane-0.37.0)
- default.qubit.jax (PennyLane-0.37.0)
- default.qubit.legacy (PennyLane-0.37.0)
- default.qubit.tf (PennyLane-0.37.0)
- default.qubit.torch (PennyLane-0.37.0)
- default.qutrit (PennyLane-0.37.0)
- default.qutrit.mixed (PennyLane-0.37.0)
- default.tensor (PennyLane-0.37.0)
- null.qubit (PennyLane-0.37.0)

Hi @NickGut0711 ! :smile: That would be a great PL feature but for now this would be an alternative:

def get_loss_function():

    @qml.qnode(qml.device("default.qubit"))
    def circuit(param):

        qml.RX(param, wires = 0)

        return qml.var(qml.X(0)), qml.expval(qml.Z(0))

    def loss(param):
      var, exp = circuit(param)
      return var / exp

    return loss

I hope that helps!

1 Like

Hi @Guillermo_Alonso! Thank you so much for the prompt, and helpful, reply! I’ll try that now :slightly_smiling_face: