def __init__(self, utry_target, gate_list): """ CircuitTensor Constructor Args: utry_target (np.ndarray): Unitary target matrix gate_list (list[Gate]): The circuit's gate list. """ if not utils.is_unitary(utry_target): raise TypeError("Specified target matrix is not unitary.") if not isinstance(gate_list, list): raise TypeError("Gate list is not a list.") if not all([isinstance(gate, Gate) for gate in gate_list]): raise TypeError("Gate list contains non-gate objects.") self.utry_target = utry_target self.num_qubits = utils.get_num_qubits(self.utry_target) if not all([ utils.is_valid_location(gate.location, self.num_qubits) for gate in gate_list ]): raise ValueError("Gate location mismatch with circuit tensor.") self.gate_list = gate_list self.reinitialize()
def __init__(self, utry, location, fixed=False, check_params=True): """ Gate Constructor Args: utry (np.ndarray): The gate's unitary operation. location (tuple[int]): Set of qubits this gate is applied to. fixed (bool): True if the gate's unitary operation is immutable. check_params (bool): True implies parameters are checked for correctness. """ if check_params: if not utils.is_unitary(utry): raise TypeError("Specified matrix is not unitary.") if not utils.is_valid_location(location): raise TypeError("Specified location is not valid.") if len(location) != utils.get_num_qubits(utry): raise ValueError("Location size does not match unitary.") if not isinstance(fixed, bool): raise TypeError("Invalid fixed parameter.") self.utry = utry self.location = location self.gate_size = len(location) self.fixed = fixed
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
def test_is_unitary_invalid(self): self.assertFalse(is_unitary(1j * np.ones((4, 4)))) self.assertFalse(is_unitary(np.ones((4, 3)))) self.assertFalse(is_unitary(np.ones((4, )))) self.assertFalse(is_unitary(1)) self.assertFalse(is_unitary("a"))
def test_is_unitary2(self): for i in range(1, 10): U = unitary_group.rvs(2 * i) U += 1e-13 * np.ones((2 * i, 2 * i)) self.assertTrue(is_unitary(U, tol=1e-12))
def test_is_unitary1(self): for i in range(1, 10): U = unitary_group.rvs(2 * i) self.assertTrue(is_unitary(U, tol=1e-14))