def run(self, circuit: Circuit, data: dict[str, Any]) -> None: """Perform the pass's operation, see BasePass for more info.""" _logger.debug('Converting PauliGates to U3Gates.') for cycle, op in circuit.operations_with_cycles(): if isinstance(op.gate, PauliGate) and len(op.location, ) == 1: # Convert to SU(2) unitary = op.get_unitary().get_numpy() mag = np.linalg.det(unitary)**(-1 / 2) special_unitary = mag * unitary a = np.angle(special_unitary[1, 1]) b = np.angle(special_unitary[1, 0]) # Get angles theta = float( np.arctan2( np.abs(special_unitary[1, 0]), np.abs(special_unitary[0, 0]), ), ) * 2 phi = (a + b) lamb = (a - b) # Replace point = CircuitPoint(cycle, op.location[0]) circuit.replace_gate( point, U3Gate(), op.location, [theta, phi, lamb], )
def points(self) -> list[CircuitPoint]: """Return the points described by this region.""" return [ CircuitPoint(cycle_index, qudit_index) for qudit_index, bounds in self.items() for cycle_index in bounds.indices ]
def test_type_invalid_2(self, not_an_int: Any) -> None: circuit = Circuit(1) try: circuit.get_operation(CircuitPoint(not_an_int, 0)) except TypeError: return except BaseException: assert False, 'Unexpected Exception.'
def test_type_valid_4(self, an_int: int) -> None: circuit = Circuit(4, [2, 2, 3, 3]) try: circuit.get_operation(CircuitPoint(an_int, an_int)) except TypeError: assert False, 'Unexpected TypeError.' except BaseException: return
def test_type_valid_2(self, an_int: int) -> None: circuit = Circuit(1) try: circuit.is_point_in_range(CircuitPoint(an_int, an_int)) except TypeError: assert False, 'Unexpected TypeError.' except BaseException: return
def run(self, circuit: Circuit, data: dict[str, Any]) -> None: """Perform the pass's operation, see BasePass for more info.""" # Collect CircuitGate blocks blocks: list[tuple[CircuitPoint, Operation]] = [] for cycle, op in circuit.operations_with_cycles(): if isinstance(op.gate, CircuitGate): blocks.append((cycle, op)) # If a MachineModel is provided in the data dict, it will be used. # Otherwise all-to-all connectivity is assumed. model = None if 'machine_model' in data: model = data['machine_model'] if (not isinstance(model, MachineModel) or model.num_qudits < circuit.get_size()): _logger.warning( 'MachineModel not specified or invalid;' ' defaulting to all-to-all.', ) model = MachineModel(circuit.get_size()) subdata = data.copy() # Perform work points: list[CircuitPoint] = [] ops: list[Operation] = [] for cycle, op in blocks: gate: CircuitGate = op.gate subcircuit = gate._circuit.copy() subcircuit.set_params(op.params) subnumbering = {op.location[i]: i for i in range(len(op.location))} subdata['machine_model'] = MachineModel( len(op.location), model.get_subgraph(op.location, subnumbering), ) for loop_pass in self.loop_body: loop_pass.run(circuit, subdata) if self.replace_filter(subcircuit, op): points.append(CircuitPoint(cycle, op.location[0])) ops.append( Operation( CircuitGate(subcircuit, True), op.location, subcircuit.get_params(), ), ) # TODO: Load freshly written data from subdata into data circuit.batch_replace(points, ops)
def __lt__(self, other: object) -> bool: if CircuitPoint.is_point(other): # TODO: TypeGuard other = CircuitPoint(*other) # type: ignore if other[0] < self.min_cycle: return True if other[1] in self.keys(): return other[0] < self[other[1]].lower elif CircuitRegion.is_region(other): # TODO: TypeGuard other = CircuitRegion(other) # type: ignore if len(self.location.intersection(other.location)) != 0: lt = None for qudit in self.location.intersection(other.location): if lt is None: lt = self[qudit] < other[qudit] elif lt != self[qudit] < other[qudit]: raise ValueError('Both regions depend on each other.') assert lt is not None return lt lower_bounds = tuple(sorted({x.lower for x in self.values()})) other_lower_bounds = tuple( sorted({x.lower for x in other.values()}), ) upper_bounds = tuple( reversed(sorted({x.upper for x in self.values()})), ) other_upper_bounds = tuple( reversed(sorted({x.upper for x in other.values()})), ) return (lower_bounds, upper_bounds) < ( other_lower_bounds, other_upper_bounds, ) return NotImplemented
def overlaps(self, other: CircuitPointLike | CircuitRegionLike) -> bool: """Return true if `other` overlaps this region.""" if CircuitPoint.is_point(other): other = CircuitPoint(*other) if other.qudit not in self: return False bounds = self[other.qudit] return bounds.lower <= other.cycle <= bounds.upper if CircuitRegion.is_region(other): # TODO: Typeguard other = CircuitRegion(other) # type: ignore if self.min_cycle > other.max_cycle: return False if self.max_cycle < other.min_cycle: return False qudit_intersection = self.location.intersection(other.location) if len(qudit_intersection) == 0: return False for qudit in qudit_intersection: if (self[qudit].lower < other[qudit].upper and self[qudit].upper > other[qudit].lower): return True return False raise TypeError( 'Expected either CircuitPoint or CircuitRegion, got %s.' % type(other), )
def __contains__(self, other: object) -> bool: if is_integer(other): return other in self._bounds.keys() if CircuitPoint.is_point(other): # TODO: TypeGuard return other[1] in self.keys() and other[0] in self[ other[1]] # type: ignore # noqa if CircuitRegion.is_region(other): # TODO: TypeGuard other = CircuitRegion(other) # type: ignore return ( all(qudit in self.keys() for qudit in other.keys()) and all( self[qudit].lower <= other[qudit][0] <= self[qudit].upper for qudit in self.keys()) and all(self[qudit].lower <= other[qudit][1] <= self[qudit].upper for qudit in self.keys())) return NotImplemented
class TestFold: """This tests `circuit.fold`.""" @pytest.mark.parametrize( 'points', [ [(0, 0)], [(0, 0), (1, 2)], [CircuitPoint(0, 0), (1, 2)], [(0, 0), CircuitPoint(1, 2)], [CircuitPoint(0, 0), CircuitPoint(1, 2)], ], ) def test_type_valid(self, points: Sequence[CircuitPointLike]) -> None: circuit = Circuit(4, [2, 2, 3, 3]) try: circuit.fold(circuit.get_region(points)) except TypeError: assert False, 'Unexpected TypeError.' except BaseException: return @pytest.mark.parametrize( 'not_points', [ 5, [1, 2], [1, 'a'], 'abc', ], ) def test_type_invalid(self, not_points: Any) -> None: circuit = Circuit(4, [2, 2, 3, 3]) with pytest.raises(TypeError): circuit.fold(not_points) @pytest.mark.parametrize( 'points', [ [(0, 0)], [(0, 0), (1, 2)], [CircuitPoint(0, 0), (1, 2)], [(0, 0), CircuitPoint(1, 2)], [CircuitPoint(0, 0), CircuitPoint(1, 2)], ], ) def test_invalid_points(self, points: Sequence[CircuitPointLike]) -> None: circuit = Circuit(4, [2, 2, 3, 3]) with pytest.raises(IndexError): circuit.fold(circuit.get_region(points)) # def test_empty(self, r6_qudit_circuit: Circuit) -> None: # num_ops = r6_qudit_circuit.get_num_operations() # gate_set = r6_qudit_circuit.get_gate_set() # r6_qudit_circuit.fold(r6_qudit_circuit.get_region([])) # assert num_ops == r6_qudit_circuit.get_num_operations() # assert gate_set == r6_qudit_circuit.get_gate_set() @pytest.mark.parametrize( 'points', [ [(0, 0), (3, 0)], [(3, 0), (0, 0)], [(0, 0), (2, 0)], [(2, 0), (0, 0)], ], ) def test_invalid_fold(self, points: Sequence[CircuitPointLike]) -> None: circuit = Circuit(4) wide_gate = IdentityGate(4) circuit.append_gate(wide_gate, [0, 1, 2, 3]) circuit.append_gate(wide_gate, [0, 1, 2, 3]) circuit.append_gate(wide_gate, [0, 1, 2, 3]) circuit.append_gate(wide_gate, [0, 1, 2, 3]) with pytest.raises(ValueError): circuit.fold(circuit.get_region(points)) def test_correctness_1(self) -> None: circuit = Circuit(4) wide_gate = IdentityGate(4) circuit.append_gate(wide_gate, [0, 1, 2, 3]) circuit.append_gate(wide_gate, [0, 1, 2, 3]) circuit.append_gate(wide_gate, [0, 1, 2, 3]) circuit.append_gate(wide_gate, [0, 1, 2, 3]) assert circuit.get_num_operations() == 4 assert circuit.get_depth() == 4 utry = circuit.get_unitary() circuit.fold(circuit.get_region([(0, 0), (1, 0)])) assert circuit.get_num_operations() == 3 assert circuit.get_depth() == 3 check_no_idle_cycles(circuit) for q in range(4): assert isinstance(circuit[0, q].gate, CircuitGate) for c in range(1, 3, 1): for q in range(4): assert isinstance(circuit[c, q].gate, IdentityGate) assert isinstance(circuit[c, q].gate, IdentityGate) test_gate: CircuitGate = circuit[0, 0].gate # type: ignore assert test_gate._circuit.get_num_operations() == 2 assert test_gate._circuit.get_num_cycles() == 2 for q in range(4): assert isinstance(test_gate._circuit[0, q].gate, IdentityGate) assert isinstance(test_gate._circuit[1, q].gate, IdentityGate) circuit.fold(circuit.get_region([(1, 0), (2, 0)])) assert circuit.get_num_operations() == 2 assert circuit.get_depth() == 2 check_no_idle_cycles(circuit) for c in range(2): for q in range(4): assert isinstance(circuit[c, q].gate, CircuitGate) test_gate: CircuitGate = circuit[0, 0].gate # type: ignore assert test_gate._circuit.get_num_operations() == 2 assert test_gate._circuit.get_num_cycles() == 2 for q in range(4): assert isinstance(test_gate._circuit[0, q].gate, IdentityGate) assert isinstance(test_gate._circuit[1, q].gate, IdentityGate) test_gate: CircuitGate = circuit[1, 0].gate # type: ignore assert test_gate._circuit.get_num_operations() == 2 assert test_gate._circuit.get_num_cycles() == 2 for q in range(4): assert isinstance(test_gate._circuit[0, q].gate, IdentityGate) assert isinstance(test_gate._circuit[1, q].gate, IdentityGate) circuit.fold(circuit.get_region([(0, 0), (1, 0)])) assert circuit.get_num_operations() == 1 assert circuit.get_depth() == 1 check_no_idle_cycles(circuit) for q in range(4): assert isinstance(circuit[0, q].gate, CircuitGate) test_gate: CircuitGate = circuit[0, 0].gate # type: ignore assert test_gate._circuit.get_num_operations() == 2 assert test_gate._circuit.get_num_cycles() == 2 for q in range(4): assert isinstance(test_gate._circuit[0, q].gate, CircuitGate) assert isinstance(test_gate._circuit[1, q].gate, CircuitGate) inner_gate1: CircuitGate = test_gate._circuit[0, 0].gate # type: ignore inner_gate2: CircuitGate = test_gate._circuit[1, 0].gate # type: ignore assert inner_gate1._circuit.get_num_operations() == 2 assert inner_gate1._circuit.get_num_cycles() == 2 for q in range(4): assert isinstance(inner_gate1._circuit[0, q].gate, IdentityGate) assert isinstance(inner_gate1._circuit[1, q].gate, IdentityGate) assert isinstance(inner_gate2._circuit[0, q].gate, IdentityGate) assert isinstance(inner_gate2._circuit[1, q].gate, IdentityGate) check_no_idle_cycles(circuit) assert np.allclose(utry.get_numpy(), circuit.get_unitary().get_numpy()) def test_correctness_2(self) -> None: circuit = Circuit(3) wide_gate = IdentityGate(3) circuit.append_gate(HGate(), [0]) circuit.append_gate(CNOTGate(), [1, 2]) circuit.append_gate(wide_gate, [0, 1, 2]) utry = circuit.get_unitary() circuit.fold(circuit.get_region([(0, 1), (1, 0)])) assert circuit.get_num_operations() == 2 assert circuit.get_depth() == 2 assert circuit[0, 0].gate is HGate() assert isinstance(circuit[1, 0].gate, CircuitGate) test_gate: CircuitGate = circuit[1, 0].gate assert test_gate._circuit[0, 1].gate is CNOTGate() assert test_gate._circuit[0, 2].gate is CNOTGate() assert test_gate._circuit[1, 0].gate is wide_gate assert test_gate._circuit[1, 1].gate is wide_gate assert test_gate._circuit[1, 2].gate is wide_gate check_no_idle_cycles(circuit) assert np.allclose(utry.get_numpy(), circuit.get_unitary().get_numpy()) def test_correctness_3(self) -> None: circuit = Circuit(5) wide_gate = IdentityGate(3) circuit.append_gate(HGate(), [1]) circuit.append_gate(CNOTGate(), [2, 3]) circuit.append_gate(wide_gate, [1, 2, 3]) circuit.append_gate(CNOTGate(), [1, 2]) circuit.append_gate(HGate(), [3]) circuit.append_gate(XGate(), [0]) circuit.append_gate(XGate(), [0]) circuit.append_gate(XGate(), [0]) circuit.append_gate(XGate(), [4]) circuit.append_gate(XGate(), [4]) circuit.append_gate(XGate(), [4]) utry = circuit.get_unitary() circuit.fold(circuit.get_region([(0, 2), (1, 1), (2, 1)])) assert circuit.get_num_operations() == 9 assert circuit.get_depth() == 3 assert circuit.count(HGate()) == 2 assert circuit.count(XGate()) == 6 assert isinstance(circuit[1, 1].gate, CircuitGate) test_gate: CircuitGate = circuit[1, 1].gate assert test_gate._circuit[0, 1].gate is CNOTGate() assert test_gate._circuit[0, 2].gate is CNOTGate() assert test_gate._circuit[1, 0].gate is wide_gate assert test_gate._circuit[1, 1].gate is wide_gate assert test_gate._circuit[1, 2].gate is wide_gate check_no_idle_cycles(circuit) assert np.allclose(utry.get_numpy(), circuit.get_unitary().get_numpy()) def test_parameters(self) -> None: circ = Circuit(2) circ.append_gate(CNOTGate(), [1, 0]) circ.append_gate(U3Gate(), [0], [0, 0, 0.23]) circ.append_gate(CNOTGate(), [1, 0]) before_fold = circ.get_unitary() circ.fold(circ.get_region([(0, 0), (1, 0), (2, 0)])) after_fold = circ.get_unitary() assert after_fold == before_fold
def __init__( self, circuit: Circuit, start: CircuitPointLike = CircuitPoint(0, 0), end: CircuitPointLike | None = None, qudits_or_region: CircuitRegionLike | Sequence[int] | None = None, exclude: bool = False, reverse: bool = False, and_cycles: bool = False, ) -> None: """ Construct a CircuitIterator. Args: circuit (Circuit): The circuit to iterate through. start (CircuitPointLike): Only iterate through points greater than or equal to `start`. Defaults to start at the beginning of the circuit. (Default: (0, 0)) end (CircuitPointLike | None): Only iterate through points less than or equal to this. If left as None, iterates until the end of the circuit. (Default: None) qudits_or_region (CircuitRegionLike | Sequence[int] | None): Determines the way the circuit is iterated. If a region is given, then iterate through operations in the region. If a sequence of qudit indices is given, then only iterate the operations touching those qudits. If left as None, then iterate through the entire circuit in simulation order. (Default: None) exclude (bool): If iterating through a region or only some qudits and `exclude` is true, then do not yield operations that are only partially in the region or on the desired qudits. This may result in a sequence of operations that does not occur in simulation order in the circuit. (Default: False) reverse (bool): Reverse the ordering. If true, then end acts as start and vice versa. (Default: False) and_cycles (bool): If true, in addition to the operation, return the cycle index where it was found. (Default: False) """ if not CircuitPoint.is_point(start): raise TypeError(f'Expected point for start, got {type(start)}.') if end is not None and not CircuitPoint.is_point(end): raise TypeError(f'Expected point for end, got {type(end)}.') if end is None: end = CircuitPoint( circuit.get_num_cycles() - 1, circuit.get_size() - 1, ) self.circuit = circuit self.start = CircuitPoint(*start) self.end = CircuitPoint(*end) self.exclude = exclude self.reverse = reverse self.and_cycles = and_cycles # Set mode of iteration: if qudits_or_region is None: # iterate through the entire circuit normally self.qudits = list(range(self.circuit.get_size())) self.region = CircuitRegion({ qudit: (0, self.circuit.get_num_cycles()) for qudit in self.qudits }) elif CircuitRegion.is_region(qudits_or_region): # TODO: Typeguard # iterate through the region in the circuit self.qudits = list(qudits_or_region.keys()) # type: ignore self.region = CircuitRegion(qudits_or_region) # type: ignore elif is_sequence(qudits_or_region): # iterate through the circuit but only on the specified qudits if not all(is_integer(qudit) for qudit in qudits_or_region): raise TypeError('Expected region or sequence of indices.') if not all(0 <= qudit < self.circuit.get_size() for qudit in qudits_or_region): raise ValueError('Invalid sequence of qudit indices.') self.qudits = list(qudits_or_region) self.region = CircuitRegion({ qudit: (0, self.circuit.get_num_cycles()) for qudit in self.qudits }) self.max_qudit = max(self.qudits) self.min_qudit = min(self.qudits) self.min_cycle = self.region.min_cycle self.max_cycle = self.region.max_cycle if start < (self.min_cycle, self.min_qudit): start = CircuitPoint(self.min_cycle, self.min_qudit) if end > (self.max_cycle, self.max_qudit): end = CircuitPoint(self.max_cycle, self.max_qudit) assert isinstance(start, CircuitPoint) # TODO: Typeguard assert isinstance(end, CircuitPoint) # TODO: Typeguard # Pointer into the circuit structure self.cycle = start.cycle if not self.reverse else end.cycle self.qudit = start.qudit if not self.reverse else end.qudit # Used to track changes to circuit structure self.num_ops = self.circuit.get_num_operations() self.num_cycles = self.circuit.get_num_cycles() self.num_qudits = self.circuit.get_size() # Ensure operations are only returned once self.qudits_to_skip: set[int] = set()
def run(self, circuit: Circuit, data: dict[str, Any]) -> None: """Perform the pass's operation, see BasePass for more info.""" # Collect synthesizable operations ops_to_syn: list[tuple[int, Operation]] = [] for cycle, op in circuit.operations_with_cycles(): if self.collection_filter(op): ops_to_syn.append((cycle, op)) # If a MachineModel is provided in the data dict, it will be used. # Otherwise all-to-all connectivity is assumed. model = None if 'machine_model' in data: model = data['machine_model'] if ( not isinstance(model, MachineModel) or model.num_qudits < circuit.get_size() ): _logger.warning( 'MachineModel not specified or invalid;' ' defaulting to all-to-all.', ) model = MachineModel(circuit.get_size()) sub_data = data.copy() structure_list: list[Sequence[int]] = [] # Synthesize operations errors: list[float] = [] points: list[CircuitPoint] = [] new_ops: list[Operation] = [] num_blocks = len(ops_to_syn) for block_num, (cycle, op) in enumerate(ops_to_syn): sub_numbering = {op.location[i]: i for i in range(op.size)} sub_data['machine_model'] = MachineModel( len(op.location), model.get_subgraph(op.location, sub_numbering), ) structure_list.append([op.location[i] for i in range(op.size)]) syn_circuit = self.synthesize(op.get_unitary(), sub_data) if self.checkpoint_dir is not None: save_checkpoint(syn_circuit, self.checkpoint_dir, block_num) if self.replace_filter(syn_circuit, op): # Calculate errors new_utry = syn_circuit.get_unitary() old_utry = op.get_unitary() error = new_utry.get_distance_from(old_utry) errors.append(error) points.append(CircuitPoint(cycle, op.location[0])) new_ops.append( Operation( CircuitGate(syn_circuit, True), op.location, list(syn_circuit.get_params()), # TODO: RealVector ), ) _logger.info( f'Error in synthesized CircuitGate {block_num+1} of ' f'{num_blocks}: {error}', ) data['synthesispass_error_sum'] = sum(errors) # TODO: Might be replaced _logger.info( 'Synthesis pass completed. Upper bound on ' f"circuit error is {data['synthesispass_error_sum']}", ) if self.checkpoint_dir is not None: with open(f'{self.checkpoint_dir}/structure.pickle', 'wb') as f: dump(structure_list, f) circuit.batch_replace(points, new_ops)