Closed co9olguy closed 4 years ago
Note that if we use a real matrix
, the circuit executes without complaint
I think two things are happening here.
Ideally, we want the following behaviour:
Arrays/matrices should always be passed as auxiliary arguments, simply so pennylane knows they are not differentiable. This is independent of them becoming Variables
or not.
Arrays/matrices passed as positional arguments should still evaluate on the forward pass, but fail on the backward pass.
However, when arrays are converted to variables, there is an issue. In the base QNode: https://github.com/XanaduAI/pennylane/blob/f7ad64a39187a577ff33d1cf3abb57e345eb317a/pennylane/qnodes/base.py#L288
Due to NumPy casting rules, the presence of a single complex value will cause the entire array to become complex valued, even values that were originally real. This explains why this bug only affects circuits that contain QubitUnitary
with complex arrays and parametrized gates.
Question: is this a new issue, or has this always been present?
Possible solutions:
Enforce arrays always being passed as auxiliary arguments. Pro: easy. Cons: loss of flexibility
Do not use a numpy array to store variable values, to allow multiple types. Cons: ?
Cast gate parameters to floats, and only raise an error if the imaginary part is non-zero. Pro: easy, makes sense, gates already declare they require real parameters. Cons: ?
I think (3) is the best solution, alongside making it more clear to the user that arrays should always be auxiliary if they plan to do backpropagation/gradient computations.
I actually prefer solution (2), just have
Variable.positional_arg_values = list(_flatten(args)))
In that way, gates will still complain when the parameters are complex.
Note that passing arrays as parameters is totally within scope in pennylane (e.g. weights
for templates). In this particular case, the problem is that the parameter matrix
is used in a place where it can not be differentiated.
I'm curious now how much of an impact simply changing
Variable.positional_arg_values = list(_flatten(args)))
and running the tests will have.
(I'm using the primary/auxiliary parameter nomeclature here.)
The quantum circuits in PL are meant to be differentiable wrt. their primary parameters, which can always be treated as a flattenable nested sequence of real scalar values. So by design no complex values can appear as primary arguments. Furthermore, by design matrix-like parameters which are not differentiable should be auxiliary.
Note that this does not prevent passing real arrays as primary arguments (weights
in templates, for example).
I would not allow passing complex values to primary parameters even on the forward pass (evaluation only, no differentiation), since this is unnecessary and just confuses the concepts. If it works, the user will be twice as surprised when the differentation suddenly raises an error.
I think @johannesjmeyer 's idea might be good: use a list instead of an array. I thought np.ndarray
access was fundamentally faster than list access, but it seems that Python lists are actually implemented using some kind of dynamic array with O(1) access, so it's certainly worth testing. If there is no performance loss, it seems like a clean solution.
Issue description
Issue posted by user
max
on the forumsExpected behavior: The following circuit containing
QubitUnitary
should function without errors:Actual behavior: When provided with the matrix
np.array([[1, 0], [0, 0.70710678 + 0.70710678*1.j]])
, an exception is raised: "TypeError: RX: Real scalar parameter expected, got <class 'numpy.complex128'>."Reproduces how often: Seems to happen no matter what gate is put before
QubitUnitary
, and no matter how many gates there are in between the first andQubitUnitary
. When the preceding gate has no parameterized argument, circuit executes without error.System information: Name: PennyLane Version: 0.9.0.dev0 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/nathan/dev/pennylane Requires: numpy, scipy, networkx, autograd, toml, appdirs, semantic-version Required-by: PennyLane-qiskit, PennyLane-Qchem, PennyLane-PQ, PennyLane-Forest Platform info: Linux-5.3.0-40-generic-x86_64-with-debian-buster-sid Python version: 3.7.6 Numpy version: 1.18.1 Scipy version: 1.4.1 Installed devices:
qiskit.aer (PennyLane-qiskit-0.8.0)
qiskit.basicaer (PennyLane-qiskit-0.8.0)
qiskit.ibmq (PennyLane-qiskit-0.8.0)
projectq.classical (PennyLane-PQ-0.8.0)
projectq.ibm (PennyLane-PQ-0.8.0)
projectq.simulator (PennyLane-PQ-0.8.0)
forest.numpy_wavefunction (PennyLane-Forest-0.8.0)
forest.qpu (PennyLane-Forest-0.8.0)
forest.qvm (PennyLane-Forest-0.8.0)
forest.wavefunction (PennyLane-Forest-0.8.0)
default.gaussian (PennyLane-0.9.0.dev0)
default.qubit (PennyLane-0.9.0.dev0)
default.tensor (PennyLane-0.9.0.dev0)
default.tensor.tf (PennyLane-0.9.0.dev0)
Source code and tracebacks
TypeError Traceback (most recent call last)