Ejemplo n.º 1
0
    def execute(self, circuit, **kwargs):

        self.check_validity(circuit.operations, circuit.observables)

        self._circuit_hash = circuit.hash

        circuit_str = circuit.to_openqasm()

        body = {**self.data, "program": circuit_str}

        response = requests.post(self.hostname,
                                 json.dumps(body),
                                 headers=self.header)
        response.raise_for_status()

        job_data = response.json()

        job_id = job_data["job"]
        job_endpoint = "/".join([self.hostname, job_id])

        while job_data["status"] not in self.TERMINAL_STATUSES:
            sleep(self.retry_delay)
            response = requests.get(job_endpoint, headers=self.header)
            response.raise_for_status()

            job_data = response.json()

        if job_data["status"] == "failed":
            raise DeviceError("Job failed in remote backend.")
        if job_data["status"] == "cancelled":
            # possible to get partial results back for cancelled jobs
            try:
                num_results = len(job_data["results"]["c"])
                assert num_results > 0
                if num_results < self.shots:
                    warnings.warn(
                        "Partial results returned from cancelled remote job.",
                        RuntimeWarning)
            except:
                raise DeviceError(
                    "Job was cancelled without returning any results.")

        # pylint: disable=attribute-defined-outside-init
        self._results = job_data["results"]["c"]  # list of binary strings

        # generate computational basis samples
        self._samples = self.generate_samples()

        # compute the required statistics
        results = self.statistics(circuit.observables)

        # Ensures that a combination with sample does not put
        # expvals and vars in superfluous arrays
        all_sampled = all(obs.return_type is Sample
                          for obs in circuit.observables)
        if circuit.is_sampled and not all_sampled:
            return self._asarray(results, dtype="object")  # pragma: no cover

        return self._asarray(results)
Ejemplo n.º 2
0
    def apply(self, operations, rotations=None, **kwargs):
        rotations = rotations or []

        # apply the circuit operations
        for i, operation in enumerate(operations):
            # number of wires on device
            wires = operation.wires
            par = operation.parameters

            if i > 0 and isinstance(operation, (QubitStateVector, BasisState)):
                raise DeviceError("Operation {} cannot be used after other Operations have already been applied "
                                  "on a {} device.".format(operation.name, self.short_name))

            if isinstance(operation, QubitStateVector):
                input_state = np.asarray(par[0], dtype=np.complex128)
                self.apply_state_vector(input_state, wires)

            elif isinstance(operation, BasisState):
                basis_state = par[0]
                self.apply_basis_state(basis_state, wires)

            else:
                self._state = self.mat_vec_product(operation.matrix, self._state, wires)

        # store the pre-rotated state
        self._pre_rotated_state = self._state

        # apply the circuit rotations
        for operation in rotations:
            wires = operation.wires
            par = operation.parameters
            self._state = self.mat_vec_product(operation.matrix, self._state, wires)
Ejemplo n.º 3
0
    def apply(self, operations, **kwargs):
        # pylint: disable=attribute-defined-outside-init
        rotations = kwargs.get("rotations", [])

        # Storing the active wires
        self._active_wires = ForestDevice.active_wires(operations + rotations)

        # Apply the circuit operations
        for i, operation in enumerate(operations):
            # map the ops' wires to the wire labels used by the device
            device_wires = self.map_wires(operation.wires)
            par = operation.parameters

            if isinstance(par, list) and par:
                if isinstance(par[0], np.ndarray) and par[0].shape == ():
                    # Array not supported
                    par = [float(i) for i in par]

            if i > 0 and operation.name in ("QubitStateVector", "BasisState"):
                name = operation.name
                short_name = self.short_name
                raise DeviceError(
                    f"Operation {name} cannot be used after other Operations have already "
                    f"been applied on a {short_name} device.")

            self.prog += self._operation_map[operation.name](
                *par, *device_wires.labels)

        self.prog += self.apply_rotations(rotations)
Ejemplo n.º 4
0
    def expval(self, observable, wires, par):
        """Retrieve the requested observable expectation value."""

        device_wires = self.map_wires(wires)

        probabilities = self._eng.backend.get_probabilities(self._reg)

        if observable in ["PauliX", "PauliY", "PauliZ", "Hadamard"]:

            if observable != "PauliZ" and not hasattr(self, "obs_queue"):
                raise DeviceError(
                    "Measurements in basis other than PauliZ are only supported when "
                    "this plugin is used with versions of PennyLane that expose the obs_queue. "
                    "Please update PennyLane and this plugin.")

            expval = (1 - (2 * sum(p for (state, p) in probabilities.items()
                                   if state[device_wires.labels[0]] == "1")) -
                      (1 - 2 * sum(p for (state, p) in probabilities.items() if
                                   state[device_wires.labels[0]] == "0"))) / 2

        elif observable == "Hermitian":
            raise NotImplementedError

        elif observable == "Identity":
            expval = sum(p for (state, p) in probabilities.items())
        # elif observable == 'AllPauliZ':
        #     expval = [((1-2*sum(p for (state, p) in probabilities.items()
        #                         if state[i] == '1'))
        #                -(1-2*sum(p for (state, p) in probabilities.items()
        #                          if state[i] == '0')))/2 for i in range(len(self._reg))]

        return expval
Ejemplo n.º 5
0
    def apply(self, operations, rotations=None, **kwargs):

        if self.num_wires > self._MAX_WIRES:
            super().apply(operations, rotations=rotations, **kwargs)
            return

        for i, operation in enumerate(
                operations):  # State preparation is currently done in Python
            if isinstance(operation, (QubitStateVector, BasisState)):
                if i == 0:
                    self._apply_operation(operation)
                    del operations[0]
                else:
                    raise DeviceError(
                        "Operation {} cannot be used after other Operations have already been "
                        "applied on a {} device.".format(
                            operation.name, self.short_name))

        if operations:
            self._pre_rotated_state = self.apply_lightning(
                self._state, operations)
        else:
            self._pre_rotated_state = self._state

        if rotations:
            self._state = self.apply_lightning(self._pre_rotated_state,
                                               rotations)
        else:
            self._state = self._pre_rotated_state
Ejemplo n.º 6
0
    def apply(self, operation, wires, par):
        # type: (Any, Sequence[int], List) -> None
        """Apply a quantum operation.

        Args:
            operation (str): name of the operation
            wires (Sequence[int]): subsystems the operation is applied on
            par (tuple): parameters for the operation
        """

        mapped_operation = self._operation_map[operation]

        if isinstance(mapped_operation, BasisState) and not self._first_operation:
            raise DeviceError("Operation {} cannot be used after other Operations have already been applied "
                              "on a {} device.".format(operation, self.short_name))
        self._first_operation = False

        qregs = [(self._reg, i) for i in wires]

        if isinstance(mapped_operation, str):
            dag = circuit_to_dag(QuantumCircuit(self._reg, self._creg, name=''))
            instruction = Instruction(mapped_operation, par, qregs, [], circuit=self._circuit)
            dag.apply_operation_back(instruction)
            qc = dag_to_circuit(dag)
            self._circuit = self._circuit + qc
        elif isinstance(mapped_operation, QiskitInstructions):
            op = mapped_operation  # type: QiskitInstructions
            op.apply(qregs=qregs, param=list(par), circuit=self._circuit)
        else:
            raise ValueError("The operation is not of an expected type. This is a software bug!")
    def apply(self, operations, rotations=None, **kwargs):
        # State preparation is currently done in Python
        if operations:  # make sure operations[0] exists
            if isinstance(operations[0], QubitStateVector):
                self._apply_state_vector(operations[0].parameters[0].copy(),
                                         operations[0].wires)
                del operations[0]
            elif isinstance(operations[0], BasisState):
                self._apply_basis_state(operations[0].parameters[0],
                                        operations[0].wires)
                del operations[0]

        for operation in operations:
            if isinstance(operation, (QubitStateVector, BasisState)):
                raise DeviceError(
                    "Operation {} cannot be used after other Operations have already been "
                    "applied on a {} device.".format(operation.name,
                                                     self.short_name))

        if operations:
            self._pre_rotated_state = self.apply_lightning(
                self._state, operations)
        else:
            self._pre_rotated_state = self._state

        if rotations:
            if any(isinstance(r, QubitUnitary) for r in rotations):
                super().apply(operations=[], rotations=rotations)
            else:
                self._state = self.apply_lightning(
                    np.copy(self._pre_rotated_state), rotations)
        else:
            self._state = self._pre_rotated_state
Ejemplo n.º 8
0
    def apply(self, operation, wires, par):
        """Apply a quantum operation.

        For plugin developers: this function should apply the operation on the device.

        Args:
            operation (str): name of the operation
            wires (Sequence[int]): subsystems the operation is applied on
            par (tuple): parameters for the operation
        """
        operation = self._operation_map[operation](*par)
        if isinstance(operation, BasisState) and not self._first_operation:
            raise DeviceError(
                "Operation {} cannot be used after other Operations have already "
                "been applied on a {} device.".format(operation,
                                                      self.short_name))
        self._first_operation = False

        # translate wires to reflect labels on the device
        device_wires = self.map_wires(wires)

        qureg = [self._reg[i] for i in device_wires.labels]
        if isinstance(
                operation,
            (
                pq.ops._metagates.ControlledGate,  # pylint: disable=protected-access
                pq.ops._gates.SqrtSwapGate,  # pylint: disable=protected-access
                pq.ops._gates.SwapGate,  # pylint: disable=protected-access
            ),
        ):  # pylint: disable=protected-access
            qureg = tuple(qureg)
        operation | qureg  # pylint: disable=pointless-statement
Ejemplo n.º 9
0
    def expval(self, observable, shot_range=None, bin_size=None):
        """Returns the expectation value of a Hamiltonian observable. When the observable is a
         ``SparseHamiltonian`` object, the expectation value is computed directly for the full
         Hamiltonian, which leads to faster execution.

        Args:
            observable (~.Observable): a PennyLane observable
            shot_range (tuple[int]): 2-tuple of integers specifying the range of samples
                to use. If not specified, all samples are used.
            bin_size (int): Divides the shot range into bins of size ``bin_size``, and
                returns the measurement statistic separately over each bin. If not
                provided, the entire shot range is treated as a single bin.

        Returns:
            float: returns the expectation value of the observable
        """
        if observable.name == "SparseHamiltonian":
            if self.shots is not None:
                raise DeviceError(
                    "SparseHamiltonian must be used with shots=None")

        if observable.name == "SparseHamiltonian" and self.shots is None:

            ev = coo_matrix.dot(
                coo_matrix(self._conj(self.state)),
                coo_matrix.dot(
                    observable.matrix,
                    coo_matrix(self.state.reshape(len(self.state), 1))),
            )

            return np.real(ev.toarray()[0])

        return super().expval(observable,
                              shot_range=shot_range,
                              bin_size=bin_size)
Ejemplo n.º 10
0
    def apply(self, operations, rotations=None, **kwargs):
        rotations = rotations or []

        # apply the circuit operations
        for i, operation in enumerate(operations):

            if i > 0 and isinstance(operation, (QubitStateVector, BasisState)):
                raise DeviceError(
                    "Operation {} cannot be used after other Operations have already been applied "
                    "on a {} device.".format(operation.name, self.short_name)
                )

            if isinstance(operation, QubitStateVector):
                self._apply_state_vector(operation.parameters[0], operation.wires)
            elif isinstance(operation, BasisState):
                self._apply_basis_state(operation.parameters[0], operation.wires)
            else:
                self._state = self._apply_operation(self._state, operation)

        # store the pre-rotated state
        self._pre_rotated_state = self._state

        # apply the circuit rotations
        for operation in rotations:
            self._state = self._apply_operation(self._state, operation)
Ejemplo n.º 11
0
    def apply(self, operations, **kwargs):
        rotations = kwargs.pop("rotations", [])

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

        # diagonalize observables
        for operation in rotations:
            self._apply_operation(operation)

        # create circuit job for submission
        self.circuit_json = self.serialize(self.circuit)
        self.data["repetitions"] = self.shots
        job_submission = {**self.data, "data": self.circuit_json}
        response = submit(self.HTTP_METHOD, self.hostname, job_submission,
                          self.header)

        # poll for completed job
        verify_valid_status(response)
        job = response.json()
        job_query_data = {"id": job["id"], "access_token": self._api_key}
        while job["status"] != "finished":
            job = submit(self.HTTP_METHOD, self.hostname, job_query_data,
                         self.header).json()
            sleep(self.retry_delay)

        self.samples = job["samples"]
Ejemplo n.º 12
0
    def apply_operations(self, operations):
        """Apply the circuit operations to the state.

        This method serves as an auxiliary method to :meth:`~.QulacsDevice.apply`.

        Args:
            operations (List[pennylane.Operation]): operations to be applied
        """

        for i, op in enumerate(operations):
            if i > 0 and isinstance(op, (QubitStateVector, BasisState)):
                raise DeviceError(
                    "Operation {} cannot be used after other Operations have already been applied "
                    "on a {} device.".format(op.name, self.short_name))

            if isinstance(op, QubitStateVector):
                self._apply_qubit_state_vector(op)
            elif isinstance(op, BasisState):
                self._apply_basis_state(op)
            elif isinstance(op, QubitUnitary):
                self._apply_qubit_unitary(op)
            elif isinstance(op, (CRZ, PhaseShift)):
                self._apply_matrix(op)
            else:
                self._apply_gate(op)
Ejemplo n.º 13
0
    def expval(self, observable, wires, par):
        """Retrieve the requested observable expectation value.
        """
        probabilities = self._eng.backend.get_probabilities(self._reg)

        if observable == 'PauliX' or observable == 'PauliY' or observable == 'PauliZ' or observable == 'Hadamard':

            if observable != 'PauliZ' and not hasattr(self, 'obs_queue'):
                raise DeviceError(
                    "Measurements in basis other than PauliZ are only supported when this plugin is used with versions of PennyLane that expose the obs_queue. Please update PennyLane and this plugin."
                )

            expval = (1 - (2 * sum(p for (state, p) in probabilities.items()
                                   if state[wires[0]] == '1')) -
                      (1 - 2 * sum(p for (state, p) in probabilities.items()
                                   if state[wires[0]] == '0'))) / 2

        elif observable == 'Hermitian':
            raise NotImplementedError
        elif observable == 'Identity':
            expval = sum(p for (state, p) in probabilities.items())
        # elif observable == 'AllPauliZ':
        #     expval = [((1-2*sum(p for (state, p) in probabilities.items()
        #                         if state[i] == '1'))
        #                -(1-2*sum(p for (state, p) in probabilities.items()
        #                          if state[i] == '0')))/2 for i in range(len(self._reg))]

        return expval
Ejemplo n.º 14
0
    def qubit_state_vector_check(self, operation, par, wires):
        """Input check for the the QubitStateVector operation."""
        if operation == "QubitStateVector":
            if self.backend_name == "unitary_simulator":
                raise DeviceError(
                    "The QubitStateVector operation "
                    "is not supported on the unitary simulator backend.")

            if len(par[0]) != 2**len(wires):
                raise ValueError("State vector must be of length 2**wires.")
Ejemplo n.º 15
0
    def execute(self, tape, **kwargs):

        self.check_validity(tape.operations, tape.observables)
        response = self._submit_circuit(tape)
        response.raise_for_status()

        job_data = response.json()

        job_data = self._query_results(job_data)

        if job_data["status"] == "failed":
            raise DeviceError("Job failed in remote backend.")
        if job_data["status"] == "cancelled":
            # possible to get partial results back for cancelled jobs
            try:
                num_results = len(job_data["results"]["c"])
                assert num_results > 0
                if num_results < self.shots:
                    warnings.warn(
                        "Partial results returned from cancelled remote job.",
                        RuntimeWarning)
            except:
                raise DeviceError(
                    "Job was cancelled without returning any results.")

        # pylint: disable=attribute-defined-outside-init
        self._results = job_data["results"]["c"]  # list of binary strings

        # generate computational basis samples
        self._samples = self.generate_samples()

        # compute the required statistics
        results = self.statistics(tape.observables)

        # Ensures that a combination with sample does not put
        # expvals and vars in superfluous arrays
        all_sampled = all(obs.return_type is Sample
                          for obs in tape.observables)
        if tape.is_sampled and not all_sampled:
            return self._asarray(results, dtype="object")  # pragma: no cover

        return self._asarray(results)
Ejemplo n.º 16
0
    def _append_op_to_queue(self, op_name, par, device_wire_labels):
        """
        Append the given operation to the circuit queue in the correct format for AQT API.

        Args:
            op_name[str]: the PennyLane name of the op
            par[float]: the numeric parameter value for the op
            device_wire_labels[list[int]]: wire labels on the device which the op is to be applied on
        """
        if not op_name in self.operations:
            raise DeviceError("Operation {} is not supported on AQT devices.")
        par = par / np.pi  # AQT convention: all gates differ from PennyLane by factor of pi
        aqt_op_name = self._operation_map[op_name]
        self.circuit.append([aqt_op_name, par, device_wire_labels])
Ejemplo n.º 17
0
    def apply(self, operation, wires, par):
        par = np.negative(par)
        if operation == 'BasisState' and not self._first_operation:
            raise DeviceError(
                'Operation {} cannot be used after other Operations have already been applied '
                'on a {} device.'.format(operation, self.short_name))

        self._first_operation = False

        if operation == 'QubitStateVector':
            if len(par[0]) != 2**len(wires):
                raise ValueError('State vector must be of length 2**wires.')

            self._state.load(par[0])
        elif operation == 'BasisState':
            if len(par[0]) != len(wires):
                raise ValueError('Basis state must prepare all qubits.')

            basis_state = 0
            for bit in reversed(par[0]):
                basis_state = (basis_state << 1) | bit

            self._state.set_computational_basis(basis_state)
        elif operation == 'QubitUnitary':
            if len(par[0]) != 2**len(wires):
                raise ValueError(
                    'Unitary matrix must be of shape (2**wires, 2**wires).')

            unitary_gate = gate.DenseMatrix(wires, par[0])
            self._circuit.add_gate(unitary_gate)
        elif operation == 'Rot':
            self._circuit.add_gate(
                gate.merge([
                    gate.RZ(wires[0], par[0]),
                    gate.RY(wires[0], par[1]),
                    gate.RZ(wires[0], par[2])
                ]))
        elif operation in ('CRZ', 'Toffoli', 'CSWAP'):
            mapped_operation = self._operations_map[operation]
            if callable(mapped_operation):
                gate_matrix = mapped_operation(*par)
            else:
                gate_matrix = mapped_operation

            dense_gate = gate.DenseMatrix(wires, gate_matrix)
            self._circuit.add_gate(dense_gate)
        else:
            mapped_operation = self._operations_map[operation]
            self._circuit.add_gate(mapped_operation(*wires, *par))
Ejemplo n.º 18
0
    def __init__(self, wires, gpu=False, **kwargs):
        super().__init__(wires=wires)

        if gpu:
            if not GPU_SUPPORTED:
                raise DeviceError(
                    'GPU not supported with installed version of qulacs. '
                    'Please install "qulacs-gpu" to use GPU simulation.')

            self._state = QuantumStateGpu(wires)
        else:
            self._state = QuantumState(wires)

        self._circuit = QuantumCircuit(wires)
        self._first_operation = True
Ejemplo n.º 19
0
    def retry_delay(self, time):
        """Changes the devices's ``retry_delay`` property.

        Args:
            time (float): time (in seconds) to wait between calls to remote server

        Raises:
            DeviceError: if the retry delay is not a positive number
        """
        if time <= 0:
            raise DeviceError(
                "The specified retry delay needs to be positive. Got {}.".
                format(time))

        self._retry_delay = float(time)
Ejemplo n.º 20
0
    def apply_parametric_program(self, operations, **kwargs):
        """Applies a parametric program by applying parametric
        operation with symbolic parameters.
        """
        # pylint: disable=attribute-defined-outside-init
        rotations = kwargs.get("rotations", [])

        # Storing the active wires
        self._active_wires = ForestDevice.active_wires(operations + rotations)

        # Apply the circuit operations
        for i, operation in enumerate(operations):
            # map the operation wires to the physical device qubits
            wires = self.remap_wires(operation.wires)

            if i > 0 and operation.name in ("QubitStateVector", "BasisState"):
                raise DeviceError(
                    "Operation {} cannot be used after other Operations have already been applied "
                    "on a {} device.".format(operation.name, self.short_name))

            # Prepare for parametric compilation
            par = []
            for param in operation.params:
                if isinstance(param, Variable):
                    # Using the idx for each Variable instance to specify the
                    # corresponding symbolic parameter
                    parameter_string = "theta" + str(param.idx)

                    if parameter_string not in self._parameter_reference_map:
                        # Create a new PyQuil memory reference and store it in the
                        # parameter reference map if it was not done so already
                        current_ref = self.prog.declare(
                            parameter_string, "REAL")
                        self._parameter_reference_map[
                            parameter_string] = current_ref

                    # Store the numeric value bound to the symbolic parameter
                    self._parameter_map[parameter_string] = [param.val]

                    # Appending the parameter reference to the parameters
                    # of the corresponding operation
                    par.append(self._parameter_reference_map[parameter_string])
                else:
                    par.append(param)

            self.prog += self._operation_map[operation.name](*par, *wires)

        self.prog += self.apply_rotations(rotations)
Ejemplo n.º 21
0
    def __init__(self, wires, shots=1000, analytic=True, gpu=False, **kwargs):
        super().__init__(wires=wires, shots=shots, analytic=analytic)

        if gpu:
            if not QulacsDevice.gpu_supported:
                raise DeviceError(
                    "GPU not supported with installed version of qulacs. "
                    "Please install 'qulacs-gpu' to use GPU simulation.")

            self._state = QuantumStateGpu(self.num_wires)
        else:
            self._state = QuantumState(self.num_wires)

        self._circuit = QuantumCircuit(self.num_wires)

        self._pre_rotated_state = self._state.copy()
Ejemplo n.º 22
0
    def qubit_state_vector_check(self, operation, par, wires):
        """Input check for the the QubitStateVector operation.

        Args:
            operation (pennylane.Operation): operation to be checked

        Raises:
            DeviceError: If the operation is QubitStateVector
            ValueError: If the state has not the right length
        """
        if operation == "QubitStateVector":
            if "unitary" in self.backend_name:
                raise DeviceError(
                    "The QubitStateVector operation "
                    "is not supported on the unitary simulator backend.")

            if len(par[0]) != 2**len(wires):
                raise ValueError("State vector must be of length 2**wires.")
Ejemplo n.º 23
0
    def apply(self, operations, **kwargs):
        self.reset()
        rotations = kwargs.pop("rotations", [])

        if len(operations) == 0 and len(rotations) == 0:
            warnings.warn("Circuit is empty. Empty circuits return failures. Submitting anyway.")

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

        # diagonalize observables
        for operation in rotations:
            self._apply_operation(operation)

        self._submit_job()
Ejemplo n.º 24
0
    def apply(self, operations, **kwargs):
        # pylint: disable=attribute-defined-outside-init
        rotations = kwargs.get("rotations", [])

        # Storing the active wires
        self._active_wires = ForestDevice.active_wires(operations + rotations)

        # Apply the circuit operations
        for i, operation in enumerate(operations):
            # map the ops' wires to the wire labels used by the device
            device_wires = self.map_wires(operation.wires)
            par = operation.parameters

            if i > 0 and operation.name in ("QubitStateVector", "BasisState"):
                raise DeviceError(
                    "Operation {} cannot be used after other Operations have already "
                    "been applied on a {} device.".format(operation.name, self.short_name)
                )
            self.prog += self._operation_map[operation.name](*par, *device_wires.labels)

        self.prog += self.apply_rotations(rotations)
Ejemplo n.º 25
0
    def apply(self, operation):
        # number of wires on device
        wires = operation.wires
        par = operation.parameters

        n = self.num_wires

        if isinstance(operation, QubitStateVector):
            input_state = np.asarray(par[0], dtype=np.complex128)

            if not np.isclose(
                    np.linalg.norm(input_state, 2), 1.0, atol=tolerance):
                raise ValueError(
                    "Sum of amplitudes-squared does not equal one.")
            n_state_vector = input_state.shape[0]
            if not self._first_operation:
                raise DeviceError(
                    "Operation {} cannot be used after other Operations have already been applied "
                    "on a {} device.".format(operation.name, self.short_name))
            if input_state.ndim == 1 and n_state_vector == 2**len(wires):

                # generate basis states on subset of qubits via the cartesian product
                tuples = np.array(
                    list(itertools.product([0, 1], repeat=len(wires))))

                # get basis states to alter on full set of qubits
                unravelled_nums = np.zeros((2**len(wires), n), dtype=int)
                unravelled_nums[:, wires] = tuples

                # get indices for which the state is changed to input state vector elements
                nums = np.ravel_multi_index(unravelled_nums.T, [2] * n)
                self._state = np.zeros_like(self._state)
                self._state[nums] = input_state
            else:
                raise ValueError("State vector must be of length 2**wires.")
            self._first_operation = False
            return

        if isinstance(operation, BasisState):
            # length of basis state parameter
            n_basis_state = len(par[0])

            if not set(par[0]).issubset({0, 1}):
                raise ValueError(
                    "BasisState parameter must consist of 0 or 1 integers.")
            if n_basis_state != len(wires):
                raise ValueError(
                    "BasisState parameter and wires must be of equal length.")
            if not self._first_operation:
                raise DeviceError(
                    "Operation {} cannot be used after other Operations have already been applied "
                    "on a {} device.".format(operation.name, self.short_name))

            # get computational basis state number
            num = int(np.dot(par[0], 2**(n - 1 - np.array(wires))))

            self._state = np.zeros_like(self._state)
            self._state[num] = 1.
            self._first_operation = False
            return

        A = self._get_operator_matrix(operation.name, par)

        self._state = self.mat_vec_product(A, self._state, wires)
        self._first_operation = False