def measure(self, qubit_name, non_destructive): res = None M_0 = qutip.fock(2, 0).proj() M_1 = qutip.fock(2, 1).proj() self._lock() target = self._qubit_names.index(qubit_name) if self.N > 1: M_0 = qutip.gate_expand_1toN(M_0, self.N, target) M_1 = qutip.gate_expand_1toN(M_1, self.N, target) pr_0 = qutip.expect(M_0, self.data) pr_1 = qutip.expect(M_1, self.data) outcome = int(np.random.choice([0, 1], 1, p=[pr_0, pr_1])) if outcome == 0: self.data = M_0 * self.data * M_0.dag() / pr_0 res = 0 else: # M_1 = qutip.gate_expand_1toN(M_1, self.N, target) self.data = M_1 * self.data * M_1.dag() / pr_1 res = 1 if non_destructive is False: i_list = [x for x in range(self.N)] i_list.remove(target) self._qubit_names.remove(qubit_name) self.N = self.N - 1 if len(i_list) > 0: self.data = self.data.ptrace(i_list) else: self.data = qutip.Qobj() self._unlock() return res
def _apply(self, unitary: qt.Qobj, ids: List[int]): if len(ids) == 1: matrix = qt.gate_expand_1toN( unitary, self.capacity, ids[0] ) else: raise ValueError("Only single-bit unitary matrices are supported.") self.register_state = matrix * self.register_state
def apply_single_gate(self, gate, qubit_name): if not isinstance(gate, qutip.Qobj): raise TypeError("Gate has to be of type Qobject.") self._lock() target = self._qubit_names.index(qubit_name) gate = qutip.gate_expand_1toN(gate, self.N, target) self.data = gate * self.data * gate.dag() self._unlock()
def _expand_gate(op: qutip.Qobj, from_qubits: int, to_qubits: int, targets: List[int]): assert len(targets) == from_qubits if from_qubits == 1: return qutip.gate_expand_1toN(op, to_qubits, targets[0]) elif from_qubits == 2: return qutip.gate_expand_2toN(op, to_qubits, targets=targets) else: raise ValueError('Unsupported from_qubits value')
def as_large_qobj_operator(self, num_qubits: int) -> qutip.Qobj: if self.typ.num_qubits == 2: return qutip.gate_expand_2toN(self.as_qobj_operator(), num_qubits, targets=self.qubits) elif self.typ.num_qubits == 1: return qutip.gate_expand_1toN(self.as_qobj_operator(), num_qubits, self.qubits[0]) else: raise NotImplemented('Not implemented for large gates')
def u3(theta3, N=None, target=0): if N is not None: return gate_expand_1toN(u3(theta3), N, target) else: mat = np.array([[np.cos(theta3[0] * 0.5) * np.exp(1j * theta3[1] * 0.5) * np.exp(1j * theta3[2] * 0.5), -np.sin(theta3[0] * 0.5) * np.exp(1j * theta3[1] * 0.5) * np.exp(-1j * theta3[2] * 0.5)], [np.sin(theta3[0] * 0.5) * np.exp(-1j * theta3[1] * 0.5) * np.exp(1j * theta3[2] * 0.5), np.cos(theta3[0] * 0.5) * np.exp(-1j * theta3[1] * 0.5) * np.exp(-1j * theta3[2] * 0.5)]], dtype='complex128') return Qobj(mat, dims=[[2],[2]])
def measure_qubit_inplace(self, qubitNum): """ Measures the desired qubit in the standard basis. This returns the classical outcome. The quantum register is in the post-measurment state corresponding to the obtained outcome. Arguments: qubitNum qubit to be measured """ # Check we have such a qubit... if (qubitNum + 1) > self.activeQubits: raise quantumError("No such qubit to be measured.") # Construct the two measurement operators, and put them at the right position v0 = qp.basis(2, 0) P0 = v0 * v0.dag() M0 = qp.gate_expand_1toN(P0, self.activeQubits, qubitNum) v1 = qp.basis(2, 1) P1 = v1 * v1.dag() M1 = qp.gate_expand_1toN(P1, self.activeQubits, qubitNum) # Compute the success probabilities obj = M0 * self.qubitReg p0 = obj.tr().real obj = M1 * self.qubitReg p1 = obj.tr().real # Sample the measurement outcome from these probabilities outcome = int(np.random.choice([0, 1], 1, p=[p0, p1])) # Compute the post-measurement state, getting rid of the measured qubit if outcome == 0: self.qubitReg = M0 * self.qubitReg * M0.dag() / p0 else: self.qubitReg = M1 * self.qubitReg * M1.dag() / p1 # return measurement outcome return outcome
def measure(self) -> bool: projectors = [ qt.gate_expand_1toN( qt.basis(2, outcome) * qt.basis(2, outcome).dag(), self.parent.capacity, self.id ) for outcome in (0, 1) ] post_measurement_states = [ projector * self.parent.register_state for projector in projectors ] probabilities = [ post_measurement_state.norm() ** 2 for post_measurement_state in post_measurement_states ] sample = np.random.choice([0, 1], p=probabilities) self.parent.register_state = post_measurement_states[sample].unit() return int(sample)
def apply_onequbit_gate(self, gateU, qubitNum): """ Applies a unitary gate to the specified qubit. Arguments: gateU unitary to apply as Qobj qubitNum the number of the qubit this gate is applied to """ # Compute the overall unitary, identity everywhere with gateU at position qubitNum overallU = qp.gate_expand_1toN(gateU, self.activeQubits, qubitNum) # Qutip distinguishes between system dimensionality and matrix dimensionality # so we need to make sure it knows we are talking about multiple qubits k = int(math.log2(overallU.shape[0])) dimL = [] for j in range(k): dimL.append(2) overallU.dims = [dimL, dimL] self.qubitReg.dims = [dimL, dimL] # Apply the unitary self.qubitReg = overallU * self.qubitReg * overallU.dag()
def h(N=None, target=0): if N is not None: return gate_expand_1toN(h(), N, target) else: return Qobj(np.array([[1.0, 1.0], [1.0, -1.0]], dtype='complex128') / np.sqrt(2.0), dims=[[2],[2]])
def z(N=None, target=0): if N is not None: return gate_expand_1toN(z(), N, target) else: return Qobj(np.array([[1., 0.0], [0.0, -1.]], dtype='complex128'), dims=[[2],[2]])
def y(N=None, target=0): if N is not None: return gate_expand_1toN(y(), N, target) else: return Qobj(np.array([[0., -1j], [1j, 0.]], dtype='complex128'), dims=[[2],[2]])