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()
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)
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))
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))
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))
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
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)
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)
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))
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)
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())
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)
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)
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)