def get_Zf(f, n): """ This function generates the Zf gate satisfying the condition for x in {0,1}^n where Zf|x> -> (-1)^f(X)|x> This function requires that f(x) be calculated for all x, so f is passed as an anonymous function, the other parameter is n. The function has one dependency, the DefGate function defined in pyquil.quil This function finds all permutations of bitstrings of length n, then initializes a 2^n x 2^n matrix of all 0's, and sets all elements along the diagonal to either 1 or -1 depending on f(x) Finally a gate representation of this matrix is returned. """ # generate bitstring permutations bitstrings = list() get_bitstring_permutations(0, bitstrings, n, [0] * n) # initialize a 2^n x 2^n matrix of all 0's gate = np.zeros((2 ** n, 2 ** n), dtype=int) # set diagonals of matrix based on f(x) for i in range(2 ** n): gate[i][i] = -1 if f(bitstrings[i]) == 1 else 1 # create and return gate return Operator(gate)
def test_block_spanning_two_regs(self): """blocks spanning wires on different quantum registers work.""" qr0 = QuantumRegister(1, "qr0") qr1 = QuantumRegister(1, "qr1") qc = QuantumCircuit(qr0, qr1) qc.u1(0.5, qr0[0]) qc.u2(0.2, 0.6, qr1[0]) qc.cx(qr0[0], qr1[0]) dag = circuit_to_dag(qc) pass_ = ConsolidateBlocks(force_consolidate=True) pass_.property_set['block_list'] = [list(dag.topological_op_nodes())] new_dag = pass_.run(dag) sim = UnitarySimulatorPy() result = execute(qc, sim).result() unitary = UnitaryGate(result.get_unitary()) self.assertEqual(len(new_dag.op_nodes()), 1) fidelity = process_fidelity(Operator(new_dag.op_nodes()[0].op), unitary.to_matrix()) self.assertAlmostEqual(fidelity, 1.0, places=7)
def test_3q_blocks(self): """blocks of more than 2 qubits work.""" qr = QuantumRegister(3, "qr") qc = QuantumCircuit(qr) qc.u1(0.5, qr[0]) qc.u2(0.2, 0.6, qr[1]) qc.cx(qr[2], qr[1]) qc.cx(qr[0], qr[1]) dag = circuit_to_dag(qc) pass_ = ConsolidateBlocks(force_consolidate=True) pass_.property_set['block_list'] = [list(dag.topological_op_nodes())] new_dag = pass_.run(dag) sim = UnitarySimulatorPy() result = execute(qc, sim).result() unitary = UnitaryGate(result.get_unitary()) self.assertEqual(len(new_dag.op_nodes()), 1) fidelity = process_fidelity(Operator(new_dag.op_nodes()[0].op), unitary.to_matrix()) self.assertAlmostEqual(fidelity, 1.0, places=7)
def random_unitary(dim, seed=None): """ Return a random dim x dim unitary Operator from the Haar measure. Args: dim (int): the dim of the state space. seed (int): Optional. To set a random seed. Returns: Operator: (dim, dim) unitary operator. Raises: QiskitError: if dim is not a positive power of 2. """ if seed is not None: np.random.seed(seed) if dim == 0 or not math.log2(dim).is_integer(): raise QiskitError( "Desired unitary dimension not a positive power of 2.") return Operator(unitary_group.rvs(dim))
def check_two_qubit_weyl_decomposition(self, target_unitary, tolerance=1.e-7): """Check TwoQubitWeylDecomposition() works for a given operator""" # pylint: disable=invalid-name decomp = TwoQubitWeylDecomposition(target_unitary) op = np.exp(1j * decomp.global_phase) * Operator(np.eye(4)) for u, qs in ( (decomp.K2r, [0]), (decomp.K2l, [1]), (Ud(decomp.a, decomp.b, decomp.c), [0, 1]), (decomp.K1r, [0]), (decomp.K1l, [1]), ): op = op.compose(u, qs) decomp_unitary = op.data maxdist = np.max(np.abs(target_unitary - decomp_unitary)) self.assertTrue( np.abs(maxdist) < tolerance, "Unitary {}: Worst distance {}".format(target_unitary, maxdist))
def _compose_instr(instr0, instr1, num_qubits): """Helper function for compose a kraus with another instruction.""" # If one of the instructions is an identity we only need # to return the other instruction if instr0['name'] == 'id': return instr1 if instr1['name'] == 'id': return instr0 # Convert to ops op0 = QuantumError._instr2op(instr0) op1 = QuantumError._instr2op(instr1) # Check if at least one of the instructions is a channel # and if so convert both to SuperOp representation if isinstance(op0, (SuperOp, Kraus)) or isinstance(op1, (SuperOp, Kraus)): name = 'kraus' op0 = SuperOp(op0) op1 = SuperOp(op1) else: name = 'unitary' # Check qubits for compositions qubits0 = instr0['qubits'] qubits1 = instr1['qubits'] if qubits0 == qubits1: composed = op0.compose(op1) qubits = qubits0 else: # If qubits don't match we compose with total number of qubits # for the error if name == 'kraus': composed = SuperOp(np.eye(4 ** num_qubits)) else: composed = Operator(np.eye(2 ** num_qubits)) composed.compose(op0, qargs=qubits0).compose(op1, qargs=qubits1) qubits = list(range(num_qubits)) # Get instruction params if name == 'kraus': params = Kraus(composed).data else: params = [composed.data] return {'name': name, 'qubits': qubits, 'params': params}
def bv_program(U_f, n, draw_circuit=False): circuit = QuantumCircuit(n + 1, n) # invert the helper qubit to make it 1 circuit.x(n) # apply Hadamard to all input qubits and helper qubit for i in range(n + 1): circuit.h(i) # define the U_f gate based on the unitary matrix returned by get_U_f U_f_gate = Operator(U_f) circuit.unitary(U_f_gate, range(n, -1, -1), label='U_f') # apply Hadamard to all input qubits for i in range(n): circuit.h(i) if draw_circuit: print(circuit_drawer(circuit, output='text')) return circuit
def C4HGate(numerator = 1, divisor = 1): h1 = compute_first_operand(numerator, divisor) h2 = compute_second_operand(numerator, divisor) return Operator([ [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, h1, 0, 0, 0, 0, 0, 0, 0, h2, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 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, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, h2, 0, 0, 0, 0, 0, 0, 0, -h1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], ])
def check_exact_decomposition(self, target_unitary, decomposer, tolerance=1.e-7): """Check exact decomposition for a particular target""" with self.subTest(unitary=target_unitary, decomposer=decomposer): decomp_circuit = decomposer(target_unitary) result = execute(decomp_circuit, UnitarySimulatorPy()).result() decomp_unitary = Operator(result.get_unitary()) result = execute(decomp_circuit, UnitarySimulatorPy()).result() decomp_unitary = result.get_unitary() target_unitary *= la.det(target_unitary)**(-0.25) decomp_unitary *= la.det(decomp_unitary)**(-0.25) maxdists = [ np.max(np.abs(target_unitary + phase * decomp_unitary)) for phase in [1, 1j, -1, -1j] ] maxdist = np.min(maxdists) self.assertTrue( np.abs(maxdist) < tolerance, "Worst distance {}".format(maxdist))
def test_cx_equivalence_1cx(self, seed=1): """Check circuits with 1 cx gates locally equivalent to a cx """ state = np.random.default_rng(seed) rnd = 2 * np.pi * state.random(size=12) 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]) sim = UnitarySimulatorPy() unitary = execute(qc, sim).result().get_unitary() self.assertEqual(two_qubit_cnot_decompose.num_basis_gates(unitary), 1) self.assertTrue(Operator(two_qubit_cnot_decompose(unitary)).equiv(unitary))
def represent_full_cost_function(params, U, gate): ''' Cost function for variationally finding the environment of an iMPS with a quantum circuit. Requires full tomography of the reduced density matrices that are being evaluated. ''' simulator = Aer.get_backend('statevector_simulator') target_environment = Operator(gate(params)) circ_ρσ = QuantumCircuit(5) circ_ρσ.append(target_environment, [2, 1]) circ_ρσ.append(U, [1, 0]) circ_ρσ.append(target_environment, [4, 3]) result = execute(circ_ρσ, simulator).result() statevector = result.get_statevector(circ_ρσ) ρ = np.outer(statevector, statevector.conj()) ρ_1 = partial_trace(ρ, 5, [0]) ρ_2 = partial_trace(ρ, 5, [3]) return np.linalg.norm(ρ_1 - ρ_2)
def qc_program(n, t, reload, verbose): trials = (n - 1) * (4 * t) u_f, s = getUf(n, reload) U_f = Operator(u_f) simulator = Aer.get_backend('qasm_simulator') circuit = QuantumCircuit(2 * n, n) circuit.h(range(n)) circuit.append(U_f, range(2 * n)[::-1]) circuit.h(range(n)) circuit.measure(range(n), range(n)) print(circuit) for i in range(4 * t): job = execute(circuit, simulator, shots=(n - 1)) results = job.result().get_counts(circuit) if len(results.keys()) != n - 1: continue if verbose: print(f' Trial {i+1}:') potential_ys = [] for index, y in enumerate(results.keys()): potential_ys.append(y) if verbose: print(f' y_{index} = {y}') if is_lin_indep(potential_ys): if verbose: print( f'Found linearly independent ys!\nChecking if they solve to s correctly...' ) print('====================================\n') return check_validity(potential_ys, s) if verbose: print('====================================\n') return None
def get_Sx(ang=None, x=None, pad=True, circuit=False): backend = Aer.get_backend('unitary_simulator') if pad==True: q = QuantumRegister(2) qc = QuantumCircuit(q) qc = state_preparation(ang, qc, [0, 1]) elif pad==False: x = x.astype(complex) q = QuantumRegister(1) qc = QuantumCircuit(q) qc.initialize(x, [0]) job = execute(qc, backend) result = job.result() U = result.get_unitary(qc) S = Operator(U) if circuit==True: return qc else: return S
def distance(circuit, c_bit, a, b): """ Calculate distances between 2 data points denoted as quantum states. Based on DistCalc from Quantum Machine Learning for data scientists. !!! Not possible to run with current version of IBM Q API. :param circuit: IBM Q circuit object """ # controlled swap operator 6x6 cswap = Operator([[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1], [0, 0, 1, 0, 0, 0], [0, 0, 0, 1, 0, 0]]) # |s0> = |0, a, b> # 0 - control bit # a, b - to find dist between circuit.h(c_bit) circuit.append(cswap, [c_bit, a, b]) circuit.h(c_bit)
def __call__(self, unitary_mat, simplify=True, atol=DEFAULT_ATOL): """Decompose single qubit gate into a circuit. Args: unitary_mat (array_like): 1-qubit unitary matrix simplify (bool): remove zero-angle rotations [Default: True] atol (float): absolute tolerance for checking angles zero. Returns: QuantumCircuit: the decomposed single-qubit gate circuit Raises: QiskitError: if input is invalid or synthesis fails. """ if hasattr(unitary_mat, 'to_operator'): # If input is a BaseOperator subclass this attempts to convert # the object to an Operator so that we can extract the underlying # numpy matrix from `Operator.data`. unitary_mat = unitary_mat.to_operator().data if hasattr(unitary_mat, 'to_matrix'): # If input is Gate subclass or some other class object that has # a to_matrix method this will call that method. unitary_mat = unitary_mat.to_matrix() # Convert to numpy array incase not already an array unitary_mat = np.asarray(unitary_mat, dtype=complex) # Check input is a 2-qubit unitary if unitary_mat.shape != (2, 2): raise QiskitError("OneQubitEulerDecomposer: " "expected 2x2 input matrix") if not is_unitary_matrix(unitary_mat): raise QiskitError("OneQubitEulerDecomposer: " "input matrix is not unitary.") circuit = self._circuit(unitary_mat, simplify=simplify, atol=atol) # Check circuit is correct if not Operator(circuit).equiv(unitary_mat): raise QiskitError("OneQubitEulerDecomposer: " "synthesis failed within required accuracy.") return circuit
def check_one_qubit_euler_angles(self, operator, basis='U3', tolerance=1e-12, phase_equal=False): """Check OneQubitEulerDecomposer works for the given unitary""" target_unitary = operator.data if basis is None: angles = OneQubitEulerDecomposer().angles(target_unitary) decomp_unitary = U3Gate(*angles).to_matrix() else: decomposer = OneQubitEulerDecomposer(basis) decomp_unitary = Operator(decomposer(target_unitary)).data # Add global phase to make special unitary target_unitary *= la.det(target_unitary)**(-0.5) decomp_unitary *= la.det(decomp_unitary)**(-0.5) maxdist = np.max(np.abs(target_unitary - decomp_unitary)) if not phase_equal and maxdist > 0.1: maxdist = np.max(np.abs(target_unitary + decomp_unitary)) self.assertTrue( np.abs(maxdist) < tolerance, "Operator {}: Worst distance {}".format(operator, maxdist))
def test_two_qubit_operations_supported_by_pennylane(self, recorder): """Tests loading a circuit with the two-qubit operations supported by PennyLane.""" two_wires = [0, 1] qc = QuantumCircuit(2, 1) unitary_op = [[1, 0, 0, 0], [0, 0, 1j, 0], [0, 1j, 0, 0], [0, 0, 0, 1]] iswap_op = Operator(unitary_op) qc.cx(*two_wires) qc.cz(*two_wires) qc.swap(*two_wires) qc.unitary(iswap_op, [0, 1], label='iswap') quantum_circuit = load(qc) with recorder: quantum_circuit() assert len(recorder.queue) == 4 assert recorder.queue[0].name == 'CNOT' assert recorder.queue[0].params == [] assert recorder.queue[0].wires == two_wires assert recorder.queue[1].name == 'CZ' assert recorder.queue[1].params == [] assert recorder.queue[1].wires == two_wires assert recorder.queue[2].name == 'SWAP' assert recorder.queue[2].params == [] assert recorder.queue[2].wires == two_wires assert recorder.queue[3].name == 'QubitUnitary' assert len(recorder.queue[3].params) == 1 assert np.array_equal(recorder.queue[3].params[0], np.array(unitary_op)) assert recorder.queue[3].wires == two_wires
def linear_operator(param, pad=True, circuit = False): backend = Aer.get_backend('unitary_simulator') '''pad variable influences the size of params vector''' if pad==True: data = QuantumRegister(2) qc = QuantumCircuit(data) qc.u3(param[0], param[1], param[2], data[0]) qc.u3(param[3], param[4], param[5], data[1]) qc.cx(data[0], data[1]) elif pad==False: data = QuantumRegister(1) qc = QuantumCircuit(data) qc.u3(param[0], param[1], param[2], data) job = execute(qc, backend) result = job.result() if circuit==True: return qc else: U = result.get_unitary(qc) G = Operator(U) return G
def random_unitary(dims, seed=None): """Return a random unitary Operator. The operator is sampled from the unitary Haar measure. Args: dims (int or tuple): the input dimensions of the Operator. seed (int or np.random.Generator): Optional. Set a fixed seed or generator for RNG. Returns: Operator: a unitary operator. """ if seed is None: random_state = np.random.default_rng() elif isinstance(seed, np.random.Generator): random_state = seed else: random_state = default_rng(seed) dim = np.product(dims) mat = stats.unitary_group.rvs(dim, random_state=random_state) return Operator(mat, input_dims=dims, output_dims=dims)
def sigma(pad=True, circuit = False): backend = Aer.get_backend('unitary_simulator') if pad==True: data = QuantumRegister(2) qc = QuantumCircuit(data) qc.id(data) if pad==False: data = QuantumRegister(1) qc = QuantumCircuit(data) qc.id(data) job = execute(qc, backend) result = job.result() U = result.get_unitary(qc) I = Operator(U) if circuit==True: return qc else: return I
def test_overlapping_block_and_run(self): """Test that an overlapping block and run only consolidate once""" qc = QuantumCircuit(2) qc.h(0) qc.t(0) qc.sdg(0) qc.cx(0, 1) qc.t(1) qc.sdg(1) qc.z(1) qc.i(1) pass_manager = PassManager() pass_manager.append(Collect2qBlocks()) pass_manager.append(Collect1qRuns()) pass_manager.append(ConsolidateBlocks(force_consolidate=True)) result = pass_manager.run(qc) expected = Operator(qc) # Assert output circuit is a single unitary gate equivalent to # unitary of original circuit self.assertEqual(len(result), 1) self.assertIsInstance(result.data[0][0], UnitaryGate) self.assertTrue(np.allclose(result.data[0][0].to_matrix(), expected))
def generate_circuit(n, t, reload, verbose): SAVEDIR = 'plots/' CIRCUIT_FILENAME = f'simon_{n}.pdf' trials = (n - 1) * (4 * t) u_f, s = getUf(n, reload) U_f = Operator(u_f) circuit = QuantumCircuit(2 * n, n) circuit.h(range(n)) # circuit.append(U_f, range(2*n)[::-1]) circuit.barrier() # for i in range(n): # for j in range(n): # circuit.cx(i, n+j) for i in range(n): if s[i] == '1': for j in range(n): circuit.cx(n - 1 - i, n + j) circuit.barrier() circuit.h(range(n)) circuit.barrier() circuit.measure(range(0, n), range(n)) #circuit.measure(range(n, 2*n), range(n)) if v: circuit.draw('mpl') print(f'Saving circuit to {CIRCUIT_FILENAME} .. ', end='', flush=True) if not os.path.exists(SAVEDIR): os.mkdir(SAVEDIR) matplotlib.pyplot.savefig(f'{SAVEDIR}{CIRCUIT_FILENAME}') print('done\n', flush=True) return circuit, s
def _logarithmic_encoding( self, spin: Union[Fraction, int] ) -> Tuple[PauliSumOp, PauliSumOp, PauliSumOp, PauliSumOp]: """The logarithmic encoding. Args: spin: Positive half-integer (integer or half-odd-integer) that represents spin. Returns: A tuple containing four PauliSumOp. """ spin_op_encoding: List[PauliSumOp] = [] dspin = int(2 * spin + 1) num_qubits = int(np.ceil(np.log2(dspin))) # Get the spin matrices spin_matrices = [ SpinOp(symbol, spin=spin).to_matrix() for symbol in "XYZ" ] # Append the identity spin_matrices.append(np.eye(dspin)) # Embed the spin matrices in a larger matrix of size 2**num_qubits x 2**num_qubits embedded_spin_matrices = [ self._embed_matrix(matrix, num_qubits) for matrix in spin_matrices ] # Generate operators from these embedded spin matrices embedded_operators = [ Operator(matrix) for matrix in embedded_spin_matrices ] for op in embedded_operators: op = SparsePauliOp.from_operator(op) op.chop() spin_op_encoding.append(PauliSumOp(1.0 * op)) return tuple(spin_op_encoding) # type: ignore
def __build_circuit(self): self.__produce_u_f_gate() circuit = QuantumCircuit(self.n + 1, self.n) circuit.x(0) for i in range(self.n + 1): circuit.h(i) if self.__check_identity() == False: circuit.unitary(Operator(self.__Uf), [i for i in range(self.n + 1)], label='Uf') for i in range(1, self.n + 1): circuit.h(i) circuit.measure([i for i in range(self.n, 0, -1)], [i for i in range(self.n)]) #print(circuit.draw()) self.__circuit = circuit
def parityCircuit(theta1=0, theta2=0): # Noise rotation matrix. n1 = rotationMatrix(theta1) n2 = rotationMatrix(theta2) n = np.kron(n1, n2) # Noise operator id_op = Operator(n) truthtable = "10011001" oracle = TruthTableOracle(truthtable) or_cx = oracle.construct_circuit() # print(oracle.output_register) v = oracle.variable_register o = oracle.output_register cr1 = ClassicalRegister(3) cr2 = ClassicalRegister(1) cx_circ = QuantumCircuit(v, cr2) or_cx.add_register(cr1) cx_circ.h(v[1]) cx_circ.cx(v[1], v[0]) cx_circ.unitary(id_op, v[1:3], label='idop') total_cx = cx_circ + or_cx total_cx.measure(v, cr1) total_cx.measure(o, cr2) return total_cx
def __init__(self, gate, basis_fidelity=1.0, euler_basis=None): self.gate = gate self.basis_fidelity = basis_fidelity basis = self.basis = TwoQubitWeylDecomposition(Operator(gate).data) if euler_basis is not None: self._decomposer1q = OneQubitEulerDecomposer(euler_basis) else: self._decomposer1q = OneQubitEulerDecomposer("U3") # FIXME: find good tolerances self.is_supercontrolled = math.isclose(basis.a, np.pi / 4) and math.isclose(basis.c, 0.0) # Create some useful matrices U1, U2, U3 are equivalent to the basis, # expand as Ui = Ki1.Ubasis.Ki2 b = basis.b K11l = ( 1 / (1 + 1j) * np.array( [ [-1j * cmath.exp(-1j * b), cmath.exp(-1j * b)], [-1j * cmath.exp(1j * b), -cmath.exp(1j * b)], ], dtype=complex, ) ) K11r = ( 1 / math.sqrt(2) * np.array( [ [1j * cmath.exp(-1j * b), -cmath.exp(-1j * b)], [cmath.exp(1j * b), -1j * cmath.exp(1j * b)], ], dtype=complex, ) ) K12l = 1 / (1 + 1j) * np.array([[1j, 1j], [-1, 1]], dtype=complex) K12r = 1 / math.sqrt(2) * np.array([[1j, 1], [-1, -1j]], dtype=complex) K32lK21l = ( 1 / math.sqrt(2) * np.array( [ [1 + 1j * np.cos(2 * b), 1j * np.sin(2 * b)], [1j * np.sin(2 * b), 1 - 1j * np.cos(2 * b)], ], dtype=complex, ) ) K21r = ( 1 / (1 - 1j) * np.array( [ [-1j * cmath.exp(-2j * b), cmath.exp(-2j * b)], [1j * cmath.exp(2j * b), cmath.exp(2j * b)], ], dtype=complex, ) ) K22l = 1 / math.sqrt(2) * np.array([[1, -1], [1, 1]], dtype=complex) K22r = np.array([[0, 1], [-1, 0]], dtype=complex) K31l = ( 1 / math.sqrt(2) * np.array( [[cmath.exp(-1j * b), cmath.exp(-1j * b)], [-cmath.exp(1j * b), cmath.exp(1j * b)]], dtype=complex, ) ) K31r = 1j * np.array([[cmath.exp(1j * b), 0], [0, -cmath.exp(-1j * b)]], dtype=complex) K32r = ( 1 / (1 - 1j) * np.array( [ [cmath.exp(1j * b), -cmath.exp(-1j * b)], [-1j * cmath.exp(1j * b), -1j * cmath.exp(-1j * b)], ], dtype=complex, ) ) k1ld = basis.K1l.T.conj() k1rd = basis.K1r.T.conj() k2ld = basis.K2l.T.conj() k2rd = basis.K2r.T.conj() # Pre-build the fixed parts of the matrices used in 3-part decomposition self.u0l = K31l.dot(k1ld) self.u0r = K31r.dot(k1rd) self.u1l = k2ld.dot(K32lK21l).dot(k1ld) self.u1ra = k2rd.dot(K32r) self.u1rb = K21r.dot(k1rd) self.u2la = k2ld.dot(K22l) self.u2lb = K11l.dot(k1ld) self.u2ra = k2rd.dot(K22r) self.u2rb = K11r.dot(k1rd) self.u3l = k2ld.dot(K12l) self.u3r = k2rd.dot(K12r) # Pre-build the fixed parts of the matrices used in the 2-part decomposition self.q0l = K12l.T.conj().dot(k1ld) self.q0r = K12r.T.conj().dot(_ipz).dot(k1rd) self.q1la = k2ld.dot(K11l.T.conj()) self.q1lb = K11l.dot(k1ld) self.q1ra = k2rd.dot(_ipz).dot(K11r.T.conj()) self.q1rb = K11r.dot(k1rd) self.q2l = k2ld.dot(K12l) self.q2r = k2rd.dot(K12r) # Decomposition into different number of gates # In the future could use different decomposition functions for different basis classes, etc if not self.is_supercontrolled: warnings.warn( "Only know how to decompose properly for supercontrolled basis gate. " "This gate is ~Ud({}, {}, {})".format(basis.a, basis.b, basis.c), stacklevel=2, ) self.decomposition_fns = [ self.decomp0, self.decomp1, self.decomp2_supercontrolled, self.decomp3_supercontrolled, ]
def actual_fidelity(self, **kwargs) -> float: """Calculates the actual fidelity of the decomposed circuit to the input unitary""" circ = self.circuit(**kwargs) trace = np.trace(Operator(circ).data.T.conj() @ self.unitary_matrix) return trace_to_fid(trace)
import numpy as np from qiskit.quantum_info.operators import Operator import qiskit ################################## 1. P_op ################################## # ################################## QSL 5.17 ################################# # The unitary U∗P is a permutation of the unitary U∗ that permutes the first and third input, # such that U∗P = P U∗P # This can also be modeled by simply swapping the inputs of qubits 4 and 6 into the unitary U∗, # and swapping them back afterwards. P_op = Operator([[1, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1]]) ################################## 2. G_op ################################## # ################################## QSL 5.18 ################################ # Deterministic Teleportation Protocol G_op = Operator([[0, 0, 0, -1], [0, 1, 0, 0], [0, 0, 1, 0], [-1, 0, 0, 0]]) ################################### 3. Us_op ################################## # ################################## QSL 5.25 ################################### # The scrambling unitary Us
def _commute(node1, node2): """Function to verify commutation relation between two nodes in the DAG Args: node1 (DAGnode): first node operation (attribute ['operation'] in the DAG) node2 (DAGnode): second node operation Return: bool: True if the gates commute and false if it is not the case. """ # Create set of qubits on which the operation acts qarg1 = [node1.qargs[i].index for i in range(0, len(node1.qargs))] qarg2 = [node2.qargs[i].index for i in range(0, len(node2.qargs))] # Create set of cbits on which the operation acts carg1 = [node1.qargs[i].index for i in range(0, len(node1.cargs))] carg2 = [node2.qargs[i].index for i in range(0, len(node2.cargs))] # Commutation for classical conditional gates if node1.condition or node2.condition: intersection = set(qarg1).intersection(set(qarg2)) if intersection or carg1 or carg2: commute_condition = False else: commute_condition = True return commute_condition # Commutation for measurement if node1.name == 'measure' or node2.name == 'measure': intersection_q = set(qarg1).intersection(set(qarg2)) intersection_c = set(carg1).intersection(set(carg2)) if intersection_q or intersection_c: commute_measurement = False else: commute_measurement = True return commute_measurement # Commutation for barrier-like directives directives = ['barrier', 'snapshot'] if node1.name in directives or node2.name in directives: intersection = set(qarg1).intersection(set(qarg2)) if intersection: commute_directive = False else: commute_directive = True return commute_directive # List of non commuting gates (TO DO: add more elements) non_commute_list = [set(['x', 'y']), set(['x', 'z'])] if qarg1 == qarg2 and (set([node1.name, node2.name]) in non_commute_list): return False # Create matrices to check commutation relation if no other criteria are matched qarg = list(set(node1.qargs + node2.qargs)) qbit_num = len(qarg) qarg1 = [qarg.index(q) for q in node1.qargs] qarg2 = [qarg.index(q) for q in node2.qargs] id_op = Operator(np.eye(2**qbit_num)) op12 = id_op.compose(node1.op, qargs=qarg1).compose(node2.op, qargs=qarg2) op21 = id_op.compose(node2.op, qargs=qarg2).compose(node1.op, qargs=qarg1) if_commute = (op12 == op21) return if_commute
def test_unitary_decomposition_via_definition(self): """Test decomposition for 1Q unitary via definition.""" mat = numpy.array([[0, 1], [1, 0]]) numpy.allclose(Operator(UnitaryGate(mat).definition).data, mat)