コード例 #1
0
ファイル: test_linalg.py プロジェクト: nguyenducnhaty/pyzx
 def test_solve_inhomogeneous_equation(self):
     b = Mat2([[1], [1], [0], [0], [0]])
     x = self.m3.solve(b)
     self.assertEqual(self.m3 * x, b)
     b = Mat2([[1], [0], [1], [1], [0]])
     x = self.m4.solve(b)
     self.assertEqual(self.m4 * x, b)
コード例 #2
0
ファイル: test_linalg.py プロジェクト: nguyenducnhaty/pyzx
 def setUp(self):
     self.m1 = Mat2([[1, 0], [1, 1]])
     self.m2 = Mat2([[1, 1], [1, 1]])
     self.m3 = Mat2([[1, 0, 1, 1, 0], [1, 1, 1, 0, 0], [1, 1, 0, 0, 1],
                     [0, 1, 0, 1, 0], [0, 0, 1, 1, 0]])
     self.m4 = Mat2([[1, 0, 1, 0, 0], [0, 1, 1, 0, 0], [1, 1, 0, 0, 1],
                     [0, 1, 0, 1, 0], [0, 0, 0, 1, 1]])
コード例 #3
0
 def update_matrix(self):
     self.matrix = Mat2.id(self.n_qubits)
     for gate in self.gates:
         if hasattr(gate, "name") and gate.name == "CNOT":
             self.matrix.row_add(gate.control, gate.target)
         else:
             print("Warning: CNOT tracker can only be used for circuits with only CNOT gates!")
コード例 #4
0
 def fromCircuit(circuit, initial_qubit_placement=None, final_qubit_placement=None):
     if isinstance(circuit, TketCircuit):
         circuit = tk_to_pyzx(circuit)
     zphases = {}
     current_parities = mat22partition(Mat2.id(circuit.qubits))
     if initial_qubit_placement is not None:
         current_parities = ["".join([row[i] for i in initial_qubit_placement]) for row in current_parities]
     for gate in circuit.gates:
         parity = current_parities[gate.target]
         if gate.name in ["CNOT", "CX"]:
             # Update current_parities
             control = current_parities[gate.control]
             current_parities[gate.target] = "".join([str((int(i)+int(j))%2) for i,j in zip(control, parity)])
         elif isinstance(gate, ZPhase):
             # Add the T rotation to the phases
             if parity in zphases:
                 zphases[parity] += gate.phase
             else: 
                 zphases[parity] = gate.phase
         else:
             print("Gate not supported!", gate.name)
     def clamp(phase):
         new_phase = phase%2
         if new_phase > 1:
             return new_phase -2
         return new_phase
     zphases = {par:clamp(r) for par, r in zphases.items() if clamp(r) != 0}
     if final_qubit_placement is not None:
         current_parities = [ current_parities[i] for i in final_qubit_placement]
     return PhasePoly(zphases, current_parities)
コード例 #5
0
 def Ariannes_synth(self, architecture, full=True, depth_aware=False, **kwargs):
     kwargs["full_reduce"] = True
     #print("------------ START! -----------------------")
     if architecture is None:
         architecture = create_architecture(FULLY_CONNECTED, n_qubits=len(self.out_par[0]))
     n_qubits = architecture.n_qubits
     # Obtain the parities
     parities_to_reach = self.all_parities
     # Make a matrix from the parities
     matrix = Mat2([[int(parity[i]) for parity in parities_to_reach] for i in range(architecture.n_qubits)] )
     circuit = CNOT_tracker(architecture.n_qubits)
     cols_to_reach = self._check_columns(matrix, circuit, [i for i in range(len(parities_to_reach))], parities_to_reach)
     self.prev_rows = None
     def place_cnot(control, target):
         # Place the CNOT on the circuit
         circuit.add_gate(CNOT(control, target))
         # Adjust the matrix accordingly - reversed elementary row operations
         matrix.row_add(target, control) 
     def base_recurse(cols_to_use, qubits_to_use):
         if qubits_to_use != [] and cols_to_use != []:
             # Select edge qubits
             qubits = architecture.non_cutting_vertices(qubits_to_use) 
             # Pick the qubit where the recursion split will be most skewed.
             chosen_row = max(qubits, key=lambda q: max([len([col for col in cols_to_use if matrix.data[q][col] == i]) for i in [1, 0]], default=-1))
             # Split the column into 1s and 0s in that row
             cols1 = [col for col in cols_to_use if matrix.data[chosen_row][col] == 1]
             cols0 = [col for col in cols_to_use if matrix.data[chosen_row][col] == 0]
             base_recurse(cols0, [q for q in qubits_to_use if q != chosen_row])
             one_recurse(cols1, qubits_to_use, chosen_row)
     def one_recurse(cols_to_use, qubits_to_use, qubit):
         if cols_to_use != []:
             neighbors = [q for q in architecture.get_neighboring_qubits(qubit) if q in qubits_to_use ]
             if neighbors == []:
                 print(qubits_to_use, qubit)
                 print(matrix)
                 exit(42)
             chosen_neighbor = max(neighbors, key=lambda q: len([col for col in cols_to_use if matrix.data[q][col] == 1]))
             # Place CNOTs if you still need to extract columns
             if sum([matrix.data[chosen_neighbor][c] for c in cols_to_use]) != 0: # Check if adding the cnot is useful
                 place_cnot(qubit, chosen_neighbor)
                 # Might have changed the matrix.
                 cols_to_use = self._check_columns(matrix, circuit, cols_to_use, parities_to_reach)  
             else: # Will never change the matrix
                 place_cnot(chosen_neighbor, qubit)
                 place_cnot(qubit, chosen_neighbor)
                 # Since the neighbor was all zeros, this is effectively a swap and no columns need to be checked.
             # Split the column into 1s and 0s in that row
             cols0 = [col for col in cols_to_use if matrix.data[qubit][col] == 0]
             cols1 = [col for col in cols_to_use if matrix.data[qubit][col] == 1]
             base_recurse(cols0, [q for q in qubits_to_use if q != qubit])
             one_recurse(cols1, qubits_to_use, qubit)
     base_recurse(cols_to_reach, [i for i in range(n_qubits)])
     if full:
         # Calculate the final parity that needs to be added from the circuit and self.out_par
        self._obtain_final_parities(circuit, architecture, **kwargs)
     # Return the circuit
     circuit.n_gadgets = len(self.zphases.keys())
     return circuit, [i for i in range(architecture.n_qubits)], [i for i in range(architecture.n_qubits)]
コード例 #6
0
 def gray_synth(self, architecture, full=True, **kwargs):
     if architecture is None:
         architecture = create_architecture(FULLY_CONNECTED, n_qubits=len(self.out_par[0]))
     n_qubits = architecture.n_qubits
     # Obtain the parities
     parities_to_reach = self.all_parities
     # Make a matrix from the parities
     matrix = Mat2([[int(parity[i]) for parity in parities_to_reach] for i in range(n_qubits)] )
     circuit = CNOT_tracker(n_qubits)
     # Make a stack - aka use the python stack >^.^<
     def recurse(cols_to_use, qubits_to_use, phase_qubit): # Arguments from the original paper
         # Check for finished columns
         cols_to_use = self._check_columns(matrix, circuit, cols_to_use, parities_to_reach)
         if cols_to_use != [] and qubits_to_use != []:   
             # Find all qubits (rows) with only 1s on the allowed parities (cols_to_use) 
             qubits = [i for i in qubits_to_use if sum([matrix.data[i][j] for j in cols_to_use]) == len(cols_to_use)]
             if len(qubits) > 1 and phase_qubit is not None:
                 # Pick the column with the most 1s to extrac the steiner tree with
                 column = max(cols_to_use, key=lambda c: sum([row[c] for row in matrix.data]))
                 col = [1 if i in qubits else 0 for i in range(n_qubits)]
                 cnots =  list(steiner_reduce_column(architecture, col, phase_qubit, set(qubits + [phase_qubit]), [i for i in range(n_qubits)], [], upper=True))
                 # For each returned CNOT:
                 for target, control in cnots:
                     # Place the CNOT on the circuit
                     circuit.add_gate(CNOT(control, target))
                     # Adjust the matrix accordingly - reversed elementary row operations
                     matrix.row_add(target, control)
                     # Keep track of the parities in the circuit - normal elementary row operations
                     cols_to_use = self._check_columns(matrix, circuit, cols_to_use, parities_to_reach)   
             # After placing the cnots do recursion
             if len(cols_to_use) > 0:
                 # Choose a row to split on
                 if len(cols_to_use) == 1: 
                     # Hack because it runs into infinite recursion otherwise, because we do not remove the chosen_row from qubits_to_use
                     # We do not remove the qubit, because it will skip columns 
                     # It skips columns because it runs out of qubits before all columns are finished for some reason - could not find the bug
                     qubits = [i for i in qubits_to_use if sum([matrix.data[i][j] for j in cols_to_use]) == 1]
                 else:
                     # Ignore rows that are currently all 0s or all 1s
                     qubits = [i for i in qubits_to_use if sum([matrix.data[i][j] for j in cols_to_use]) not in [0, len(cols_to_use)]]
                 # Pick the qubit where the recursion split will be most skewed.
                 chosen_row = max(qubits, key=lambda q: max([len([col for col in cols_to_use if matrix.data[q][col] == i]) for i in [1,0]], default=-1))
                 # Split the column into 1s and 0s in that row
                 cols1 = [col for col in cols_to_use if matrix.data[chosen_row][col] == 1]
                 cols0 = [col for col in cols_to_use if matrix.data[chosen_row][col] == 0]
                 #qubits_to_use = [i for i in qubits_to_use if i != chosen_row]
                 recurse(cols0, qubits_to_use, phase_qubit)
                 recurse(cols1, qubits_to_use, phase_qubit if phase_qubit is not None else chosen_row)
     # Put the base case into the python stack
     recurse([i for i in range(len(parities_to_reach))], [i for i in range(n_qubits)], None)
     if full:
         # Calculate the final parity that needs to be added from the circuit and self.out_par
        self._obtain_final_parities(circuit, architecture, **kwargs)
     # Return the circuit
     circuit.n_gadgets = len(self.zphases.keys())
     return circuit, [i for i in range(architecture.n_qubits)], [i for i in range(architecture.n_qubits)]
コード例 #7
0
 def _obtain_final_parities(self, circuit, architecture, **kwargs):
     # Calculate the final parity that needs to be added from the circuit and self.out_par
     current_parities = circuit.matrix
     output_parities = Mat2([[int(v) for v in row] for row in self.out_par])
     last_parities = output_parities*current_parities.inverse()
     # Do steiner-gauss to calculate necessary CNOTs and add those to the circuit.
     cnots = CNOT_tracker(architecture.n_qubits)
     gauss(last_parities, architecture, y=cnots, **kwargs) 
     for cnot in cnots.gates:
         circuit.add_gate(cnot)
コード例 #8
0
 def to_tket(self):
     if self.out_par != mat22partition(Mat2.id(self.n_qubits)):
         print(self.out_par)
         raise NotImplementedError("The linear transformation part of the phase polynomial cannot yet be transformed into a tket circuit")
     circuit = TketCircuit(self.n_qubits)
     for parity, phase in self.zphases.items():
         qubits = [i for i,s in enumerate(parity) if s == '1']
         circuit.add_pauliexpbox(PauliExpBox([Pauli.Z]*len(qubits), phase), qubits)
     # TODO add the linear combination part with CNOTs
     Transform.DecomposeBoxes().apply(circuit)
     return circuit
コード例 #9
0
def build_random_parity_map(qubits, n_cnots, circuit=None):
    """
    Builds a random parity map.

    :param qubits: The number of qubits that participate in the parity map
    :param n_cnots: The number of CNOTs in the parity map
    :param circuit: A (list of) circuit object(s) that implements a row_add() method to add the generated CNOT gates [optional]
    :return: a 2D numpy array that represents the parity map.
    """
    if circuit is None:
        circuit = []
    if not isinstance(circuit, list):
        circuit = [circuit]
    g = generate_cnots(qubits=qubits, depth=n_cnots)
    c = Circuit.from_graph(g)
    matrix = Mat2.id(qubits)
    for gate in c.gates:
        matrix.row_add(gate.control, gate.target)
        for c in circuit:
            c.row_add(gate.control, gate.target)
    return matrix.data
コード例 #10
0
def make_random_phase_poly(n_qubits, n_gadgets, return_circuit=False):
    parities = set()
    if n_gadgets > 2**n_qubits:
        n_gadgets=n_qubits^3
    if n_qubits < 26:
        for integer in np.random.choice(2**n_qubits-1, replace=False, size=n_gadgets):
            parities.add(integer+1)
    elif n_qubits < 64:
        while len(parities) < n_gadgets:
            parities.add(np.random.randint(1, 2**n_qubits))
    else:
        while len(parities) < n_gadgets:
            parities.add("".join(np.random.choice(["0", "1"], n_qubits, replace=True)))
    if n_qubits < 64:
        parities = [("{0:{fill}"+str(n_qubits)+"b}").format(integer, fill='0', align='right') for integer in parities]
    zphase_dict = {"".join([str(int(i)) for i in p]):Fraction(1,4) for p in parities}
    out_parities = mat22partition(Mat2.id(n_qubits))
    phase_poly = PhasePoly(zphase_dict, out_parities)
    if return_circuit:
        return route_phase_poly(phase_poly, create_architecture(FULLY_CONNECTED, n_qubits=n_qubits), do_matroid="arianne")
    return phase_poly
コード例 #11
0
 def __init__(self, n_qubits, **kwargs):
     super().__init__(n_qubits, **kwargs)
     self.matrix = Mat2.id(n_qubits)
     self.row_perm = np.arange(n_qubits)
     self.col_perm = np.arange(n_qubits)
     self.n_qubits = n_qubits
コード例 #12
0
def partition2mat2(partition):
    return Mat2([[int(i) for i in parity] for parity in partition])
コード例 #13
0
ファイル: test_linalg.py プロジェクト: nguyenducnhaty/pyzx
 def test_inverse(self):
     inv = self.m4.inverse()
     self.assertEqual(inv * self.m4, Mat2.id(5))
     self.assertEqual(self.m4 * inv, Mat2.id(5))
コード例 #14
0
ファイル: test_linalg.py プロジェクト: nguyenducnhaty/pyzx
 def test_matrix_multiplication(self):
     result = Mat2([[1, 1], [0, 0]])
     self.assertEqual(self.m1 * self.m2, result)
     result = Mat2([[0, 1], [0, 1]])
     self.assertEqual(self.m2 * self.m1, result)
コード例 #15
0
def rec_steiner_gauss(matrix,
                      architecture,
                      full_reduce=False,
                      x=None,
                      y=None,
                      permutation=None,
                      **kwargs):
    """
    Performs recursive Gaussian elimination that is constraint bij the given architecture
    
    :param matrix: PyZX Mat2 matrix to be reduced
    :param architecture: The Architecture object to conform to
    :param full_reduce: Whether to fully reduce or only create an upper triangular form
    :param x: 
    :param y: 
    """
    #print(matrix)
    if permutation is None:
        permutation = [i for i in range(len(matrix.data))]
    else:
        matrix = Mat2([[row[i] for i in permutation] for row in matrix.data])
    #print(matrix)
    def row_add(c0, c1):
        matrix.row_add(c0, c1)
        debug and print("Reducing", c0, c1)
        c0 = architecture.qubit_map[c0]
        c1 = architecture.qubit_map[c1]
        if x != None: x.row_add(c0, c1)
        if y != None: y.col_add(c1, c0)

    def steiner_reduce(col, root, nodes, usable_nodes, rec_nodes, upper):
        generator = steiner_reduce_column(architecture,
                                          [row[col] for row in matrix.data],
                                          root, nodes, usable_nodes, rec_nodes,
                                          upper)
        cnot = next(generator, None)
        while cnot is not None:
            row_add(*cnot)
            cnot = next(generator, None)

    def rec_step(cols, rows):
        size = len(rows)
        # Upper triangular form uses the same structure.
        p_cols = []
        pivot = 0
        rows2 = [r for r in range(len(matrix.data[0])) if r in rows]
        cols2 = [c for c in range(len(matrix.data)) if c in cols]
        for i, c in enumerate(cols2):
            if pivot < size:
                nodes = [
                    r for r in rows2[pivot:]
                    if rows2[pivot] == r or matrix.data[r][c] == 1
                ]
                steiner_reduce(c, rows2[pivot], nodes, cols2[i:], [], True)
                if matrix.data[rows2[pivot]][c] == 1:
                    p_cols.append(c)
                    pivot += 1
        # Full reduce requires the recursion
        if full_reduce:
            pivot -= 1
            for i, c in enumerate(cols):
                if c in p_cols:
                    nodes = [
                        r for r in rows
                        if r == rows[pivot] or matrix.data[r][c] == 1
                    ]
                    usable_nodes = cols[i:]
                    #rec_nodes = list(set([node for edge in architecture.distances["upper"][c][(max(usable_nodes),c)][1] for node in edge]))
                    #rec_nodes = [n for n in usable_nodes if n in rec_nodes]
                    rec_nodes = architecture.shortest_path(
                        c, max(usable_nodes), usable_nodes)

                    if len(nodes) > 1:
                        #print("-", c, nodes, cols, rec_nodes)
                        steiner_reduce(c, rows[pivot], nodes, cols, rec_nodes,
                                       False)

                    # Do recursion on the given nodes.
                    if len(rec_nodes) > 1:
                        rec_step(list(reversed(rec_nodes)), rec_nodes)
                pivot -= 1
        #return rank

    qubit_order = architecture.reduce_order
    rec_step(qubit_order, list(reversed(qubit_order)))
コード例 #16
0
 def __init__(self, n_qubits, **kwargs):
     super().__init__(n_qubits, **kwargs)
     self.matrix = Mat2(np.identity(n_qubits, dtype=np.int32).tolist())
     self.row_perm = np.arange(n_qubits)
     self.col_perm = np.arange(n_qubits)
     self.n_qubits = n_qubits