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.27k stars 585 forks source link

KerasLayer.__call__ throws errors #945

Closed iamlucaswolf closed 3 years ago

iamlucaswolf commented 3 years ago

Last issue for today, promise! :sweat_smile:

Issue description

So I have my little circuit that I run on a DefaultQubitTF device...

device = qml.device('default.qubit.tf', wires=4)

def layer(theta):
    >>> do stuff on those wires <<<

@qml.qnode(device)
def circuit(inputs, thetas):

    for idx, bit in enumerate(inputs):
        qml.RX(np.pi * bit, wires=idx)
        qml.RZ(np.pi * bit, wires=idx)

    for theta in thetas:
        layer(theta)

    return qml.probs(range(4))

... and executing the circuit works just fine.

>>> circuit(np.array([1,0,0,0]), np.random.randn(2, 4, 3))
array([0.04326995, 0.04985228, 0.01675847, 0.01612908, 0.00691995,
       0.00795971, 0.05612402, 0.05462552, 0.16421624, 0.18976849,
       0.08500942, 0.08153118, 0.00468652, 0.00521023, 0.11034525,
       0.1075937 ])

But once I wrap this in a KerasLayer:

>>> layer = qml.qnn.KerasLayer(circuit, weight_shapes={'thetas': (2, 4, 3)}, output_dim=4)

# (1) NumPy without batch dimension
>>> layer(np.array([1,0,0,0]))
TypeError: 'Variable' object is not iterable

# (2) NumPy with batch dimension
>>> layer(np.array([[1,0,0,0]]))
ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type Variable).

The same happens when using tf.constant instead of np.array as input data. I also restarted the Jupyter kernel between all these calls to make sure that the layer is in a valid state.

Am I missing something here?

Source code and tracebacks

Stacktrace from (1)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-caaccc4673d4> in <module>
----> 1 layer(tf.constant([1,0,0,0]))

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/keras/engine/base_layer.py in __call__(self, *args, **kwargs)
    983 
    984         with ops.enable_auto_cast_variables(self._compute_dtype_object):
--> 985           outputs = call_fn(inputs, *args, **kwargs)
    986 
    987         if self._activity_regularizer:

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnn/keras.py in call(self, inputs)
    329                         else:
    330                             qnode = functools.partial(qnode, x)
--> 331                 outputs.append(qnode())
    332 
    333         return tf.stack(outputs)

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/ops/custom_gradient.py in __call__(self, *a, **k)
    262 
    263   def __call__(self, *a, **k):
--> 264     return self._d(self._f, a, k)
    265 
    266 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/ops/custom_gradient.py in decorated(wrapped, args, kwargs)
    216 
    217     if context.executing_eagerly():
--> 218       return _eager_mode_decorator(wrapped, args, kwargs)
    219     else:
    220       return _graph_mode_decorator(wrapped, args, kwargs)

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/ops/custom_gradient.py in _eager_mode_decorator(f, args, kwargs)
    410   """Implement custom gradient decorator for eager mode."""
    411   with tape_lib.VariableWatcher() as variable_watcher:
--> 412     result, grad_fn = f(*args, **kwargs)
    413   args = nest.flatten(args)
    414   all_inputs = list(args) + list(kwargs.values())

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/interfaces/tf.py in _TFQNode(*input_, **input_kwargs)
    162         # evaluate the QNode
    163         qnode.set_trainable_args(trainable_args)
--> 164         res = qnode(*args, **kwargs)
    165 
    166         if not isinstance(res, np.ndarray):

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/interfaces/autograd.py in __call__(self, *args, **kwargs)
     67             self.set_trainable(args)
     68             args = autograd.builtins.tuple(args)  # pylint: disable=no-member
---> 69             return self.evaluate(args, kwargs)
     70 
     71         @staticmethod

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/autograd/tracer.py in f_wrapped(*args, **kwargs)
     46             return new_box(ans, trace, node)
     47         else:
---> 48             return f_raw(*args, **kwargs)
     49     f_wrapped.fun = f_raw
     50     f_wrapped._is_autograd_primitive = True

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnodes/base.py in evaluate(self, args, kwargs)
    848 
    849         if self.circuit is None or self.mutable:
--> 850             self._construct(args, kwargs)
    851 
    852         self.device.reset()

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnodes/jacobian.py in _construct(self, args, kwargs)
     87         for each positional parameter.
     88         """
---> 89         super()._construct(args, kwargs)
     90         self.par_to_grad_method = {k: self._best_method(k) for k in self.variable_deps}
     91 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnodes/base.py in _construct(self, args, kwargs)
    590                         # re-constructed each time (positional args must be replaced because
    591                         # parameter-shift differentiation requires Variables)
--> 592                         res = self.func(*self.arg_vars, **kwargs)
    593                     else:
    594                         # TODO: Maybe we should only convert the kwarg_vars that were actually given

<ipython-input-4-6f5592bab365> in circuit(inputs, thetas)
      2 def circuit(inputs, thetas):
      3 
----> 4     for idx, bit in enumerate(inputs):
      5         qml.RX(np.pi * bit, wires=idx)
      6         qml.RZ(np.pi * bit, wires=idx)

TypeError: 'Variable' object is not iterable

Stacktrace from (2)

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-7-6d9d3ee02dab> in <module>
----> 1 layer(tf.constant([[1,0,0,0]]))

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/keras/engine/base_layer.py in __call__(self, *args, **kwargs)
    983 
    984         with ops.enable_auto_cast_variables(self._compute_dtype_object):
--> 985           outputs = call_fn(inputs, *args, **kwargs)
    986 
    987         if self._activity_regularizer:

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnn/keras.py in call(self, inputs)
    329                         else:
    330                             qnode = functools.partial(qnode, x)
--> 331                 outputs.append(qnode())
    332 
    333         return tf.stack(outputs)

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/ops/custom_gradient.py in __call__(self, *a, **k)
    262 
    263   def __call__(self, *a, **k):
--> 264     return self._d(self._f, a, k)
    265 
    266 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/ops/custom_gradient.py in decorated(wrapped, args, kwargs)
    216 
    217     if context.executing_eagerly():
--> 218       return _eager_mode_decorator(wrapped, args, kwargs)
    219     else:
    220       return _graph_mode_decorator(wrapped, args, kwargs)

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/ops/custom_gradient.py in _eager_mode_decorator(f, args, kwargs)
    410   """Implement custom gradient decorator for eager mode."""
    411   with tape_lib.VariableWatcher() as variable_watcher:
--> 412     result, grad_fn = f(*args, **kwargs)
    413   args = nest.flatten(args)
    414   all_inputs = list(args) + list(kwargs.values())

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/interfaces/tf.py in _TFQNode(*input_, **input_kwargs)
    162         # evaluate the QNode
    163         qnode.set_trainable_args(trainable_args)
--> 164         res = qnode(*args, **kwargs)
    165 
    166         if not isinstance(res, np.ndarray):

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/interfaces/autograd.py in __call__(self, *args, **kwargs)
     67             self.set_trainable(args)
     68             args = autograd.builtins.tuple(args)  # pylint: disable=no-member
---> 69             return self.evaluate(args, kwargs)
     70 
     71         @staticmethod

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/autograd/tracer.py in f_wrapped(*args, **kwargs)
     46             return new_box(ans, trace, node)
     47         else:
---> 48             return f_raw(*args, **kwargs)
     49     f_wrapped.fun = f_raw
     50     f_wrapped._is_autograd_primitive = True

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnodes/base.py in evaluate(self, args, kwargs)
    848 
    849         if self.circuit is None or self.mutable:
--> 850             self._construct(args, kwargs)
    851 
    852         self.device.reset()

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnodes/jacobian.py in _construct(self, args, kwargs)
     87         for each positional parameter.
     88         """
---> 89         super()._construct(args, kwargs)
     90         self.par_to_grad_method = {k: self._best_method(k) for k in self.variable_deps}
     91 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnodes/base.py in _construct(self, args, kwargs)
    590                         # re-constructed each time (positional args must be replaced because
    591                         # parameter-shift differentiation requires Variables)
--> 592                         res = self.func(*self.arg_vars, **kwargs)
    593                     else:
    594                         # TODO: Maybe we should only convert the kwarg_vars that were actually given

<ipython-input-4-6f5592bab365> in circuit(inputs, thetas)
      7 
      8     for theta in thetas:
----> 9         layer(theta)
     10 
     11     return qml.probs(range(4))

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/keras/engine/base_layer.py in __call__(self, *args, **kwargs)
    931     # Accept NumPy and scalar inputs by converting to Tensors.
    932     if any(isinstance(x, (np.ndarray, float, int)) for x in input_list):
--> 933       inputs = nest.map_structure(_convert_numpy_or_python_types, inputs)
    934       input_list = nest.flatten(inputs)
    935 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/util/nest.py in map_structure(func, *structure, **kwargs)
    633 
    634   return pack_sequence_as(
--> 635       structure[0], [func(*x) for x in entries],
    636       expand_composites=expand_composites)
    637 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/util/nest.py in <listcomp>(.0)
    633 
    634   return pack_sequence_as(
--> 635       structure[0], [func(*x) for x in entries],
    636       expand_composites=expand_composites)
    637 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/keras/engine/base_layer.py in _convert_numpy_or_python_types(x)
   3237 def _convert_numpy_or_python_types(x):
   3238   if isinstance(x, (np.ndarray, float, int)):
-> 3239     return ops.convert_to_tensor_v2(x)
   3240   return x
   3241 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/framework/ops.py in convert_to_tensor_v2(value, dtype, dtype_hint, name)
   1375     ValueError: If the `value` is a tensor not of given `dtype` in graph mode.
   1376   """
-> 1377   return convert_to_tensor(
   1378       value=value,
   1379       dtype=dtype,

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/framework/ops.py in convert_to_tensor(value, dtype, name, as_ref, preferred_dtype, dtype_hint, ctx, accepted_result_types)
   1497 
   1498     if ret is None:
-> 1499       ret = conversion_func(value, dtype=dtype, name=name, as_ref=as_ref)
   1500 
   1501     if ret is NotImplemented:

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/framework/tensor_conversion_registry.py in _default_conversion_function(***failed resolving arguments***)
     50 def _default_conversion_function(value, dtype, name, as_ref):
     51   del as_ref  # Unused.
---> 52   return constant_op.constant(value, dtype, name=name)
     53 
     54 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/framework/constant_op.py in constant(value, dtype, shape, name)
    261     ValueError: if called on a symbolic tensor.
    262   """
--> 263   return _constant_impl(value, dtype, shape, name, verify_shape=False,
    264                         allow_broadcast=True)
    265 

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/framework/constant_op.py in _constant_impl(value, dtype, shape, name, verify_shape, allow_broadcast)
    273       with trace.Trace("tf.constant"):
    274         return _constant_eager_impl(ctx, value, dtype, shape, verify_shape)
--> 275     return _constant_eager_impl(ctx, value, dtype, shape, verify_shape)
    276 
    277   g = ops.get_default_graph()

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/framework/constant_op.py in _constant_eager_impl(ctx, value, dtype, shape, verify_shape)
    298 def _constant_eager_impl(ctx, value, dtype, shape, verify_shape):
    299   """Implementation of eager constant."""
--> 300   t = convert_to_eager_tensor(value, ctx, dtype)
    301   if shape is None:
    302     return t

~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/tensorflow/python/framework/constant_op.py in convert_to_eager_tensor(value, ctx, dtype)
     96       dtype = dtypes.as_dtype(dtype).as_datatype_enum
     97   ctx.ensure_initialized()
---> 98   return ops.EagerTensor(value, ctx.device_name, dtype)
     99 
    100 

ValueError: Failed to convert a NumPy array to a Tensor (Unsupported object type Variable).
josh146 commented 3 years ago

Last issue for today, promise! 😅

No need to worry --- issue reporting is great, it's improving the library one issue at a time 🙂

This issue is actually very related to #944. What is happening here, is that in non-tape mode, we do not support TF backprop in non-tape mode. Switching over to tape mode, and a similar model to your example now runs:

import pennylane as qml
import tensorflow as tf
import numpy as np

tf.keras.backend.set_floatx('float64')

qml.enable_tape()

device = qml.device('default.qubit.tf', wires=4)

@qml.qnode(device, interface="tf", diff_method="backprop")
def circuit(inputs, thetas):
    for idx, bit in enumerate(inputs):
        qml.RX(np.pi * bit, wires=idx)
        qml.RZ(np.pi * bit, wires=idx)

    for theta in thetas:
        qml.RX(theta[0], wires=0)
        qml.RX(theta[1], wires=1)
        qml.RY(theta[2], wires=2)
        qml.RZ(theta[3], wires=3)
        qml.CNOT(wires=[0, 1])
        qml.CNOT(wires=[1, 2])
        qml.CNOT(wires=[2, 3])
        qml.CNOT(wires=[3, 0])

    return [qml.expval(qml.PauliZ(i)) for i in range(4)]

layer = qml.qnn.KerasLayer(circuit, weight_shapes={'thetas': (2, 4)}, output_dim=4)

with tf.GradientTape() as tape:
    res = layer(tf.constant([[1, 0, 0, 0]], dtype=tf.float64))

grad = tape.gradient(res, layer.trainable_weights)

print("Output:", res)
print("Gradient:", grad)

This gives output

Output: tf.Tensor([[-0.58071214 -0.624736    0.48711172 -0.40601237]], shape=(1, 4), dtype=float64)
Gradient: [<tf.Tensor: shape=(2, 4), dtype=float64, numpy=
array([[ 5.03389399e-02, -1.17089383e-02, -5.86374458e-01,
         5.55111512e-17],
       [-3.53413082e-01, -6.29945412e-01,  5.84276547e-01,
         0.00000000e+00]])>]
josh146 commented 3 years ago

@iamlucaswolf since this issue is similar to #944, I might close this one in favour of the other one.