Exemplo n.º 1
0
    def test_calc_env_matrix(self):
        u1 = unitary_group.rvs(8)
        u2 = u1.conj().T

        ct = CircuitTensor(u1, [])

        env = ct.calc_env_matrix([0, 1, 2])
        self.assertTrue(np.allclose(env, u2))
Exemplo n.º 2
0
    def __init__(self, gate_size, num_qubits, locations, native):
        self.gate_size = gate_size
        self.num_qubits = num_qubits
        self.locations = [
            tuple([int(x) for x in location]) for location in locations
        ]
        self.native = native

        self.target = CircuitTensor(np.identity(2**self.num_qubits),
                                    self.get_qfactor()).utry

        self.data = {}
Exemplo n.º 3
0
    def test_calc_env_matrix_invalid(self):
        u1 = unitary_group.rvs(8)
        u2 = u1.conj().T

        ct = CircuitTensor(u1, [])

        self.assertRaises(ValueError, ct.calc_env_matrix, [0, 1, 2, 3])
        self.assertRaises(TypeError, ct.calc_env_matrix, "a")
Exemplo n.º 4
0
def get_distance(circuit, target):
    """
    Returns the distance between the circuit and the unitary target.

    Args:
        circuit (list[Gate]): The circuit.

        target (np.ndarray): The unitary target.
    
    Returns:
        (float): The distance between the circuit and unitary target.
    """

    ct = CircuitTensor(target, circuit)
    num_qubits = utils.get_num_qubits(target)
    return 1 - (np.abs(np.trace(ct.utry)) / (2**num_qubits))
Exemplo n.º 5
0
    def test_apply_right(self):
        u1 = unitary_group.rvs(8)
        u2 = unitary_group.rvs(4)
        g = Gate(u2, (0, 1))
        ct = CircuitTensor(u1, [])

        ct.apply_right(g)
        prod = np.kron(u2, np.identity(2)) @ u1.conj().T
        prod_test = ct.utry
        self.assertTrue(np.allclose(prod, prod_test))

        ct.apply_right(g)
        prod = np.kron(u2, np.identity(2)) @ prod
        prod_test = ct.utry
        self.assertTrue(np.allclose(prod, prod_test))
Exemplo n.º 6
0
class CircuitDataPoint():
    def __init__(self, gate_size, num_qubits, locations, native):
        self.gate_size = gate_size
        self.num_qubits = num_qubits
        self.locations = [
            tuple([int(x) for x in location]) for location in locations
        ]
        self.native = native

        self.target = CircuitTensor(np.identity(2**self.num_qubits),
                                    self.get_qfactor()).utry

        self.data = {}

    @staticmethod
    def generate_circuit(gate_size, num_qubits, length):
        native = gate_size <= 1
        gate_size = 2 if native else gate_size
        locations = list(it.combinations(range(num_qubits), 2))
        locations = np.array(locations)
        idxs = np.random.choice(len(locations), length, replace=True)
        locations = locations[idxs]
        return CircuitDataPoint(gate_size, num_qubits, locations, native)

    def get_qfactor(self):
        circuit = []

        if self.native:
            for pair in self.locations:
                circuit.append(CnotGate(pair[0], pair[1]))
                circuit.append(Gate(unitary_group.rvs(2), (pair[0], )))
                circuit.append(Gate(unitary_group.rvs(2), (pair[1], )))
            return circuit

        for location in self.locations:
            circuit.append(Gate(unitary_group.rvs(2**self.gate_size),
                                location))
        return circuit

    def get_qsearch(self):
        if not self.native:
            return None

        steps = []
        u30 = qsearch.gates.U3Gate()
        u31 = qsearch.gates.U3Gate()
        for pair in self.locations:
            min_idx = min(pair)
            max_idx = max(pair)

            cnot = qsearch.gates.NonadjacentCNOTGate(max_idx - min_idx + 1,
                                                     pair[0] - min_idx,
                                                     pair[1] - min_idx)
            if max_idx - min_idx == 1:
                u_layer = qsearch.gates.KroneckerGate(u30, u31)
            else:
                mid_layer = qsearch.gates.IdentityGate(max_idx - min_idx - 1)
                u_layer = qsearch.gates.KroneckerGate(u30, mid_layer, u31)
            p_layer = qsearch.gates.ProductGate(cnot, u_layer)

            up = qsearch.gates.IdentityGate(min_idx)
            down = qsearch.gates.IdentityGate(self.num_qubits - max_idx - 1)
            steps.append(qsearch.gates.KroneckerGate(up, p_layer, down))
        return qsearch.gates.ProductGate(*steps)

    def get_qfast(self):
        return FixedModel(self.target,
                          self.gate_size, [],
                          LBFGSOptimizer(),
                          success_threshold=1e-8,
                          structure=self.locations)

    def count_qfactor_tries(self):
        dist = 1
        tries = 0
        self.data["qfactor_retry_times"] = []
        while dist > 1e-8:
            start = timer()
            tries += 1
            res = optimize(self.get_qfactor(), self.target, min_iters=0)
            dist = qfactor.get_distance(res, self.target)
            end = timer()
            self.data["qfactor_retry_times"].append(end - start)
        self.data["qfactor_retries"] = tries

    def count_qfast_tries(self):
        dist = 1
        tries = 0
        self.data["qfast_retry_times"] = []
        while dist > 1e-8:
            start = timer()
            tries += 1
            model = self.get_qfast()
            model.optimize(fine=True)
            dist = model.distance()
            end = timer()
            self.data["qfast_retry_times"].append(end - start)
        self.data["qfast_retries"] = tries

    def count_qsearch_tries(self):
        solver = qsearch.solvers.LeastSquares_Jac_SolverNative()
        options = qsearch.options.Options()
        options.target = self.target
        tries = 0
        dist = 1
        circ = self.get_qsearch()
        self.data["qsearch_retry_times"] = []
        while dist > 1e-8:
            start = timer()
            tries += 1
            U, xopts = solver.solve_for_unitary(circ, options)
            dist = 1 - (np.abs(np.trace(self.target.conj().T @ U)) /
                        U.shape[0])
            end = timer()
            self.data["qsearch_retry_times"].append(end - start)
        self.data["qsearch_retries"] = tries
Exemplo n.º 7
0
def optimize(circuit,
             target,
             diff_tol_a=1e-12,
             diff_tol_r=1e-6,
             dist_tol=1e-10,
             max_iters=100000,
             min_iters=1000,
             slowdown_factor=0.0):
    """
    Optimize distance between circuit and target unitary.

    Args:
        circuit (list[Gate]): The circuit to optimize.

        target (np.ndarray): The target unitary matrix.

        diff_tol_a (float): Terminate when the difference in distance
            between iterations is less than this threshold.
       
       diff_tol_r (float): Terminate when the relative difference in
            distance between iterations is iless than this threshold:
                |c1 - c2| <= diff_tol_a + diff_tol_r * abs( c1 )

        dist_tol (float): Terminate when the distance is less than
            this threshold.

        max_iters (int): Maximum number of iterations.

        min_iters (int): Minimum number of iterations.

        slowdown_factor (float): A positive number less than 1. 
            The larger this factor, the slower the optimization.

    Returns:
        (list[Gate]): The optimized circuit.
    """

    if not isinstance(circuit, list):
        raise TypeError("The circuit argument is not a list of gates.")

    if not all([isinstance(g, Gate) for g in circuit]):
        raise TypeError("The circuit argument is not a list of gates.")

    if not utils.is_unitary(target):
        raise TypeError("The target matrix is not unitary.")

    if not isinstance(diff_tol_a, float) or diff_tol_a > 0.5:
        raise TypeError("Invalid absolute difference threshold.")

    if not isinstance(diff_tol_r, float) or diff_tol_r > 0.5:
        raise TypeError("Invalid relative difference threshold.")

    if not isinstance(dist_tol, float) or dist_tol > 0.5:
        raise TypeError("Invalid distance threshold.")

    if not isinstance(max_iters, int) or max_iters < 0:
        raise TypeError("Invalid maximum number of iterations.")

    if not isinstance(min_iters, int) or min_iters < 0:
        raise TypeError("Invalid minimum number of iterations.")

    if slowdown_factor < 0 or slowdown_factor >= 1:
        raise TypeError("Slowdown factor is a positive number less than 1.")

    ct = CircuitTensor(target, circuit)

    c1 = 0
    c2 = 1
    it = 0

    while True:

        # Termination conditions
        if it > min_iters:

            if np.abs(c1 - c2) <= diff_tol_a + diff_tol_r * np.abs(c1):
                diff = np.abs(c1 - c2)
                logger.info(f"Terminated: |c1 - c2| = {diff}"
                            " <= diff_tol_a + diff_tol_r * |c1|.")
                break

            if it > max_iters:
                logger.info("Terminated: iteration limit reached.")
                break

        it += 1

        # from right to left
        for k in range(len(circuit)):
            rk = len(circuit) - 1 - k

            # Remove current gate from right of circuit tensor
            ct.apply_right(circuit[rk], inverse=True)

            # Update current gate
            if not circuit[rk].fixed:
                env = ct.calc_env_matrix(circuit[rk].location)
                circuit[rk].update(env, slowdown_factor)

            # Add updated gate to left of circuit tensor
            ct.apply_left(circuit[rk])

        # from left to right
        for k in range(len(circuit)):

            # Remove current gate from left of circuit tensor
            ct.apply_left(circuit[k], inverse=True)

            # Update current gate
            if not circuit[k].fixed:
                env = ct.calc_env_matrix(circuit[k].location)
                circuit[k].update(env, slowdown_factor)

            # Add updated gate to right of circuit tensor
            ct.apply_right(circuit[k])

        c2 = c1
        c1 = np.abs(np.trace(ct.utry))
        c1 = 1 - (c1 / (2**ct.num_qubits))

        if c1 <= dist_tol:
            logger.info(f"Terminated: c1 = {c1} <= dist_tol.")
            return circuit

        if it % 100 == 0:
            logger.info(f"iteration: {it}, cost: {c1}")

        if it % 40 == 0:
            ct.reinitialize()

    return circuit
Exemplo n.º 8
0
 def test_apply_right_invalid(self):
     u1 = unitary_group.rvs(8)
     ct = CircuitTensor(u1, [])
     self.assertRaises(Exception, ct.apply_right, "a")
Exemplo n.º 9
0
 def test_gate_constructor_valid(self):
     gate = Gate(self.TOFFOLI, (0, 1, 2))
     ct = CircuitTensor(self.TOFFOLI, [gate])
     self.assertTrue(np.array_equal(gate.utry, ct.gate_list[0].utry))
     self.assertTrue(len(ct.gate_list) == 1)
     self.assertTrue(np.allclose(ct.utry, np.identity(8)))
Exemplo n.º 10
0
    def test_toffoli_tensor ( self ):
        toffoli = np.array( [ [ 1, 0, 0, 0, 0, 0, 0, 0 ],
                              [ 0, 1, 0, 0, 0, 0, 0, 0 ],
                              [ 0, 0, 1, 0, 0, 0, 0, 0 ],
                              [ 0, 0, 0, 1, 0, 0, 0, 0 ],
                              [ 0, 0, 0, 0, 1, 0, 0, 0 ],
                              [ 0, 0, 0, 0, 0, 1, 0, 0 ],
                              [ 0, 0, 0, 0, 0, 0, 0, 1 ],
                              [ 0, 0, 0, 0, 0, 0, 1, 0 ] ] )

        p12 = calc_permutation_matrix( 3, (1, 2) )
        p02 = calc_permutation_matrix( 3, (0, 2) )

        cnot = np.array( [ [ 1, 0, 0, 0 ],
                           [ 0, 1, 0, 0 ],
                           [ 0, 0, 0, 1 ],
                           [ 0, 0, 1, 0 ] ] )

        H = (np.sqrt(2)/2) * np.array( [ [ 1, 1 ],
                                         [ 1, -1 ] ] )

        T = np.array( [ [ 1, 0 ], [ 0, np.exp( 1j * np.pi/4 ) ] ] )
        I = np.identity( 2 )

        u1 = np.kron( I, T.conj().T ) @ cnot @ np.kron( I, H )
        u2 = np.kron( I, T ) @ cnot
        u3 = np.kron( I, T.conj().T ) @ cnot
        u4 = np.kron( I, H @ T ) @ cnot
        u5 = cnot @ np.kron( T, T.conj().T ) @ cnot @ np.kron( I, T )

        circuit = [ Gate( u1, (1, 2) ),
                    Gate( u2, (0, 2) ),
                    Gate( u3, (1, 2) ),
                    Gate( u4, (0, 2) ),
                    Gate( u5, (0, 1) ) ]


        c1 = p12 @ np.kron( u1, I ) @ p12.T
        c2 = p02 @ np.kron( u2, I ) @ p02.T
        c3 = p12 @ np.kron( u3, I ) @ p12.T
        c4 = p02 @ np.kron( u4, I ) @ p02.T
        c5 = np.kron( u5, I )
        self.assertTrue( np.allclose( toffoli, c5 @ c4 @ c3 @ c2 @ c1 ) )


        ct = CircuitTensor( toffoli, [] )
        self.assertTrue( np.allclose( ct.utry, toffoli.conj().T  ) )
        ct.apply_right( circuit[0] )
        self.assertTrue( np.allclose( ct.utry, c1 @ toffoli.conj().T ) )
        ct.apply_right( circuit[1] )
        self.assertTrue( np.allclose( ct.utry, c2 @ c1 @ toffoli.conj().T ) )
        ct.apply_right( circuit[2] )
        self.assertTrue( np.allclose( ct.utry, c3 @ c2 @ c1 @ toffoli.conj().T ) )
        ct.apply_right( circuit[3] )
        self.assertTrue( np.allclose( ct.utry, c4 @ c3 @ c2 @ c1 @ toffoli.conj().T ) )
        ct.apply_right( circuit[4] )
        self.assertTrue( np.allclose( ct.utry, c5 @ c4 @ c3 @ c2 @ c1 @ toffoli.conj().T ) )
        self.assertTrue( np.allclose( ct.utry, np.identity( 8 ) ) )
        ct = CircuitTensor( toffoli, circuit )
        self.assertTrue( np.allclose( ct.utry, np.identity( 8 ) ) )