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 inverse_operation(*p): # embed the gate in a unitary matrix with shape (2**wires, 2**wires) g = mapped_operation(*p).get_matrix() mat = reduce(np.kron, [np.eye(2)] * len(device_wires)).astype(complex) mat[-len(g):, -len(g):] = g # mat follows PL convention => reverse wire-order reverse_wire_labels = device_wires.tolist()[::-1] gate_mat = gate.DenseMatrix(reverse_wire_labels, np.conj(mat).T) return gate_mat
def _apply_matrix(self, op): """Apply predefined gate-matrix to state (must follow qulacs convention)""" # translate op wire labels to consecutive wire labels used by the device device_wires = self.map_wires(op.wires) par = op.parameters mapped_operation = self._operation_map[op.name] if op.inverse: mapped_operation = self._get_inverse_operation( mapped_operation, device_wires, par) if callable(mapped_operation): gate_matrix = mapped_operation(*par) else: gate_matrix = mapped_operation # gate_matrix is already in correct order => no wire-reversal needed dense_gate = gate.DenseMatrix(device_wires.labels, gate_matrix) self._circuit.add_gate(dense_gate) gate.DenseMatrix(device_wires.labels, gate_matrix).update_quantum_state(self._state)
def expval(self, observable, wires, par): bra = self._state.copy() if isinstance(observable, list): A = self._get_tensor_operator_matrix(observable, par) wires = [item for sublist in wires for item in sublist] else: A = self._get_operator_matrix(observable, par) dense_gate = gate.DenseMatrix(wires, A) dense_gate.update_quantum_state(self._state) expectation = inner_product(bra, self._state) return expectation.real
def _apply_qubit_unitary(self, op): """Apply unitary to state""" # translate op wire labels to consecutive wire labels used by the device device_wires = self.map_wires(op.wires) par = op.parameters if len(par[0]) != 2**len(device_wires): raise ValueError( "Unitary matrix must be of shape (2**wires, 2**wires).") if op.inverse: par[0] = par[0].conj().T # reverse wires (could also change par[0]) reverse_wire_labels = device_wires.tolist()[::-1] unitary_gate = gate.DenseMatrix(reverse_wire_labels, par[0]) self._circuit.add_gate(unitary_gate) unitary_gate.update_quantum_state(self._state)