Skip to content

Commit

Permalink
Reverting some changes
Browse files Browse the repository at this point in the history
  • Loading branch information
jkalloor3 committed Oct 25, 2024
1 parent 7ca88b2 commit 880d8a2
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 125 deletions.
25 changes: 21 additions & 4 deletions bqskit/compiler/passdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,'
Expand Down Expand Up @@ -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)
Expand Down
36 changes: 5 additions & 31 deletions bqskit/passes/control/foreach.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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))
Expand All @@ -218,15 +212,14 @@ 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

# 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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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, (
Expand Down
157 changes: 67 additions & 90 deletions bqskit/passes/io/intermediate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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:
Expand All @@ -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]] = []
Expand All @@ -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)

Expand All @@ -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.
Expand All @@ -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:
"""
Expand All @@ -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):
'''
Expand Down Expand Up @@ -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!")
Expand Down

0 comments on commit 880d8a2

Please sign in to comment.