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))
Exemple #3
0
 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))
Exemple #4
0
 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())
Exemple #6
0
 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