From 880d8a277a7a0e6991a7e2dda087e71e5c85df99 Mon Sep 17 00:00:00 2001 From: Justin Kalloor Date: Fri, 25 Oct 2024 12:41:16 -0700 Subject: [PATCH] Reverting some changes --- bqskit/compiler/passdata.py | 25 ++++- bqskit/passes/control/foreach.py | 36 +------ bqskit/passes/io/intermediate.py | 157 +++++++++++++------------------ 3 files changed, 93 insertions(+), 125 deletions(-) diff --git a/bqskit/compiler/passdata.py b/bqskit/compiler/passdata.py index 7f6bf785f..23d584712 100644 --- a/bqskit/compiler/passdata.py +++ b/bqskit/compiler/passdata.py @@ -63,14 +63,13 @@ def __init__(self, circuit: Circuit) -> None: def target(self) -> StateVector | UnitaryMatrix | StateSystem: """Return the current target unitary or state.""" if isinstance(self._target, Circuit): - if self._target.num_qudits <= 8: - self._target = self._target.get_unitary() + self._target = self._target.get_unitary() return self._target @target.setter - def target(self, _val: StateVector | UnitaryMatrix | StateSystem | Circuit) -> None: - if not isinstance(_val, (StateVector, UnitaryMatrix, StateSystem, Circuit)): + def target(self, _val: StateVector | UnitaryMatrix | StateSystem) -> None: + if not isinstance(_val, (StateVector, UnitaryMatrix, StateSystem)): raise TypeError( f'Cannot assign type {type(_val)} to target.' ' Expected either a StateVector, StateSystem,' @@ -253,6 +252,24 @@ def __contains__(self, _o: object) -> bool: in_data = self._data.__contains__(_o) return in_resv or in_data + def update(self, other: Any = (), /, **kwds: Any) -> None: + """Update the data with key-values pairs from `other` and `kwds`.""" + if isinstance(other, PassData): + for key in other: + # Handle target specially to avoid circuit evaluation + if key == 'target': + self._target = other._target + continue + + self[key] = other[key] + + for key, value in kwds.items(): + self[key] = value + + return + + super().update(other, **kwds) + def copy(self) -> PassData: """Returns a deep copy of the data.""" return copy.deepcopy(self) diff --git a/bqskit/passes/control/foreach.py b/bqskit/passes/control/foreach.py index f59857b14..77f872c94 100644 --- a/bqskit/passes/control/foreach.py +++ b/bqskit/passes/control/foreach.py @@ -8,7 +8,7 @@ from os.path import join, exists from pathlib import Path -# from bqskit.compiler.basepass import _sub_do_work +from bqskit.compiler.basepass import _sub_do_work from collections import Counter from bqskit.compiler.basepass import BasePass from bqskit.compiler.machine import MachineModel @@ -72,10 +72,7 @@ def __init__( collection_filter: Callable[[Operation], bool] | None = None, replace_filter: ReplaceFilterFn | str = 'always', batch_size: int | None = None, - blocks_to_run: List[int] = [], - allocate_error: bool = False, - allocate_error_gate: Gate = CNOTGate(), - allocate_skew_factor: int = 3 + blocks_to_run: List[int] = [] ) -> None: """ Construct a ForEachBlockPass. @@ -159,9 +156,6 @@ def __init__( self.replace_filter = replace_filter or default_replace_filter self.workflow = Workflow(loop_body) self.blocks_to_run = sorted(blocks_to_run) - self.allocate_error = allocate_error - self.allocate_error_gate = allocate_error_gate - self.allocate_skew_factor = allocate_skew_factor self.error_cost_gen = error_cost_gen if not callable(self.collection_filter): raise TypeError( @@ -198,7 +192,7 @@ async def run(self, circuit: Circuit, data: PassData) -> None: if self.key not in data: data[self.key] = [] - # Collect blocks + # Collect blocks to run with blocks: list[tuple[int, Operation]] = [] if (len(self.blocks_to_run) == 0): self.blocks_to_run = list(range(circuit.num_operations)) @@ -218,7 +212,7 @@ async def run(self, circuit: Circuit, data: PassData) -> None: if len(blocks) == 0: data[self.key].append([]) return - + # Get the machine model model = data.model coupling_graph = data.connectivity @@ -226,7 +220,6 @@ async def run(self, circuit: Circuit, data: PassData) -> None: # Preprocess blocks subcircuits: list[Circuit] = [] block_datas: list[PassData] = [] - block_gates = [] for i, (cycle, op) in enumerate(blocks): # Set up checkpoint data and circuit files # Need to zero pad block ids for consistency @@ -301,8 +294,7 @@ async def run(self, circuit: Circuit, data: PassData) -> None: _sub_do_work, [self.workflow] * len(subcircuits), subcircuits, - block_datas, - cost=self.error_cost_gen, + block_datas ) # Unpack results @@ -345,24 +337,6 @@ async def run(self, circuit: Circuit, data: PassData) -> None: if self.calculate_error_bound: _logger.debug(f'New circuit error is {data.error}.') - -async def _sub_do_work( - workflow: Workflow, - circuit: Circuit, - data: PassData, - cost: CostFunctionGenerator, -) -> tuple[Circuit, PassData]: - """Execute a sequence of passes on circuit.""" - if 'calculate_error_bound' in data and data['calculate_error_bound']: - old_utry = circuit.get_unitary() - - await workflow.run(circuit, data) - - if 'calculate_error_bound' in data and data['calculate_error_bound']: - data.error = cost.calc_cost(circuit, old_utry) - - return circuit, data - def default_collection_filter(op: Operation) -> bool: return isinstance( op.gate, ( diff --git a/bqskit/passes/io/intermediate.py b/bqskit/passes/io/intermediate.py index 85fbb2344..d648ec2ff 100644 --- a/bqskit/passes/io/intermediate.py +++ b/bqskit/passes/io/intermediate.py @@ -25,14 +25,6 @@ _logger = logging.getLogger(__name__) - -def contains_subdirectory_with_os_listdir(directory): - for item in listdir(directory): - item_path = join(directory, item) - if isdir(item_path): - return True - return False - class SaveIntermediatePass(BasePass): """ The SaveIntermediate class. @@ -43,9 +35,9 @@ class SaveIntermediatePass(BasePass): def __init__( self, - project_dir: str, + path_to_save_dir: str, + project_name: str | None = None, save_as_qasm: bool = True, - overwrite: bool = False ) -> None: """ Constructor for the SaveIntermediatePass. @@ -59,21 +51,30 @@ def __init__( Raises: ValueError: If `path_to_save_dir` is not an existing directory. """ - if exists(project_dir): - if not overwrite: - _logger.error("Directory already exists!") - return - self.pathdir = project_dir + if exists(path_to_save_dir): + self.pathdir = path_to_save_dir if self.pathdir[-1] != '/': self.pathdir += '/' else: - Path(project_dir).mkdir(parents=True, exist_ok=True) + raise ValueError( + f'Path {path_to_save_dir} does not exist', + ) + self.projname = project_name if project_name is not None \ + else 'unnamed_project' + + enum = 1 + if exists(self.pathdir + self.projname): + while exists(self.pathdir + self.projname + f'_{enum}'): + enum += 1 + self.projname += f'_{enum}' _logger.warning( - f'Path {project_dir} does not exist', + f'Path {path_to_save_dir} already exists, ' + f'saving to {self.pathdir + self.projname} ' + 'instead.', ) - self.pathdir = project_dir - # mkdir(join(self.pathdir,self.projname)) + mkdir(self.pathdir + self.projname) + self.as_qasm = save_as_qasm async def run(self, circuit: Circuit, data: PassData) -> None: @@ -86,7 +87,8 @@ async def run(self, circuit: Circuit, data: PassData) -> None: blocks_to_save.append((enum, op)) # Set up path and file names - structure_file = join(self.pathdir, 'structure.pickle') + structure_file = self.pathdir + self.projname + '/structure.pickle' + block_skeleton = self.pathdir + self.projname + '/block_' num_digits = len(str(len(blocks_to_save))) structure_list: list[list[int]] = [] @@ -106,13 +108,13 @@ async def run(self, circuit: Circuit, data: PassData) -> None: block.params, ) subcircuit.unfold((0, 0)) + await ToU3Pass().run(subcircuit, PassData(subcircuit)) if self.as_qasm: - await ToU3Pass().run(subcircuit, PassData(subcircuit)) - with open(join(self.pathdir, f'block_{enum}.qasm'), 'w') as f: + with open(block_skeleton + f'{enum}.qasm', 'w') as f: f.write(OPENQASM2Language().encode(subcircuit)) else: with open( - join(self.pathdir, f'block_{enum}.pickle'), 'wb', + f'{block_skeleton}{enum}.pickle', 'wb', ) as f: pickle.dump(subcircuit, f) @@ -121,7 +123,7 @@ async def run(self, circuit: Circuit, data: PassData) -> None: class RestoreIntermediatePass(BasePass): - def __init__(self, project_directory: str, load_blocks: bool = True, as_circuit_gate: bool = False): + def __init__(self, project_directory: str, load_blocks: bool = True): """ Constructor for the RestoreIntermediatePass. @@ -134,43 +136,46 @@ def __init__(self, project_directory: str, load_blocks: bool = True, as_circuit_ the user must explicitly call load_blocks() themselves. Defaults to True. - as_circuit_gate (bool): If True, blocks are reloaded as a circuit - gate rather than a circuit. - Raises: ValueError: If `project_directory` does not exist or if `structure.pickle` is invalid. """ self.proj_dir = project_directory - # self.block_list: list[str] = [] - self.as_circuit_gate = as_circuit_gate + if not exists(self.proj_dir): + raise TypeError( + f"Project directory '{self.proj_dir}' does not exist.", + ) + if not exists(self.proj_dir + '/structure.pickle'): + raise TypeError( + f'Project directory `{self.proj_dir}` does not ' + 'contain `structure.pickle`.', + ) + + with open(self.proj_dir + '/structure.pickle', 'rb') as f: + self.structure = pickle.load(f) + + if not isinstance(self.structure, list): + raise TypeError('The provided `structure.pickle` is not a list.') - self.load_blocks = load_blocks + self.block_list: list[str] = [] + if load_blocks: + self.reload_blocks() - def reload_blocks(proj_dir: str, structure: list) -> tuple[list[str], bool]: + def reload_blocks(self) -> None: """ Updates the `block_list` variable with the current contents of the `proj_dir`. - + Raises: ValueError: if there are more block files than indices in the `structure.pickle`. """ - files = sorted(listdir(proj_dir)) - # Files are of the form block_*.pickle or block_*.qasm - block_list = [f for f in files if 'block_' in f] - pickle_list = [f for f in block_list if ".pickle" in f] - saved_as_qasm = False - if len(pickle_list) == 0: - saved_as_qasm = True - block_list = [f for f in block_list if ".qasm" in f] - else: - block_list = pickle_list - if len(block_list) > len(structure): + files = listdir(self.proj_dir) + self.block_list = [f for f in files if 'block_' in f] + if len(self.block_list) > len(self.structure): raise ValueError( - f'More block files ({len(block_list), len(pickle_list)}) than indices ({len(structure)}) in `{proj_dir}/structure.pickle` {block_list}', + 'More block files than indicies in `structure.pickle`', ) - return block_list, saved_as_qasm async def run(self, circuit: Circuit, data: PassData) -> None: """ @@ -180,51 +185,24 @@ async def run(self, circuit: Circuit, data: PassData) -> None: ValueError: if a block file and the corresponding index in `structure.pickle` are differnt lengths. """ - - if not exists(self.proj_dir): - raise TypeError( - f"Project directory '{self.proj_dir}' does not exist.", - ) - if not exists(self.proj_dir + '/structure.pickle'): - raise TypeError( - f'Project directory `{self.proj_dir}` does not ' - 'contain `structure.pickle`.', - ) - - with open(self.proj_dir + '/structure.pickle', 'rb') as f: - structure = pickle.load(f) - - if not isinstance(structure, list): - raise TypeError('The provided `structure.pickle` is not a list.') - - block_list, saved_as_qasm = RestoreIntermediatePass.reload_blocks(self.proj_dir, structure) - - # Get circuit from checkpoint, ignore previous circuit - new_circuit = Circuit(circuit.num_qudits, circuit.radixes) - for block in block_list: - # Get block - block_num = int(findall(r'\d+', block)[0]) - if saved_as_qasm: - with open(join(self.proj_dir, block)) as f: + # If the circuit is empty, just append blocks in order + if circuit.depth == 0: + for block in self.block_list: + # Get block + block_num = int(findall(r'\d+', block)[0]) + with open(self.proj_dir + '/' + block) as f: block_circ = OPENQASM2Language().decode(f.read()) - else: - with open(join(self.proj_dir, block), "rb") as f: - block_circ = pickle.load(f) - # Get location - block_location = structure[block_num] - if block_circ.num_qudits != len(block_location): - raise ValueError( - f'{block} and `structure.pickle` locations are ' - 'different sizes.', - ) - # Append to circuit - try: - new_circuit.append_circuit(block_circ, block_location, as_circuit_gate=self.as_circuit_gate) - except Exception as e: - print(self.proj_dir) - raise e - - circuit.become(new_circuit) + # Get location + block_location = self.structure[block_num] + if block_circ.num_qudits != len(block_location): + raise ValueError( + f'{block} and `structure.pickle` locations are ' + 'different sizes.', + ) + # Append to circuit + circuit.append_circuit(block_circ, block_location) + # Check if the circuit has been partitioned, if so, try to replace + # blocks class CheckpointRestartPass(BasePass): ''' @@ -258,7 +236,6 @@ async def run(self, circuit: Circuit, data: PassData) -> None: checkpoint if possible. If the checkpoint does not exist, the default passes are run. """ - # block_id = data.get("block_num", "0") data["checkpoint_dir"] = self.checkpoint_dir if not exists(join(self.checkpoint_dir, "circuit.pickle")): _logger.info("Checkpoint does not exist!")