Exemple #1
0
 def test_no_permutation_pattern(self):
     """Tests that an error is raised when when
     the linear function is not a permutation."""
     mat = [[1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0]]
     linear_function = LinearFunction(mat)
     with self.assertRaises(CircuitError):
         linear_function.permutation_pattern()
Exemple #2
0
 def test_permutation_pattern(self):
     """Tests that a permutation pattern is returned correctly when
     the linear function is a permutation."""
     mat = [[1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0]]
     linear_function = LinearFunction(mat)
     pattern = linear_function.permutation_pattern()
     self.assertIsInstance(pattern, np.ndarray)
Exemple #3
0
    def test_conversion_to_linear_function_and_back(self, num_qubits):
        """Test correctness of first synthesizing a linear circuit from a linear function,
        and then converting this linear circuit to a linear function."""
        rng = np.random.default_rng(5678)

        for _ in range(10):
            # create a random invertible binary matrix
            binary_matrix = random_invertible_binary_matrix(num_qubits,
                                                            seed=rng)

            # create a linear function with this matrix
            linear_function = LinearFunction(binary_matrix,
                                             validate_input=True)
            self.assertTrue(np.all(linear_function.linear == binary_matrix))

            # synthesize linear function
            synthesized_circuit = linear_function.definition
            self.assertIsInstance(synthesized_circuit, QuantumCircuit)

            # check that the synthesized linear function only contains CX and SWAP gates
            for inst, _, _ in synthesized_circuit.data:
                self.assertIsInstance(inst, (CXGate, SwapGate))

            # construct a linear function out of this linear circuit
            synthesized_linear_function = LinearFunction(synthesized_circuit,
                                                         validate_input=True)

            # check equivalence of the two linear matrices
            self.assertTrue(
                np.all(synthesized_linear_function.linear == binary_matrix))
Exemple #4
0
    def test_conversion_to_matrix_and_back(self, num_qubits):
        """Test correctness of first constructing a linear function from a linear quantum circuit,
        and then synthesizing this linear function to a quantum circuit."""
        rng = np.random.default_rng(1234)

        for _ in range(10):
            for num_gates in [0, 5, 5 * num_qubits]:
                # create a random linear circuit
                linear_circuit = random_linear_circuit(num_qubits,
                                                       num_gates,
                                                       seed=rng)
                self.assertIsInstance(linear_circuit, QuantumCircuit)

                # convert it to a linear function
                linear_function = LinearFunction(linear_circuit,
                                                 validate_input=True)

                # check that the internal matrix has right dimensions
                self.assertEqual(linear_function.linear.shape,
                                 (num_qubits, num_qubits))

                # synthesize linear function
                synthesized_linear_function = linear_function.definition
                self.assertIsInstance(synthesized_linear_function,
                                      QuantumCircuit)

                # check that the synthesized linear function only contains CX and SWAP gates
                for inst, _, _ in synthesized_linear_function.data:
                    self.assertIsInstance(inst, (CXGate, SwapGate))

                # check equivalence to the original function
                self.assertEqual(Operator(linear_circuit),
                                 Operator(synthesized_linear_function))
Exemple #5
0
    def test_patel_markov_hayes(self):
        """Checks the explicit example from Patel-Markov-Hayes's paper."""

        # This code is adapted from test_synthesis.py
        binary_matrix = [
            [1, 1, 0, 0, 0, 0],
            [1, 0, 0, 1, 1, 0],
            [0, 1, 0, 0, 1, 0],
            [1, 1, 1, 1, 1, 1],
            [1, 1, 0, 1, 1, 1],
            [0, 0, 1, 1, 1, 0],
        ]

        # Construct linear function from matrix: here, we copy a matrix
        linear_function_from_matrix = LinearFunction(binary_matrix,
                                                     validate_input=True)

        # Create the circuit displayed above:
        linear_circuit = QuantumCircuit(6)
        linear_circuit.cx(4, 3)
        linear_circuit.cx(5, 2)
        linear_circuit.cx(1, 0)
        linear_circuit.cx(3, 1)
        linear_circuit.cx(4, 2)
        linear_circuit.cx(4, 3)
        linear_circuit.cx(5, 4)
        linear_circuit.cx(2, 3)
        linear_circuit.cx(3, 2)
        linear_circuit.cx(3, 5)
        linear_circuit.cx(2, 4)
        linear_circuit.cx(1, 2)
        linear_circuit.cx(0, 1)
        linear_circuit.cx(0, 4)
        linear_circuit.cx(0, 3)

        # Construct linear function from matrix: we build a matrix
        linear_function_from_circuit = LinearFunction(linear_circuit,
                                                      validate_input=True)

        # Compare the matrices
        self.assertTrue(
            np.all(linear_function_from_circuit.linear ==
                   linear_function_from_matrix.linear))

        self.assertTrue(
            Operator(linear_function_from_matrix.definition) == Operator(
                linear_circuit))
Exemple #6
0
    def run(self, dag):
        """Run the CollectLinearFunctions pass on `dag`.

        Args:
            dag (DAGCircuit): the DAG to be optimized.

        Returns:
            DAGCircuit: the optimized DAG.
        """
        blocks = []

        cur_nodes = []
        for node in dag.topological_op_nodes():
            # If the current gate is not linear, we are done processing the current block
            if not self._is_linear_gate(node.op):
                self._finalize_processing_block(cur_nodes, blocks)
                cur_nodes = []

            else:
                # This is a linear gate, we add the node and its qubits
                cur_nodes.append(node)

        # Last block
        self._finalize_processing_block(cur_nodes, blocks)

        # Replace every discovered block by a linear function
        global_index_map = {wire: idx for idx, wire in enumerate(dag.qubits)}
        for cur_nodes in blocks:
            # Find the set of all qubits used in this block
            cur_qubits = set()
            for node in cur_nodes:
                cur_qubits.update(node.qargs)

            # For reproducibility, order these qubits compatibly with the global order
            sorted_qubits = sorted(cur_qubits,
                                   key=lambda x: global_index_map[x])
            wire_pos_map = dict(
                (qb, ix) for ix, qb in enumerate(sorted_qubits))

            # Construct a linear circuit
            qc = QuantumCircuit(len(cur_qubits))
            for node in cur_nodes:
                if node.op.name == "cx":
                    qc.cx(wire_pos_map[node.qargs[0]],
                          wire_pos_map[node.qargs[1]])
                elif node.op.name == "swap":
                    qc.swap(wire_pos_map[node.qargs[0]],
                            wire_pos_map[node.qargs[1]])

            # Create a linear function from this quantum circuit
            op = LinearFunction(qc)

            # Replace the block by the constructed circuit
            dag.replace_block_with_op(cur_nodes,
                                      op,
                                      wire_pos_map,
                                      cycle_check=False)

        return dag
Exemple #7
0
 def test_original_definition(self):
     """Tests that when a linear function is constructed from
     a QuantumCircuit, it saves the original definition."""
     linear_circuit = QuantumCircuit(4)
     linear_circuit.cx(0, 1)
     linear_circuit.cx(1, 2)
     linear_circuit.cx(2, 3)
     linear_function = LinearFunction(linear_circuit)
     self.assertIsNotNone(linear_function.original_circuit)
Exemple #8
0
 def test_bad_circuit_non_linear(self):
     """Tests that an error is raised if a circuit is not linear."""
     non_linear_circuit = QuantumCircuit(4)
     non_linear_circuit.cx(0, 1)
     non_linear_circuit.swap(2, 3)
     non_linear_circuit.h(2)
     non_linear_circuit.swap(1, 2)
     non_linear_circuit.cx(1, 3)
     with self.assertRaises(CircuitError):
         LinearFunction(non_linear_circuit)
Exemple #9
0
    def run(self, dag):
        """Run the CollectLinearFunctions pass on `dag`.

        Args:
            dag (DAGCircuit): the DAG to be optimized.

        Returns:
            DAGCircuit: the optimized DAG.
        """

        # collect blocks of linear gates
        blocks = collect_linear_blocks(dag)

        # refine blocks by splitting into disconnected components
        blocks = split_blocks_into_components(dag, blocks)

        # Replace every discovered block by a linear function
        global_index_map = {wire: idx for idx, wire in enumerate(dag.qubits)}
        for cur_nodes in blocks:
            # Create linear functions only out of blocks with at least 2 gates
            if len(cur_nodes) == 1:
                continue

            # Find the set of all qubits used in this block
            cur_qubits = set()
            for node in cur_nodes:
                cur_qubits.update(node.qargs)

            # For reproducibility, order these qubits compatibly with the global order
            sorted_qubits = sorted(cur_qubits,
                                   key=lambda x: global_index_map[x])
            wire_pos_map = dict(
                (qb, ix) for ix, qb in enumerate(sorted_qubits))

            # Construct a linear circuit
            qc = QuantumCircuit(len(cur_qubits))
            for node in cur_nodes:
                if node.op.name == "cx":
                    qc.cx(wire_pos_map[node.qargs[0]],
                          wire_pos_map[node.qargs[1]])
                elif node.op.name == "swap":
                    qc.swap(wire_pos_map[node.qargs[0]],
                            wire_pos_map[node.qargs[1]])

            # Create a linear function from this quantum circuit
            op = LinearFunction(qc)

            # Replace the block by the constructed circuit
            dag.replace_block_with_op(cur_nodes,
                                      op,
                                      wire_pos_map,
                                      cycle_check=False)

        return dag
    def test_single_linear_block(self):
        """Test that when all gates in a circuit are either CX or SWAP,
        we end up with a single LinearFunction."""

        # original circuit
        circuit = QuantumCircuit(4)
        circuit.cx(0, 1)
        circuit.cx(0, 2)
        circuit.cx(0, 3)
        circuit.swap(2, 3)
        circuit.cx(0, 1)
        circuit.cx(0, 3)

        # new circuit with linear functions extracted using transpiler pass
        optimized_circuit = PassManager(CollectLinearFunctions()).run(circuit)

        # check that this circuit consists of a single LinearFunction
        self.assertIn("linear_function", optimized_circuit.count_ops().keys())
        self.assertEqual(len(optimized_circuit.data), 1)
        inst1, _, _ = optimized_circuit.data[0]
        self.assertIsInstance(inst1, LinearFunction)

        # construct a circuit with linear function directly, without the transpiler pass
        expected_circuit = QuantumCircuit(4)
        expected_circuit.append(LinearFunction(circuit), [0, 1, 2, 3])

        # check that we have an equivalent circuit
        self.assertEqual(Operator(optimized_circuit),
                         Operator(expected_circuit))

        # now a circuit with linear functions synthesized
        synthesized_circuit = PassManager(
            LinearFunctionsSynthesis()).run(optimized_circuit)

        # check that there are no LinearFunctions present in synthesized_circuit
        self.assertNotIn("linear_function",
                         synthesized_circuit.count_ops().keys())

        # check that we have an equivalent circuit
        self.assertEqual(Operator(optimized_circuit),
                         Operator(synthesized_circuit))
Exemple #11
0
 def test_no_original_definition(self):
     """Tests that when a linear function is constructed from
     a matrix, there is no original definition."""
     mat = [[1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 0], [0, 0, 1, 0]]
     linear_function = LinearFunction(mat)
     self.assertIsNone(linear_function.original_circuit)
Exemple #12
0
 def test_is_not_permutation(self):
     """Tests that a permutation is detected correctly."""
     mat = [[1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 0, 1], [0, 0, 1, 0]]
     linear_function = LinearFunction(mat)
     self.assertFalse(linear_function.is_permutation())
Exemple #13
0
 def test_bad_matrix_non_invertible(self):
     """Tests that an error is raised if the matrix is not invertible."""
     mat = [[1, 0, 0], [0, 1, 1], [1, 1, 1]]
     with self.assertRaises(CircuitError):
         LinearFunction(mat, validate_input=True)
Exemple #14
0
 def test_bad_matrix_non_two_dimensional(self):
     """Tests that an error is raised if the matrix is not two-dimensional."""
     mat = [1, 0, 0, 1, 0]
     with self.assertRaises(CircuitError):
         LinearFunction(mat)
Exemple #15
0
 def test_bad_matrix_non_square(self):
     """Tests that an error is raised if the matrix is not square."""
     mat = [[1, 1, 0], [1, 0, 0], [0, 1, 0], [1, 1, 1]]
     with self.assertRaises(CircuitError):
         LinearFunction(mat)