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)
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)
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)
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
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
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
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
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)
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)
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"]
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)
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
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.")
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)
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])
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))
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
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)
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)
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()
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.")
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()
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)
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