Repository: PennyLaneAI/pennylane

Issue #944: qml.qnn.KerasLayer does not support backprop in non-tape mode
Created: 2020-11-30T20:24:01Z
Issue description
Hey, me again, just noticed the following:
dev = qml.device('default.qubit.tf', wires=2)

@qml.qnode(dev, interface='tf')
def circuit(inputs, weights):
    qml.RX(inputs, wires=0)
    qml.Rot(*weights)
    return qml.expval(qml.PauliZ(0))
Now, when you try:
>>> qlayer = qml.qnn.KerasLayer(circuit, weight_shapes, output_dim=1)
AttributeError: 'PassthruQNode' object has no attribute '_dtype'
Notably, this only happens when specifying interface='tf' in the qnode decorator.


Expected behavior: (What you expect to happen)
N/A


Actual behavior: (What actually happens)
N/A


Reproduces how often: (What percentage of the time does it reproduce?)
Always.


System information: (post the output of import pennylane as qml; qml.about())
Name: PennyLane
Version: 0.13.0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author: None
Author-email: None
License: Apache License 2.0
Location: /home/iamlucaswolf/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages
Requires: semantic-version, appdirs, autograd, numpy, toml, scipy, networkx
Required-by:
Platform info:           Linux-5.4.0-53-generic-x86_64-with-glibc2.27
Python version:          3.8.6
Numpy version:           1.18.5
Scipy version:           1.5.4
Installed devices:



default.gaussian (PennyLane-0.13.0)
default.mixed (PennyLane-0.13.0)
default.qubit (PennyLane-0.13.0)
default.qubit.autograd (PennyLane-0.13.0)
default.qubit.tf (PennyLane-0.13.0)
default.tensor (PennyLane-0.13.0)
default.tensor.tf (PennyLane-0.13.0)

Source code and tracebacks
Full stacktrace:

AttributeError                            Traceback (most recent call last)
 in 
----> 1 qlayer = qml.qnn.KerasLayer(circuit, weight_shapes, output_dim=1)
~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/qnn/keras.py in init(self, qnode, weight_shapes, output_dim, weight_specs, **kwargs)
215         else:
216             self._signature_validation(qnode, weight_shapes)
--> 217             self.qnode = to_tf(qnode, dtype=tf.keras.backend.floatx())
218
219         # Allows output_dim to be specified as an int, e.g., 5, or as a length-1 tuple, e.g., (5,)
~/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages/pennylane/interfaces/tf.py in to_tf(qnode, dtype)
79     qnode_interface = getattr(qnode, "interface", None)
80
---> 81     if qnode_interface == "tf" and dtype == qnode._dtype:  # pylint: disable=protected-access
82         return qnode
83
AttributeError: 'PassthruQNode' object has no attribute '_dtype'
Additional information
N/A

Duplicate issue #945: KerasLayer.__call__ throws errors
Created: 2020-11-30T23:17:12Z
Last issue for today, promise! 😅
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?


Expected behavior: (What you expect to happen)
N/A


Actual behavior: (What actually happens)
N/A


Reproduces how often: (What percentage of the time does it reproduce?)
Always.


System information: (post the output of import pennylane as qml; qml.about())
Name: PennyLane
Version: 0.13.0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author: None
Author-email: None
License: Apache License 2.0
Location: /home/iamlucaswolf/.cache/pypoetry/virtualenvs/quantum-rl-eji9J1nt-py3.8/lib/python3.8/site-packages
Requires: semantic-version, appdirs, autograd, numpy, toml, scipy, networkx
Required-by:
Platform info: Linux-5.4.0-53-generic-x86_64-with-glibc2.27
Python version: 3.8.6
Numpy version: 1.18.5
Scipy version: 1.5.4
Installed devices:
default.gaussian (PennyLane-0.13.0)
default.mixed (PennyLane-0.13.0)
default.qubit (PennyLane-0.13.0)
default.qubit.autograd (PennyLane-0.13.0)
default.qubit.tf (PennyLane-0.13.0)
default.tensor (PennyLane-0.13.0)
default.tensor.tf (PennyLane-0.13.0)


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).

Closing comment (2020-12-09T16:49:04Z): @iamlucaswolf since this issue is similar to #944, I might close this one in favour of the other one.