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 586 forks source link

PassthruQNode decomposition error with default.tensor.tf #620

Closed antalszava closed 4 years ago

antalszava commented 4 years ago

Issue description

from pennylane.qnodes import PassthruQNode

theta = 0.543 phi = -0.234 lam = 0.654 p = [theta, phi, lam]

dev = qml.device("default.tensor.tf", wires=1)

def circuit(weights, w=None): qml.U3(weights[0], weights[1], weights[2], wires=w) # <--- decomposition is required return qml.expval(qml.PauliX(w))

node = PassthruQNode(circuit, dev, interface="tf")

params = tf.Variable(p, dtype=tf.float64)

with tf.GradientTape() as tape: tape.watch(params) res = node(params, w=0)

Produces:
```python
~/pennylane/pennylane/operation.py in __init__(self, wires, do_queue, *params)
    328         if self.do_check_domain:
    329             for p in params:
--> 330                 self.check_domain(p)
    331         self.params = list(params)  #: list[Any]: parameters of the operator
    332 

~/pennylane/pennylane/operation.py in check_domain(self, p, flattened)
    425             if not isinstance(p, numbers.Real):
    426                 raise TypeError(
--> 427                     "{}: Real scalar parameter expected, got {}.".format(self.name, type(p))
    428                 )
    429 

TypeError: Rot: Real scalar parameter expected, got <class 'tensorflow.python.framework.ops.EagerTensor'>.

Additional information

Testing of the "classical" differentiation method proposed in #552 will slightly change once this issue is resolved. This PR works with gate decomposition due to the error.

josh146 commented 4 years ago

Good catch @antalszava! In the Operator class, we do some basic input validation of arguments. However, this won't work for PassthruQNodes, since they will no longer be using NumPy arrays/Python floats, but rather the default data structure for their target ML framework.

In the PassthruQNode, operation validation is turned off explicitly within the construct method:

def _construct(self, args, kwargs):
    """Construct the quantum circuit graph by calling the quantum function.

    Like :class:`.BaseQNode._construct`, but does not use Variables.
    """
    # temporary queues for operations and observables
    self.queue = []  #: list[Operation]: applied operations
    self.obs_queue = []  #: list[Observable]: applied observables

    # set up the context for Operator entry
    with self:
        try:
            # turn off domain checking since PassthruQNode qfuncs can take any class as input
            pennylane.operation.Operator.do_check_domain = False
            # generate the program queue by executing the quantum circuit function
            res = self.func(*args, **kwargs)
        finally:
            pennylane.operation.Operator.do_check_domain = True

My guess is that this seemingly doesn't propagate to decomposed operations, and it somehow gets turned on again?

josh146 commented 4 years ago

I think I have found the cause. The operation decomposition is performed during the self._circuit_check method call. So this just needs to be moved to within the try: block:


# set up the context for Operator entry
with self:
    try:
        # turn off domain checking since PassthruQNode qfuncs can take any class as input
        pennylane.operation.Operator.do_check_domain = False
        # generate the program queue by executing the quantum circuit function
        res = self.func(*args, **kwargs)
        # check the validity of the circuit
        self._check_circuit(res)
    finally:
        pennylane.operation.Operator.do_check_domain = True

del self.queue
del self.obs_queue
antalszava commented 4 years ago

Applying this seems to result in another one for the same example:

~/pennylane/pennylane/_queuing_context.py in append_operator(cls, operator)
     72         """
     73         for context in cls._active_contexts:
---> 74             context._append_operator(operator)  # pylint: disable=protected-access
     75 
     76     @abc.abstractmethod
~/pennylane/pennylane/qnodes/base.py in _append_operator(self, operator)
    329             if self.obs_queue:
    330                 raise QuantumFunctionError(
--> 331                     "State preparations and gates must precede measured observables."
    332                 )
    333             self.queue.append(operator)
QuantumFunctionError: State preparations and gates must precede measured observables.

This does not arise when using the decomposition explicitly.