Precautions to take when switching to an IBM device

I have code that runs on the default.qubit without problem, but when I switch to a ibm_kyoto, I get what seems to be a decomposition error. This is an example of my the circuit I am trying to run.

def QuantumLayer():

    n_qubits =2
    dev = qml.device("qiskit.ibmq", wires=n_qubits, backend='ibm_kyoto', ibmqx_token = XXX)
    
    def _circuit(inputs, weights):
        qml.Hadamard(wires=[0])
        qml.AngleEmbedding(features=(inputs*3*torch.pi/4), wires=range(1,n_qubits), rotation='Y')
        qml.ctrl(qml.adjoint(qml.AngleEmbedding(features=(inputs*3*torch.pi/4), wires=range(1,n_qubits), rotation='Y')),control=[0])

        for layer in range(1):
            for i in range(1, n_qubits):
                qml.CRY(phi=weights[layer][i-1][0],wires=[0,i])
        
        qml.Hadamard(wires=[0])
        return qml.expval(qml.PauliZ(wires=0))
        
    qlayer = qml.QNode(_circuit, dev, interface="torch")
    weight_shapes = {"weights": (vqc_layers,n_qubits-1, 1)}
    return qml.qnn.TorchLayer(qlayer, weight_shapes)

My error:

Traceback (most recent call last):
  File "QCNN.py", line 290, in <module>
    train_and_evaluate(quantum_model, quantum_optimizer, train_dataloader, test_dataloader, 'Quantum', device, criterion, num_epochs, model_save_dir, current_datetime)
  File "QCNN.py", line 226, in train_and_evaluate
    outputs = model(images)
  File ".../lib/site-packages/torch/nn/modules/module.py", line 1518, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
  File ".../lib/site-packages/torch/nn/modules/module.py", line 1527, in _call_impl
    return forward_call(*args, **kwargs)
  File "models.py", line 17, in forward
    x = self.quantum_layer(x)
  File ".../lib/site-packages/torch/nn/modules/module.py", line 1518, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
  File ".../lib/site-packages/torch/nn/modules/module.py", line 1527, in _call_impl
    return forward_call(*args, **kwargs)
  File "modules.py", line 183, in forward
    QL_output = selected_quantum_layer(x_features_unique[:, self.input_data_per_circuit*j:self.input_data_per_circuit*(j+1)])
  File ".../lib/site-packages/torch/nn/modules/module.py", line 1518, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
  File ".../lib/site-packages/torch/nn/modules/module.py", line 1527, in _call_impl
    return forward_call(*args, **kwargs)
  File ".../lib/site-packages/pennylane/qnn/torch.py", line 402, in forward
    results = self._evaluate_qnode(inputs)
  File ".../lib/site-packages/pennylane/qnn/torch.py", line 423, in _evaluate_qnode
    res = self.qnode(**kwargs)
  File ".../lib/site-packages/pennylane/qnode.py", line 1027, in __call__
    res = qml.execute(
  File ".../lib/site-packages/pennylane/interfaces/execution.py", line 736, in execute
    results = ml_boundary_execute(
  File ".../lib/site-packages/pennylane/interfaces/torch.py", line 314, in execute
    return ExecuteTapes.apply(kwargs, *parameters)
  File ".../lib/site-packages/pennylane/interfaces/torch.py", line 45, in new_apply
    flat_out = orig_apply(out_struct_holder, *inp)
  File ".../lib/site-packages/torch/autograd/function.py", line 539, in apply
    return super().apply(*args, **kwargs)  # type: ignore[misc]
  File ".../lib/site-packages/pennylane/interfaces/torch.py", line 49, in new_forward
    out = orig_fw(ctx, *inp)
  File ".../lib/site-packages/pennylane/interfaces/torch.py", line 145, in forward
    res, ctx.jacs = ctx.execute_fn(ctx.tapes, **ctx.gradient_kwargs)
  File ".../lib/site-packages/pennylane/interfaces/execution.py", line 588, in inner_execute_with_empty_jac
    return (inner_execute(tapes), [])
  File ".../lib/site-packages/pennylane/interfaces/execution.py", line 246, in inner_execute
    tapes = tuple(expand_fn(t) for t in tapes)
  File ".../lib/site-packages/pennylane/interfaces/execution.py", line 246, in <genexpr>
    tapes = tuple(expand_fn(t) for t in tapes)
  File ".../lib/site-packages/pennylane/interfaces/execution.py", line 206, in device_expansion_function
    return device.expand_fn(tape, max_expansion=max_expansion)
  File ".../lib/site-packages/pennylane/_device.py", line 718, in expand_fn
    return self.default_expand_fn(circuit, max_expansion=max_expansion)
  File ".../lib/site-packages/pennylane/_device.py", line 689, in default_expand_fn
    circuit = _local_tape_expand(
  File ".../lib/site-packages/pennylane/_device.py", line 90, in _local_tape_expand
    expanded_tape = _local_tape_expand(obj, stop_at=stop_at, depth=depth - 1)
  File ".../lib/site-packages/pennylane/_device.py", line 84, in _local_tape_expand
    obj = QuantumScript(obj.decomposition(), _update=False)
  File ".../lib/site-packages/pennylane/ops/op_math/controlled.py", line 502, in decomposition
    decomp = _decompose_no_control_values(self)
  File ".../lib/site-packages/pennylane/ops/op_math/controlled.py", line 599, in _decompose_no_control_values
    return ctrl_decomp_zyz(op.base, op.control_wires)
  File ".../lib/site-packages/pennylane/ops/op_math/controlled_decompositions.py", line 200, in ctrl_decomp_zyz
    if not qml.math.isclose(theta / 2, 0.0, atol=1e-8, rtol=0):
  File ".../lib/site-packages/autoray/autoray.py", line 80, in do
    return get_lib_fn(backend, fn)(*args, **kwargs)
TypeError: isclose(): argument 'other' (position 2) must be Tensor, not float

Hello,
try updating the versions, I don’t get this error.

!pip install --upgrade pennylane
!pip install --upgrade qiskit

Thanks, unfortunately I am already on the latest versions of both pennylane (0.34.0) and qiskit (0.45.2) and just installed the pennylane-qiskit plugin.

That is unusual, on what platform are you working? I ran this code in an IBM Quantum Lab notebook, without errors.

Hi @Anthony_Smaldone , thanks for reaching out! I took a quick look at your problem, but unfortunately the code example you’ve shared isn’t complete enough for me to run it and see if I get a similar error.

Do you have an example of what you are running to generate the error message, as well as any imports or definitions (for example, how you have defined vqc_layers)? A complete but minimal example that will run and produce the error message is the easiest way for us to help. Thanks!

@Lillian_Frederiksen Thank you for taking a look, sorry for the delay. I built a very simple model for demonstration, this time trying to use the honeywell device. I believe the problem is with controlled gate decompositions.

import pennylane as qml
from pennylane_honeywell import HQSDevice
import torch
import torch.nn as nn
import numpy as np

def AngleQuantumLayer():
    dev = HQSDevice(wires=3, machine='H1-E1', shots=1024, user_email="MYEMAIL", access_token=None, refresh_token=None, retry_delay=2)
    
    def _circuit(inputs, weights):
        qml.Hadamard(wires=[0])
        
        qml.AngleEmbedding(features=(inputs*torch.pi), wires=[1], rotation='Y')
        qml.ctrl(qml.adjoint(qml.AngleEmbedding(features=(inputs*torch.pi), wires=[1], rotation='Y')),control=[0])
        qml.CNOT(wires=[0,1])
        qml.ctrl(qml.Hadamard, control=0)(wires=2)
        qml.ctrl(qml.CRX(phi=weights,wires=[1,2]),control=[0])

        qml.Hadamard(wires=[0])
        return qml.expval(qml.PauliZ(wires=0))
        
    qlayer = qml.QNode(_circuit, dev, interface="torch")
    weight_shapes = {"weights": (1)}
    return qml.qnn.TorchLayer(qlayer, weight_shapes)

class SimpleQuantumModel(nn.Module):
    def __init__(self):
        super(SimpleQuantumModel, self).__init__()
        self.quantum_layer = AngleQuantumLayer()

    def forward(self, x):
        return self.quantum_layer(x)

# Example usage
model = SimpleQuantumModel()
features = torch.tensor([[0.0],[1.0]], dtype=torch.float32, requires_grad=True)

output = model(features)
print(output)

Error message:

TypeError                                 Traceback (most recent call last)

<ipython-input-6-6662ce711f35> in <cell line: 17>()
     15 print(features.shape)
     16 print(features)
---> 17 output = model(features)
     18 print(output)

23 frames

/usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _wrapped_call_impl(self, *args, **kwargs)
   1516             return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1517         else:
-> 1518             return self._call_impl(*args, **kwargs)
   1519 
   1520     def _call_impl(self, *args, **kwargs):

/usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs)
   1525                 or _global_backward_pre_hooks or _global_backward_hooks
   1526                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1527             return forward_call(*args, **kwargs)
   1528 
   1529         try:

<ipython-input-6-6662ce711f35> in forward(self, x)
      6 
      7     def forward(self, x):
----> 8         return self.quantum_layer(x)
      9 
     10 # Example usage

/usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _wrapped_call_impl(self, *args, **kwargs)
   1516             return self._compiled_call_impl(*args, **kwargs)  # type: ignore[misc]
   1517         else:
-> 1518             return self._call_impl(*args, **kwargs)
   1519 
   1520     def _call_impl(self, *args, **kwargs):

/usr/local/lib/python3.10/dist-packages/torch/nn/modules/module.py in _call_impl(self, *args, **kwargs)
   1525                 or _global_backward_pre_hooks or _global_backward_hooks
   1526                 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1527             return forward_call(*args, **kwargs)
   1528 
   1529         try:

/usr/local/lib/python3.10/dist-packages/pennylane/qnn/torch.py in forward(self, inputs)
    400 
    401         # calculate the forward pass as usual
--> 402         results = self._evaluate_qnode(inputs)
    403 
    404         # reshape to the correct number of batch dims

/usr/local/lib/python3.10/dist-packages/pennylane/qnn/torch.py in _evaluate_qnode(self, x)
    421             **{arg: weight.to(x) for arg, weight in self.qnode_weights.items()},
    422         }
--> 423         res = self.qnode(**kwargs)
    424 
    425         if isinstance(res, torch.Tensor):

/usr/local/lib/python3.10/dist-packages/pennylane/qnode.py in __call__(self, *args, **kwargs)
   1037 
   1038         # pylint: disable=unexpected-keyword-arg
-> 1039         res = qml.execute(
   1040             (self._tape,),
   1041             device=self.device,

/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/execution.py in execute(tapes, device, gradient_fn, interface, transform_program, config, grad_on_execution, gradient_kwargs, cache, cachesize, max_diff, override_shots, expand_fn, max_expansion, device_batch_transform, device_vjp)
    806 
    807     if interface in jpc_interfaces:
--> 808         results = ml_boundary_execute(tapes, execute_fn, jpc, device=device)
    809     else:
    810         results = ml_boundary_execute(

/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/torch.py in execute(tapes, execute_fn, jpc, device)
    233     }
    234 
--> 235     return ExecuteTapes.apply(kwargs, *parameters)
    236 
    237 

/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/torch.py in new_apply(*inp)
     88         # Inputs already flat
     89         out_struct_holder = []
---> 90         flat_out = orig_apply(out_struct_holder, *inp)
     91         return pytree.tree_unflatten(flat_out, out_struct_holder[0])
     92 

/usr/local/lib/python3.10/dist-packages/torch/autograd/function.py in apply(cls, *args, **kwargs)
    537             # See NOTE: [functorch vjp and autograd interaction]
    538             args = _functorch.utils.unwrap_dead_wrappers(args)
--> 539             return super().apply(*args, **kwargs)  # type: ignore[misc]
    540 
    541         if cls.setup_context == _SingleLevelFunction.setup_context:

/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/torch.py in new_forward(ctx, out_struct_holder, *inp)
     92 
     93     def new_forward(ctx, out_struct_holder, *inp):
---> 94         out = orig_fw(ctx, *inp)
     95         flat_out, out_struct = pytree.tree_flatten(out)
     96         ctx._out_struct = out_struct

/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/torch.py in forward(ctx, kwargs, *parameters)
    157         ctx.jpc = kwargs["jpc"]
    158 
--> 159         res = tuple(kwargs["execute_fn"](ctx.tapes))
    160 
    161         # if any input tensor uses the GPU, the output should as well

/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/execution.py in inner_execute(tapes, **_)
    256         """
    257         if expand_fn:
--> 258             tapes = tuple(expand_fn(t) for t in tapes)
    259         if numpy_only:
    260             tapes = tuple(qml.transforms.convert_to_numpy_parameters(t) for t in tapes)

/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/execution.py in <genexpr>(.0)
    256         """
    257         if expand_fn:
--> 258             tapes = tuple(expand_fn(t) for t in tapes)
    259         if numpy_only:
    260             tapes = tuple(qml.transforms.convert_to_numpy_parameters(t) for t in tapes)

/usr/local/lib/python3.10/dist-packages/pennylane/interfaces/execution.py in device_expansion_function(tape)
    216     def device_expansion_function(tape):  # pylint: disable=function-redefined
    217         """A wrapper around the device ``expand_fn``."""
--> 218         return device.expand_fn(tape, max_expansion=max_expansion)
    219 
    220     return device_expansion_function

/usr/local/lib/python3.10/dist-packages/pennylane/_device.py in expand_fn(self, circuit, max_expansion)
    716             return self.custom_expand_fn(circuit, max_expansion=max_expansion)
    717 
--> 718         return self.default_expand_fn(circuit, max_expansion=max_expansion)
    719 
    720     def batch_transform(self, circuit: QuantumTape):

/usr/local/lib/python3.10/dist-packages/pennylane/_device.py in default_expand_fn(self, circuit, max_expansion)
    687 
    688         elif ops_not_supported:
--> 689             circuit = _local_tape_expand(
    690                 circuit, depth=max_expansion, stop_at=self.stopping_condition
    691             )

/usr/local/lib/python3.10/dist-packages/pennylane/_device.py in _local_tape_expand(tape, depth, stop_at)
     83                 if obj.has_decomposition:
     84                     with QueuingManager.stop_recording():
---> 85                         obj = QuantumScript(obj.decomposition(), _update=False)
     86                 else:
     87                     new_queue.append(obj)

/usr/local/lib/python3.10/dist-packages/pennylane/ops/op_math/controlled.py in decomposition(self)
    531     def decomposition(self):
    532         if all(self.control_values):
--> 533             decomp = _decompose_no_control_values(self)
    534             if decomp is None:
    535                 raise qml.operation.DecompositionUndefinedError

/usr/local/lib/python3.10/dist-packages/pennylane/ops/op_math/controlled.py in _decompose_no_control_values(op)
    628         if len(op.control_wires) >= 2 and qmlmath.get_interface(*op.data) == "numpy":
    629             return ctrl_decomp_bisect(op.base, op.control_wires)
--> 630         return ctrl_decomp_zyz(op.base, op.control_wires)
    631 
    632     if not op.base.has_decomposition:

/usr/local/lib/python3.10/dist-packages/pennylane/ops/op_math/controlled_decompositions.py in ctrl_decomp_zyz(target_operation, control_wires)
    196     if not qml.math.isclose(phi, 0.0, atol=1e-8, rtol=0):
    197         decomp.append(qml.RZ(phi, wires=target_wire))
--> 198     if not qml.math.isclose(theta / 2, 0.0, atol=1e-8, rtol=0):
    199         decomp.extend(
    200             [

/usr/local/lib/python3.10/dist-packages/autoray/autoray.py in do(fn, like, *args, **kwargs)
     78     """
     79     backend = choose_backend(fn, *args, like=like, **kwargs)
---> 80     return get_lib_fn(backend, fn)(*args, **kwargs)
     81 
     82 

TypeError: isclose(): argument 'other' (position 2) must be Tensor, not float

Thanks @Anthony_Smaldone , that was very helpful! It looks like you’ve found a bug with the decomposition when using PyTorch parameters. I’ve got a bug-fix up for it now.

This fix should make it out in v0.35, released at the start of March. If you would like to access it sooner, then you can install the development version of PennyLane using:

pip install git+https://github.com/PennyLaneAI/pennylane.git#egg=pennylane

It won’t be available in the development version until the link above is marked as “merged”, which should happen in the next few days. Note that the development version might have a few rough edges that need to be resolved before release, so proceed with caution!

1 Like