Ejemplo n.º 1
0
    def _apply_qubit_state_vector(self, qubit_state_vector_operation):
        # pylint: disable=missing-function-docstring
        if not self.analytic:
            raise qml.DeviceError(
                "The operation QubitStateVector is only supported in analytic mode."
            )

        state_vector = np.array(qubit_state_vector_operation.parameters[0], dtype=np.complex64)

        if len(state_vector) != 2 ** len(self.qubits):
            raise qml.DeviceError(
                "For QubitStateVector, the state has to be specified for the correct number of qubits. Got a state of length {}, expected {}.".format(
                    len(state_vector), 2 ** len(self.qubits)
                )
            )

        norm_squared = np.sum(np.abs(state_vector) ** 2)
        if not np.isclose(norm_squared, 1.0, atol=1e-3, rtol=0):
            raise qml.DeviceError(
                "The given state for QubitStateVector is not properly normalized to 1.0. Got norm {}".format(
                    math.sqrt(norm_squared)
                )
            )

        self._initial_state = state_vector
Ejemplo n.º 2
0
    def _apply_basis_state(self, basis_state_operation):
        # pylint: disable=missing-function-docstring
        if not self.shots is None:
            raise qml.DeviceError(
                "The operation BasisState is only supported in analytic mode.")

        wires = basis_state_operation.wires

        if len(basis_state_operation.parameters[0]) != len(wires):
            raise qml.DeviceError(
                "For BasisState, the state has to be specified for the correct number of qubits. Got a state for {} qubits, expected {}."
                .format(len(basis_state_operation.parameters[0]),
                        len(self.qubits)))

        if not np.all(
                np.isin(basis_state_operation.parameters[0], np.array([0, 1
                                                                       ]))):
            raise qml.DeviceError(
                "Argument for BasisState can only contain 0 and 1. Got {}".
                format(basis_state_operation.parameters[0]))

        # expand basis state to device wires
        basis_state_array = np.zeros(self.num_wires, dtype=int)
        basis_state_array[wires] = basis_state_operation.parameters[0]

        self._initial_state = np.zeros(2**len(self.qubits), dtype=np.complex64)
        basis_state_idx = np.sum(
            2**np.argwhere(np.flip(basis_state_array) == 1))
        self._initial_state[basis_state_idx] = 1.0
Ejemplo n.º 3
0
    def apply(self, operation, wires, par):
        super().apply(operation, wires, par)

        if operation == "BasisState":
            if not self._first_apply:
                raise qml.DeviceError(
                    "The operation BasisState is only supported at the beginning of a circuit."
                )

            if not self.analytic:
                raise qml.DeviceError(
                    "The operation BasisState is only supported in analytic mode."
                )

            basis_state_array = np.array(par[0])

            if len(basis_state_array) != len(self.qubits):
                raise qml.DeviceError(
                    "For BasisState, the state has to be specified for the correct number of qubits. Got a state for {} qubits, expected {}."
                    .format(len(basis_state_array), len(self.qubits)))

            if not np.all(np.isin(basis_state_array, np.array([0, 1]))):
                raise qml.DeviceError(
                    "Argument for BasisState can only contain 0 and 1. Got {}".
                    format(par[0]))

            self.initial_state = np.zeros(2**len(self.qubits),
                                          dtype=np.complex64)
            basis_state_idx = np.sum(
                2**np.argwhere(np.flip(basis_state_array) == 1))
            self.initial_state[basis_state_idx] = 1.0

        elif operation == "QubitStateVector":
            if not self._first_apply:
                raise qml.DeviceError(
                    "The operation QubitStateVector is only supported at the beginning of a circuit."
                )

            if not self.analytic:
                raise qml.DeviceError(
                    "The operation QubitStateVector is only supported in analytic mode."
                )

            state_vector = np.array(par[0], dtype=np.complex64)

            if len(state_vector) != 2**len(self.qubits):
                raise qml.DeviceError(
                    "For QubitStateVector, the state has to be specified for the correct number of qubits. Got a state of length {}, expected {}."
                    .format(len(state_vector), 2**len(self.qubits)))

            norm_squared = np.sum(np.abs(state_vector)**2)
            if not np.isclose(norm_squared, 1.0, atol=1e-3, rtol=0):
                raise qml.DeviceError(
                    "The given state for QubitStateVector is not properly normalized to 1.0. Got norm {}"
                    .format(math.sqrt(norm_squared)))

            self.initial_state = state_vector

        if self._first_apply:
            self._first_apply = False
Ejemplo n.º 4
0
    def inv(self):
        """Inverses the CirqOperation."""
        # We can also support inversion after parametrization, but this is not necessary for the
        # PennyLane-Cirq codebase at the moment.

        if self.parametrized_cirq_gates:
            raise qml.DeviceError(
                "CirqOperation can't be inverted after it was parametrized.")

        self.is_inverse = not self.is_inverse
Ejemplo n.º 5
0
    def probability(self):
        if self.state is None:
            raise qml.DeviceError(
                "Probability can not be computed because the internal state is None."
            )

        states = itertools.product(range(2), repeat=self.num_wires)
        probs = np.abs(self.state)**2

        return OrderedDict(zip(states, probs))
Ejemplo n.º 6
0
    def apply(self, *qubits):
        """Applies the CirqOperation.

        Args:
            *qubits (Cirq:Qid): the qubits on which the Cirq gates should be performed.
        """
        if not self.parametrized_cirq_gates:
            raise qml.DeviceError(
                "CirqOperation must be parametrized before it can be applied.")

        return (parametrized_gate(*qubits)
                for parametrized_gate in self.parametrized_cirq_gates)
Ejemplo n.º 7
0
def unitary_matrix_gate(U):
    """Creates a Cirq unitary matrix gate from a given matrix.

        Args:
            U (numpy.ndarray): an array representing the gate matrix.
    """
    if U.shape == (2, 2):
        return cirq.SingleQubitMatrixGate(U)
    if U.shape == (4, 4):
        return cirq.TwoQubitMatrixGate(U)
    else:
        raise qml.DeviceError(
            "Cirq only supports single-qubit and two-qubit unitary matrix gates. The given matrix had shape {}"
            .format(U.shape))
Ejemplo n.º 8
0
    def __init__(self, wires, shots, qubits=None):
        super().__init__(wires, shots)

        self._eigs = dict()
        self.circuit = None

        if qubits:
            if wires != len(qubits):
                raise qml.DeviceError(
                    "The number of given qubits and the specified number of wires have to match. Got {} wires and {} qubits."
                    .format(wires, len(qubits)))

            self.qubits = qubits
        else:
            self.qubits = [cirq.LineQubit(wire) for wire in range(wires)]
Ejemplo n.º 9
0
    def pre_measure(self):
        # Cirq only measures states in the computational basis, i.e. 0 and 1
        # To measure different observables, we have to go to their eigenbases

        # This code is adapted from the pennylane-qiskit plugin
        for e in self.obs_queue:
            # Identity and PauliZ need no changes
            if e.name == "PauliX":
                # X = H.Z.H
                self.apply("Hadamard", wires=e.wires, par=[])

            elif e.name == "PauliY":
                # Y = (HS^)^.Z.(HS^) and S^=SZ
                self.apply("PauliZ", wires=e.wires, par=[])
                self.apply("S", wires=e.wires, par=[])
                self.apply("Hadamard", wires=e.wires, par=[])

            elif e.name == "Hadamard":
                # H = Ry(-pi/4)^.Z.Ry(-pi/4)
                self.apply("RY", e.wires, [-np.pi / 4])

            elif e.name == "Hermitian":
                # For arbitrary Hermitian matrix H, let U be the unitary matrix
                # that diagonalises it, and w_i be the eigenvalues.
                Hmat = e.parameters[0]
                Hkey = tuple(Hmat.flatten().tolist())

                if Hmat.shape not in [(2, 2), (4, 4)]:
                    raise qml.DeviceError(
                        "Cirq only supports single-qubit and two-qubit unitary gates and thus only single-qubit and two-qubit Hermitian observables."
                    )

                if Hkey in self._eigs:
                    # retrieve eigenvectors
                    U = self._eigs[Hkey]["eigvec"]
                else:
                    # store the eigenvalues corresponding to H
                    # in a dictionary, so that they do not need to
                    # be calculated later
                    w, U = np.linalg.eigh(Hmat)
                    self._eigs[Hkey] = {"eigval": w, "eigvec": U}

                # Perform a change of basis before measuring by applying U^ to the circuit
                self.apply("QubitUnitary", e.wires, [U.conj().T])
Ejemplo n.º 10
0
    def __init__(self, wires, shots, analytic, qubits=None):

        if not isinstance(wires, Iterable):
            # interpret wires as the number of consecutive wires
            wires = range(wires)
        num_wires = len(wires)

        if qubits:
            if num_wires != len(qubits):
                raise qml.DeviceError(
                    "The number of given qubits and the specified number of wires have to match. Got {} wires and {} qubits."
                    .format(wires, len(qubits)))
        else:
            qubits = [cirq.LineQubit(idx) for idx in range(num_wires)]

        # cirq orders the subsystems based on a total order defined on qubits.
        # For consistency, this plugin uses that same total order
        self._unsorted_qubits = qubits
        self.qubits = sorted(qubits)

        super().__init__(wires, shots, analytic)

        self.circuit = None
        self.cirq_device = None

        # Add inverse operations
        self._inverse_operation_map = {}
        for key in self._operation_map:
            if not self._operation_map[key]:
                continue

            # We have to use a new CirqOperation instance because .inv() acts in-place
            inverted_operation = CirqOperation(
                self._operation_map[key].parametrization)
            inverted_operation.inv()

            self._inverse_operation_map[
                key + Operation.string_for_inverse] = inverted_operation

        self._complete_operation_map = {
            **self._operation_map,
            **self._inverse_operation_map,
        }
Ejemplo n.º 11
0
    def _load_template(self):
        """Load the template corresponding to the pyquil Program.

        Raises:
            qml.DeviceError: When the import of a forked gate is attempted
        """
        self._parametrized_gates = []

        for i, instruction in enumerate(self.program.instructions):
            # Skip all statements that are not gates (RESET, MEASURE, PRAGMA, ...)
            if not _is_gate(instruction):
                if not _is_declaration(instruction):
                    warnings.warn(
                        "Instruction Nr. {} is not supported by PennyLane and was ignored: {}"
                        .format(i + 1, instruction))

                continue

            # Rename for better readability
            gate = instruction

            if _is_forked(gate):
                raise qml.DeviceError(
                    "Forked gates can not be imported into PennyLane, as this functionality is not supported. "
                    + "Instruction Nr. {}, {} was a forked gate.".format(
                        i + 1, gate))

            resolved_gate = _resolve_gate(gate)

            # If the gate is a DefGate or not all CONTROLLED statements can be resolved
            # we resort to QubitUnitary
            if _is_controlled(resolved_gate) or self._is_defined_gate(
                    resolved_gate):
                pl_gate = ParametrizedQubitUnitary(
                    self._resolve_gate_matrix(resolved_gate))
            else:
                pl_gate = pyquil_inv_operation_map[resolved_gate.name]

            parametrized_gate = ParametrizedGate(pl_gate,
                                                 gate.qubits, gate.params,
                                                 _is_inverted(gate))

            self._parametrized_gates.append(parametrized_gate)
Ejemplo n.º 12
0
    def _check_parameter_map(self, parameter_map):
        """Check that all variables of the program are defined.

        Only variables used in measurements need not be defined.

        Args:
            parameter_map (Dict[str, object]): Map that assigns values to variables

        Raises:
            qml.DeviceError: When not all variables are defined in the variable map
        """
        for declaration in self._declarations:
            if not declaration.name in parameter_map:
                # If the variable is used in measurement we don't complain
                if not declaration.name in self._measurement_variable_names:
                    raise qml.DeviceError((
                        "The PyQuil program defines a variable {} that is not present in the given variable map. "
                        + "Instruction: {}").format(declaration.name,
                                                    declaration))
Ejemplo n.º 13
0
def decompose_queue(ops, device):
    """Decompose operations in a queue that are not supported by a device.

    This is a wrapper function for :func:`~._decompose_queue`,
    which raises an error if an operation or its decomposition
    is not supported by the device.

    Args:
        ops (List[~.Operation]): operation queue
        device (~.Device): a PennyLane device
    """
    new_ops = []

    for op in ops:
        try:
            new_ops.extend(_decompose_queue([op], device))
        except NotImplementedError:
            raise qml.DeviceError("Gate {} not supported on device {}".format(op.name, device.short_name))

    return new_ops
Ejemplo n.º 14
0
    def _load_qubit_to_wire_map(self, wires):
        """Build the map that assigns wires to qubits.

        Args:
            wires (Sequence[int]): The wires that should be assigned to the qubits

        Raises:
            qml.DeviceError: When the number of given wires does not match the number of qubits in the pyquil Program

        Returns:
            Dict[int, int]: The map that assigns wires to qubits
        """
        if len(wires) != len(self.qubits):
            raise qml.DeviceError(
                "The number of given wires does not match the number of qubits in the PyQuil Program. "
                + "{} wires were given, Program has {} qubits".format(
                    len(wires), len(self.qubits)))

        self._qubit_to_wire_map = dict(zip(self.qubits, wires))

        return self._qubit_to_wire_map
Ejemplo n.º 15
0
    def apply(self, operations, **kwargs):
        # pylint: disable=missing-function-docstring
        rotations = kwargs.pop("rotations", [])

        for i, operation in enumerate(operations):
            if i > 0 and operation.name in {"BasisState", "QubitStateVector"}:
                raise qml.DeviceError(
                    "The operation {} is only supported at the beginning of a circuit."
                    .format(operation.name))

            if operation.name == "BasisState":
                self._apply_basis_state(operation)
            elif operation.name == "QubitStateVector":
                self._apply_qubit_state_vector(operation)
            else:
                self._apply_operation(operation)

        # TODO: get pre rotated state here

        # Diagonalize the given observables
        for operation in rotations:
            self._apply_operation(operation)