Spooky / quantum /utils /preprocess.py
JavideuS's picture
Deploy 7d1d5a0
a115eaf verified
Raw History Blame Contribute Delete
5.07 kB
"""
Pre-processing modes for the windowed QUBO pipeline.
Pre-processing is two independent stages, and the old boolean could only
express two of their four combinations:
1. structural BFS pruning -- which cells a robot may occupy per timestep
2. numerical reduction -- pinning individual variables to 0/1 from
their diagonal/individual coefficients
`preprocess=True` meant "aggressive BFS + numerical", `preprocess=False`
meant neither *and* dropped the whole windowed loop (no correction retries,
no forced-collision flagging) for a separate simpler implementation. So the
two settings differed in three ways at once and no measurement could
attribute a result to any one of them.
The modes below separate the stages. `raw` and `full` reproduce the old
False/True exactly, so existing configs and recorded sweeps keep working.
raw the old preprocess=False: the simple sampling loop, no
pruning, no pinning, no correction retries. A different
algorithm rather than "the same one with reductions off" --
do not read it as a clean no-preprocessing control.
bfs_aggressive windowed loop, non-backtracking BFS, no numerical pinning.
bfs_safe windowed loop, monotone BFS (staying in place and revisiting
both allowed), no numerical pinning. The only mode whose
pruning cannot exclude a feasible solution -- and therefore
the only one where a QUBO-vs-ILP comparison is like-for-like,
since ILP/CBS use exactly these semantics via
bfs_reachable_sets(). Costs roughly 10-19x the variables.
full the old preprocess=True: aggressive BFS + numerical.
full_safe monotone BFS + numerical. Measured to keep the numerical
pass fully active (the coefficient trigger fires on 60-100%
of timestep groups under either BFS), so this is a real
configuration and not a no-op variant of bfs_safe.
"""
# BFS variants a mode can select, consumed by BaseQUBO.reachable_for_window()
BFS_VARIANT_AGGRESSIVE = "aggressive"
BFS_VARIANT_SAFE = "safe"
RAW = "raw"
BFS_AGGRESSIVE = "bfs_aggressive"
BFS_SAFE = "bfs_safe"
FULL = "full"
FULL_SAFE = "full_safe"
# One row per mode, one column per stage. Adding a mode means adding a row
# here and nothing else; the predicates below are all lookups into it.
# windowed run the windowed loop (correction retries, forced-collision
# flagging) rather than the separate simple sampling loop
# bfs which reachability policy prunes the window, None for no pruning
# numeric run reduce_diag_fixed_vars_iterative()
# fmt: off (the column alignment is the point; black would collapse it)
_MODES = {
RAW: {"windowed": False, "bfs": None, "numeric": False},
BFS_AGGRESSIVE: {"windowed": True, "bfs": BFS_VARIANT_AGGRESSIVE, "numeric": False},
BFS_SAFE: {"windowed": True, "bfs": BFS_VARIANT_SAFE, "numeric": False},
FULL: {"windowed": True, "bfs": BFS_VARIANT_AGGRESSIVE, "numeric": True},
FULL_SAFE: {"windowed": True, "bfs": BFS_VARIANT_SAFE, "numeric": True},
}
# fmt: on
MODES = tuple(_MODES)
def normalize(value) -> str:
"""Coerce a mode from a config, a CLI flag, or a legacy boolean.
True/False are still accepted because index.json, manifest.json and every
sweep config written before the modes existed record booleans, and those
sweeps must stay re-aggregatable.
"""
if value is True:
return FULL
if value is False:
return RAW
if isinstance(value, str):
candidate = value.strip().lower()
if candidate in _MODES:
return candidate
raise ValueError(
f"Unknown preprocess mode {value!r}. Expected one of {list(MODES)} "
"(or a legacy True/False)."
)
raise TypeError(
f"preprocess must be one of {list(MODES)} or a bool, got {type(value).__name__}"
)
def uses_windowed_pipeline(mode) -> bool:
"""False only for `raw`, which runs the separate simple sampling loop."""
return _MODES[normalize(mode)]["windowed"]
def bfs_variant(mode):
"""Which reachability policy to prune with, or None under `raw`."""
return _MODES[normalize(mode)]["bfs"]
def applies_numeric_reduction(mode) -> bool:
return _MODES[normalize(mode)]["numeric"]
def applies_bfs_pruning(mode) -> bool:
"""Whether any BFS reachability pruning happens at all.
This is the form the exact solvers want. ILP and CBS take a plain bool
into builder.build(preprocess=...) and have no aggressive/safe choice --
bfs_reachable_sets() is already the monotone variant -- so every mode
except `raw` is simply "prune". Passing them a mode string directly would
be silently wrong: "raw" is a non-empty string and therefore truthy, so
they would prune in the one mode that must not.
"""
return bfs_variant(mode) is not None