def test_location_second_type_error(self): circ = circuit.Circuit(3, [ _random_operation(0), _random_operation(0, 1), _random_operation(1), _random_operation(1, 2), _random_operation(2) ]) with self.assertRaisesRegex( TypeError, r'location_second is not integer-like \(found type: float\)'): transform.focus_operation_pair(circ, 3, 47.11)
def test_locations_not_sorted_error(self, location_first, location_second): circ = circuit.Circuit(3, [ _random_operation(0), _random_operation(0, 1), _random_operation(1), _random_operation(1, 2), _random_operation(2), ]) with self.assertRaisesRegex( ValueError, r'location_first not smaller than location_second:' r' 4 \(or -1\) vs 3 \(or -2\)'): transform.focus_operation_pair(circ, location_first, location_second)
def test_location_second_out_of_bounds_error(self, location_second): circ = circuit.Circuit(3, [ _random_operation(0), _random_operation(0, 1), _random_operation(1), _random_operation(1, 2), _random_operation(2), ]) with self.assertRaisesRegex( IndexError, r'location_second %d out of bounds for a Circuit of length 5' %location_second): transform.focus_operation_pair(circ, 3, location_second)
def test_positive(self, circ, att_circ_expected): assert len(att_circ_expected) == 2 location_first, location_second = att_circ_expected.locations() # call the function to be tested att_circ = transform.focus_operation_pair(circ, location_first, location_second) # check the type for att_circ self.assertIsInstance(att_circ, transform.AttentionCircuit) # check the focus for att_circ self.assertLen(att_circ, 2) self.assertTrue( _elementwise_is(att_circ.focus(), att_circ_expected.focus())) # check the locations for att_circ self.assertTupleEqual(att_circ.locations(), (location_first, location_second)) # check the context for att_circ self.assertTrue( _elementwise_is( att_circ.context().before().get_operation_sequence(), att_circ_expected.context().before().get_operation_sequence())) self.assertTrue( _elementwise_is( att_circ.context().between().get_operation_sequence(), att_circ_expected.context().between().get_operation_sequence()) ) self.assertTrue( _elementwise_is( att_circ.context().after().get_operation_sequence(), att_circ_expected.context().after().get_operation_sequence()))
def test_circ_type_error(self): with self.assertRaisesRegex( TypeError, r'circ is not a Circuit \(found type: range\)'): transform.focus_operation_pair(range(10), 3, 5)
def test_negative(self, circ, location_first, location_second): with self.assertRaises(transform.OperationsNotAlignedError): transform.focus_operation_pair(circ, location_first, location_second)
def scan_for_operation_pairs(circ): """Iterates over all attention circuits with two aligned operations in the focus. For each pair of operations in the circuit which are not separated by other operations, there will be one AttentionCircuit with only these two operations in the focus. The exact criterion for whether the operations are considered to be not separated by other operations is whether it is possible, by only exchanging trivially commuting operations, to transform the circuit such that there is no operation between them. Example: the two operations B and D (marked with a star) in the following circuit form such a pair. operation A on qubits [0, 1] operation B on qubits [1, 3] (*) operation C on qubits [0] operation D on qubits [3, 4] (*) operation E on qubits [1, 3] Operation C is no problem because it does not act on any qubit affected by B and D. Also, all operations before B or after D are no problem anyways. Note that "no operation in between acts on one of the qubits affected by one of the operations in the pair" is only a sufficient, but not a necessary criterion. For example, operations A' and C' in operation A' on qubits [1, 3] (*) operation B' on qubits [1] operation C' on qubits [3, 4] (*) still form a pair even though operation B' acts on qubit 1 which is affected by operation A'. The reason is that operation B' and C' commute trivially, so we can arrive at the circuit (A', C', B') for which the pair relation for operations A' and C' is obvious. This function always takes into account the minimal requirement to form a pair. This function is lazy, i.e. items are not computed before they are actually requested. Args: circ: the circuit to be scanned. Yields: all attention circuits with two aligned operations in the focus. Raises: TypeError: if circ is not a Circuit. """ if not isinstance(circ, circuit.Circuit): raise TypeError('circ is not a Circuit (found type: %s)' % type(circ).__name__) last_operation_on_qubit = np.full(circ.get_num_qubits(), -1) for curr_location, curr_operation in enumerate(circ): qubits = np.array(curr_operation.get_qubits()) # look up the locations for the latest operations (so far) on all qubits # affected by curr_operation prev_locations = np.unique(last_operation_on_qubit[qubits]) assert np.all(np.isin(prev_locations, range(-1, curr_location))) prev_locations = prev_locations[prev_locations >= 0] for prev_location in prev_locations: # we have a candidate # now, there is no better way than "trial and error" try: yield transform.focus_operation_pair(circ, prev_location, curr_location) except transform.OperationsNotAlignedError: continue # update last_operation_on_qubit to curr_location for all qubits affected # by curr_operation last_operation_on_qubit[qubits] = curr_location