def test_cx_equivalence_3cx(self, seed=3): """Check circuits with 3 cx gates are outside the 0, 1, and 2 qubit regions. """ state = np.random.default_rng(seed) rnd = 2 * np.pi * state.random(size=24) qr = QuantumRegister(2, name='q') qc = QuantumCircuit(qr) qc.u(rnd[0], rnd[1], rnd[2], qr[0]) qc.u(rnd[3], rnd[4], rnd[5], qr[1]) qc.cx(qr[1], qr[0]) qc.u(rnd[6], rnd[7], rnd[8], qr[0]) qc.u(rnd[9], rnd[10], rnd[11], qr[1]) qc.cx(qr[0], qr[1]) qc.u(rnd[12], rnd[13], rnd[14], qr[0]) qc.u(rnd[15], rnd[16], rnd[17], qr[1]) qc.cx(qr[1], qr[0]) qc.u(rnd[18], rnd[19], rnd[20], qr[0]) qc.u(rnd[21], rnd[22], rnd[23], qr[1]) sim = UnitarySimulatorPy() unitary = execute(qc, sim).result().get_unitary() self.assertEqual(two_qubit_cnot_decompose.num_basis_gates(unitary), 3) self.assertTrue(Operator(two_qubit_cnot_decompose(unitary)).equiv(unitary))
def test_cx_equivalence_2cx(self, seed=2): """Check circuits with 2 cx gates locally equivalent to some circuit with 2 cx. """ state = np.random.default_rng(seed) rnd = 2 * np.pi * state.random(size=18) qr = QuantumRegister(2, name='q') qc = QuantumCircuit(qr) qc.u(rnd[0], rnd[1], rnd[2], qr[0]) qc.u(rnd[3], rnd[4], rnd[5], qr[1]) qc.cx(qr[1], qr[0]) qc.u(rnd[6], rnd[7], rnd[8], qr[0]) qc.u(rnd[9], rnd[10], rnd[11], qr[1]) qc.cx(qr[0], qr[1]) qc.u(rnd[12], rnd[13], rnd[14], qr[0]) qc.u(rnd[15], rnd[16], rnd[17], qr[1]) sim = UnitarySimulatorPy() unitary = execute(qc, sim).result().get_unitary() self.assertEqual(two_qubit_cnot_decompose.num_basis_gates(unitary), 2) self.assertTrue(Operator(two_qubit_cnot_decompose(unitary)).equiv(unitary))
def _define(self): """Calculate a subcircuit that implements this unitary.""" if self.num_qubits == 1: q = QuantumRegister(1, "q") theta, phi, lam = _DECOMPOSER1Q.angles(self.to_matrix()) self.definition = [(U3Gate(theta, phi, lam), [q[0]], [])] elif self.num_qubits == 2: self.definition = two_qubit_cnot_decompose(self.to_matrix()) else: raise NotImplementedError("Not able to generate a subcircuit for " "a {}-qubit unitary".format(self.num_qubits))
def _define(self): """Calculate a subcircuit that implements this unitary.""" if self.num_qubits == 1: q = QuantumRegister(1, "q") theta, phi, lam = _DECOMPOSER1Q.angles(self.to_matrix()) self.definition = [(U3Gate(theta, phi, lam), [q[0]], [])] elif self.num_qubits == 2: self.definition = two_qubit_cnot_decompose(self.to_matrix()).data else: q = QuantumRegister(self.num_qubits, "q") self.definition = [(isometry.Isometry(self.to_matrix(), 0, 0), q[:], [])]
def _define(self): """Calculate a subcircuit that implements this unitary.""" if self.num_qubits == 1: q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) theta, phi, lam, global_phase = _DECOMPOSER1Q.angles_and_phase(self.to_matrix()) qc._append(U3Gate(theta, phi, lam), [q[0]], []) qc.global_phase = global_phase self.definition = qc elif self.num_qubits == 2: self.definition = two_qubit_cnot_decompose(self.to_matrix()) else: self.definition = qs_decomposition(self.to_matrix())
def _define(self): """Calculate a subcircuit that implements this unitary.""" if self.num_qubits == 1: q = QuantumRegister(1, "q") qc = QuantumCircuit(q, name=self.name) theta, phi, lam = _DECOMPOSER1Q.angles(self.to_matrix()) qc._append(U3Gate(theta, phi, lam), [q[0]], []) self.definition = qc elif self.num_qubits == 2: self.definition = two_qubit_cnot_decompose(self.to_matrix()) else: q = QuantumRegister(self.num_qubits, "q") qc = QuantumCircuit(q, name=self.name) qc.append(isometry.Isometry(self.to_matrix(), 0, 0), qargs=q[:]) self.definition = qc
def set_relationship(self, relationships, qubit0, qubit1, fraction=1, update=True): ''' Rotates the given pair of qubits towards the given target expectation values. Args: target_state: Target expectation values. qubit0, qubit1: Qubits on which the operation is applied fraction: fraction of the rotation toward the target state to apply. update: whether to update the tomography after the rotation is added to the circuit. ''' zero = 0.001 def inner(vec1, vec2): inner = 0 for j in range(len(vec1)): inner += conj(vec1[j]) * vec2[j] return inner def normalize(vec): renorm = sqrt(inner(vec, vec)) if abs((renorm * conj(renorm))) > zero: return np.copy(vec) / renorm else: return [nan for amp in vec] def random_vector(ortho_vecs=[]): vec = np.array([2 * random() - 1 for _ in range(4)], dtype='complex') vec[0] = abs(vec[0]) for ortho_vec in ortho_vecs: vec -= inner(ortho_vec, vec) * ortho_vec return normalize(vec) def get_rho(qubit0, qubit1): if type(self.backend) == ExpectationValue: rel = self.get_relationship(qubit0, qubit1) b0 = self.get_bloch(qubit0) b1 = self.get_bloch(qubit1) rho = np.identity(4, dtype='complex128') for pauli in ['X', 'Y', 'Z']: rho += b0[pauli] * matrices[pauli + 'I'] rho += b1[pauli] * matrices['I' + pauli] for pauli in [ 'XX', 'XY', 'XZ', 'YX', 'YY', 'YZ', 'ZX', 'ZY', 'ZZ' ]: rho += rel[pauli] * matrices[pauli] return rho / 4 else: (q0, q1) = sorted([qubit0, qubit1]) return self.tomography.fit(output='density_matrix', pairs_list=[(q0, q1)])[q0, q1] raw_vals, raw_vecs = la.eigh(get_rho(qubit0, qubit1)) vals = sorted([(val, k) for k, val in enumerate(raw_vals)], reverse=True) vecs = [[raw_vecs[j][k] for j in range(4)] for (val, k) in vals] Pup = np.identity(4, dtype='complex') for (pauli, sign) in relationships.items(): Pup = dot(Pup, (matrices['II'] + sign * matrices[pauli]) / 2) Pdown = (matrices['II'] - Pup) new_vecs = [[nan for _ in range(4)] for _ in range(4)] valid = [False for _ in range(4)] # the first new vector comes from projecting the first eigenvector vec = np.copy(vecs[0]) while not valid[0]: new_vecs[0] = normalize(dot(Pup, vec)) valid[0] = True not in [isnan(new_vecs[0][j]) for j in range(4)] # if that doesn't work, a random vector is projected instead vec = random_vector() # the second is found by similarly projecting the second eigenvector # and then finding the component orthogonal to new_vecs[0] vec = dot(Pup, vecs[1]) while not valid[1]: new_vecs[1] = vec - inner(new_vecs[0], vec) * new_vecs[0] new_vecs[1] = normalize(new_vecs[1]) valid[1] = True not in [isnan(new_vecs[1][j]) for j in range(4)] # if that doesn't work, start with a random one instead vec = random_vector() # the third is the projection of the third eigenvector to the subpace orthogonal to the first two vec = np.copy(vecs[2]) for j in range(2): vec -= inner(new_vecs[j], vec) * new_vecs[j] while not valid[2]: new_vecs[2] = normalize(vec) valid[2] = True not in [isnan(new_vecs[2][j]) for j in range(4)] # if that doesn't work, use a random vector orthogonal to the first two vec = random_vector(ortho_vecs=[new_vecs[0], new_vecs[1]]) # the last is just orthogonal to the rest vec = normalize(dot(Pdown, vecs[3])) while not valid[3]: new_vecs[3] = random_vector( ortho_vecs=[new_vecs[0], new_vecs[1], new_vecs[2]]) valid[3] = True not in [isnan(new_vecs[3][j]) for j in range(4)] # a unitary is then constructed to rotate the old basis into the new U = [[0 for _ in range(4)] for _ in range(4)] for j in range(4): U += outer(new_vecs[j], conj(vecs[j])) if fraction != 1: U = pwr(U, fraction) try: circuit = two_qubit_cnot_decompose(U) gate = circuit.to_instruction() done = True except Exception as e: print(e) gate = None if gate: self.qc.append(gate, [qubit0, qubit1]) if update: self.update_tomography() return gate