Exemplo n.º 1
0
    def __call__(self, *args, **kwargs):

        if self.interface == "autograd":
            # HOTFIX: to maintain compatibility with core, here we treat
            # all inputs that do not explicitly specify `requires_grad=False`
            # as trainable. This should be removed at some point, forcing users
            # to specify `requires_grad=True` for trainable parameters.
            args = [
                anp.array(a, requires_grad=True)
                if not hasattr(a, "requires_grad") else a for a in args
            ]

        # construct the tape
        self.construct(args, kwargs)

        # execute the tape
        res = self.qtape.execute(device=self.device)

        if isinstance(self.qfunc_output, Sequence):
            return res

        # HOTFIX: Output is a single measurement function. To maintain compatibility
        # with core, we squeeze all outputs.

        # Get the namespace associated with the return type
        res_type_namespace = res.__class__.__module__.split(".")[0]

        if res_type_namespace in ("pennylane", "autograd"):
            # For PennyLane and autograd we must branch, since
            # 'squeeze' does not exist in the top-level of the namespace
            return anp.squeeze(res)
        # Same for JAX
        if res_type_namespace == "jax":
            return __import__(res_type_namespace).numpy.squeeze(res)
        return __import__(res_type_namespace).squeeze(res)
Exemplo n.º 2
0
    def __call__(self, *args, **kwargs):

        if self.interface == "autograd":
            # HOTFIX: to maintain compatibility with core, here we treat
            # all inputs that do not explicitly specify `requires_grad=False`
            # as trainable. This should be removed at some point, forcing users
            # to specify `requires_grad=True` for trainable parameters.
            args = [
                anp.array(a, requires_grad=True)
                if not hasattr(a, "requires_grad") else a for a in args
            ]

        # construct the tape
        self.construct(args, kwargs)

        if self._caching:
            # Every time the QNode is called, it creates a new tape. We want the tape cache to
            # persist over multiple tapes, so hence keep track of it as a QNode attribute and
            # load it into the new tape
            self.qtape._cache_execute = self._cache_execute

        # execute the tape
        res = self.qtape.execute(device=self.device)

        # HOTFIX: to maintain compatibility with core, we squeeze
        # all outputs.

        # Get the namespace associated with the return type
        res_type_namespace = res.__class__.__module__.split(".")[0]

        if res_type_namespace in ("pennylane", "autograd"):
            # For PennyLane and autograd we must branch, since
            # 'squeeze' does not exist in the top-level of the namespace
            return anp.squeeze(res)

        if self._caching:
            self._cache_execute = self.qtape._cache_execute

        return __import__(res_type_namespace).squeeze(res)
Exemplo n.º 3
0
    def construct(self, args, kwargs):
        """Call the quantum function with a tape context, ensuring the operations get queued."""

        if self.interface == "autograd":
            # HOTFIX: to maintain compatibility with core, here we treat
            # all inputs that do not explicitly specify `requires_grad=False`
            # as trainable. This should be removed at some point, forcing users
            # to specify `requires_grad=True` for trainable parameters.
            args = [
                anp.array(a, requires_grad=True) if not hasattr(a, "requires_grad") else a
                for a in args
            ]

        self.qtape = self._tape()

        with self.qtape:
            self.qfunc_output = self.func(*args, **kwargs)

        if not isinstance(self.qfunc_output, Sequence):
            measurement_processes = (self.qfunc_output,)
        else:
            measurement_processes = self.qfunc_output

        if not all(isinstance(m, qml.tape.MeasurementProcess) for m in measurement_processes):
            raise qml.QuantumFunctionError(
                "A quantum function must return either a single measurement, "
                "or a nonempty sequence of measurements."
            )

        state_returns = any([m.return_type is State for m in measurement_processes])

        # apply the interface (if any)
        if self.diff_options["method"] != "backprop" and self.interface is not None:
            # pylint: disable=protected-access
            if state_returns and self.interface in ["torch", "tf"]:
                # The state is complex and we need to indicate this in the to_torch or to_tf
                # functions
                self.INTERFACE_MAP[self.interface](self, dtype=np.complex128)
            else:
                self.INTERFACE_MAP[self.interface](self)

        if not all(ret == m for ret, m in zip(measurement_processes, self.qtape.measurements)):
            raise qml.QuantumFunctionError(
                "All measurements must be returned in the order they are measured."
            )

        for obj in self.qtape.operations + self.qtape.observables:
            if getattr(obj, "num_wires", None) is qml.operation.WiresEnum.AllWires:
                # check here only if enough wires
                if len(obj.wires) != self.device.num_wires:
                    raise qml.QuantumFunctionError(
                        "Operator {} must act on all wires".format(obj.name)
                    )

        # provide the jacobian options
        self.qtape.jacobian_options = self.diff_options

        # pylint: disable=protected-access
        obs_on_same_wire = len(self.qtape._obs_sharing_wires) > 0
        ops_not_supported = any(
            isinstance(op, qml.tape.QuantumTape)  # nested tapes must be expanded
            or not self.device.supports_operation(op.name)  # unsupported ops must be expanded
            for op in self.qtape.operations
        )

        # expand out the tape, if nested tapes are present, any operations are not supported on the
        # device, or multiple observables are measured on the same wire
        if ops_not_supported or obs_on_same_wire:
            self.qtape = self.qtape.expand(
                depth=self.max_expansion,
                stop_at=lambda obj: not isinstance(obj, qml.tape.QuantumTape)
                and self.device.supports_operation(obj.name),
            )