qubosolver.transforms
qubosolver.transforms
Section titled “
qubosolver.transforms
”Transforms for QUBO instances.
Provides preprocessing transforms such as variable fixing to reduce problem size before solving.
Modules:
-
negative_bitflip–Bit-flip preprocessing transforms for QUBO instances with negative interactions.
-
variable_fixing–Variable-fixing transforms for QUBO problem reduction.
-
zeroing–Zeroing fallback for QUBO negative off-diagonal coefficients.
qubosolver.transforms.negative_bitflip
Section titled “
qubosolver.transforms.negative_bitflip
”Bit-flip preprocessing transforms for QUBO instances with negative interactions.
Quantum (Rydberg) solvers cannot encode attractive interactions, so a QUBO must
have non-negative off-diagonal coefficients to be embeddable. A change of
variable x_i -> 1 - y_i on a subset of variables flips the sign of the
interactions incident to it; choosing the subset that removes as much negative
weight as possible is an integer linear program solved here with GLPK (external).
apply solves the Integer Linear Program (ILP) and
applies the optimal bit flips to the matrix, returning a wrapper Instance that
records the flip vector so the solution can later be mapped back with
lift. When bit flips
cannot remove every negative off-diagonal coefficient, the remaining ones can
be dropped with transforms.zeroing.apply.
Typical usage:
from qubosolver.transforms import negative_bitflipfrom qubosolver.solving import brute_force
flipped_instance = negative_bitflip.apply(instance, time_limit_s=60.0)flipped_solution = brute_force.solve(flipped_instance)solution = negative_bitflip.lift(flipped_solution, flipped_instance)Classes:
-
Instance–A QUBO instance carrying bit-flip preprocessing history.
Functions:
-
apply–Solve the bit-flip ILP and apply the optimal flips to the QUBO matrix.
-
lift–Map a solution of the bit-flipped QUBO instance back onto the original variables.
Instance
Section titled “
Instance
”Instance(parent_instance: qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance)A QUBO instance carrying bit-flip preprocessing history.
Wraps a parent qubosolver.Instance whose off-diagonal coefficients may
contain negative interactions. Applying apply solves the bit-flip ILP,
stores the flip vector here, and exposes the transformed matrix so it can be
embedded and solved. lift uses the stored state to map a solution
back onto the original variables.
Parameters:
-
parent_instance(qubosolver.Instance) –The original QUBO instance (before bit flips). A deep copy is kept internally for later reconstruction.
Methods:
-
__init_subclass__–Register
clsunder its_tagsoloadcan dispatch to it. -
__len__–Number of binary variables in the QUBO problem (same as
size). -
cost–Compute the QUBO objective for a candidate solution .
-
load– -
save–Serialize this instance to
file_like, tagged with its type.
Attributes:
-
flips(Bitstring) –Flip vector applied to the parent matrix, all-zero until
applypopulates it. -
matrix(Matrix) –The QUBO symmetric matrix.
-
metrics(dict (external)[str (external), Any (external)]) –Negative off-diagonal count and weight before/after the flips, as computed
-
negative_bitflip(negative_bitflip.Instance) –View of this instance as a negative-bitflip instance.
-
offset(float (external)) –Constant term relating the flipped and original QUBO costs,
-
size(int (external)) –Number of binary variables in the QUBO problem.
-
status(str (external)) –Outcome of the bit-flip ILP solve (e.g.
"NONE","OPTIMAL", -
variable_fixing(variable_fixing.Instance) –View of this instance as a variable-fixing instance.
-
zeroing(zeroing.Instance) –View of this instance as a zeroing instance.
Source code in qubosolver/transforms/negative_bitflip.py
def __init__(self, parent_instance: qubosolver.Instance) -> None: """Initialize from a parent QUBO instance.
Args: parent_instance: The original QUBO instance (before bit flips). A deep copy is kept internally for later reconstruction. """ super().__init__(parent_instance.matrix.detach().clone()) self._parent_instance = copy.deepcopy(parent_instance)
self.flips: Bitstring = bitstring.zeros(parent_instance.size) """Flip vector applied to the parent matrix, all-zero until [`apply`][] populates it."""
self.metrics: dict[str, Any] = {} """Negative off-diagonal count and weight before/after the flips, as computed by [`apply`][]."""
self.status: str = "NONE" """Outcome of the bit-flip ILP solve (e.g. ``"NONE"``, ``"OPTIMAL"``, ``"REJECTED_WORSE_THAN_NOOP"``)."""
self.offset: float = 0.0 """Constant term relating the flipped and original QUBO costs, xTQx=yTQflippedy+offsetx^T Q x = y^T Q_{flipped} y + offsetxTQx=yTQflippedy+offset."""
flips
instance-attribute
Section titled “
flips
instance-attribute
”flips: qubosolver.Bitstring
module-attribute (qubosolver.types.Bitstring)" href="../bitstring/#qubosolver.Bitstring">Bitstring = qubosolver.bitstring (qubosolver.types.bitstring)" href="../bitstring/#qubosolver.bitstring">bitstring. zeros (qubosolver.types.bitstring.zeros)" href="../bitstring/#qubosolver.bitstring.zeros">zeros(parent_instance.size)Flip vector applied to the parent matrix, all-zero until apply populates it.
matrix
property
Section titled “
matrix
property
”matrix: qubosolver.Matrix
module-attribute (qubosolver.types.linalg.Matrix)" href="../matrix/#qubosolver.Matrix">MatrixThe QUBO symmetric matrix.
Returns:
-
Matrix–QUBO symmetric matrix of shape
(size, size).
Raises:
-
AssertionError (external)–If the internal tensor is not 2-D or not square.
metrics
instance-attribute
Section titled “
metrics
instance-attribute
”metrics: dict[str, Any] = {}Negative off-diagonal count and weight before/after the flips, as computed
by apply.
negative_bitflip
property
Section titled “
negative_bitflip
property
”negative_bitflip: negative_bitflip
property (qubosolver.types.instance.Instance.negative_bitflip)" href="#qubosolver.transforms.negative_bitflip.Instance.negative_bitflip">negative_bitflip.InstanceView of this instance as a negative-bitflip instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, negative_bitflip.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
negative_bitflip.Instance–This instance, narrowed to the negative-bitflip subclass.
Raises:
-
TypeError (external)–If this instance is not a
negative_bitflip.Instance.
offset
instance-attribute
Section titled “
offset
instance-attribute
”offset: float = 0.0Constant term relating the flipped and original QUBO costs, .
size
property
Section titled “
size
property
”size: intNumber of binary variables in the QUBO problem.
status
instance-attribute
Section titled “
status
instance-attribute
”status: str = 'NONE'Outcome of the bit-flip ILP solve (e.g. "NONE", "OPTIMAL",
"REJECTED_WORSE_THAN_NOOP").
variable_fixing
property
Section titled “
variable_fixing
property
”variable_fixing: variable_fixing
property (qubosolver.types.instance.Instance.variable_fixing)" href="#qubosolver.transforms.negative_bitflip.Instance.variable_fixing">variable_fixing.InstanceView of this instance as a variable-fixing instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, variable_fixing.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
variable_fixing.Instance–This instance, narrowed to the variable-fixing subclass.
Raises:
-
TypeError (external)–If this instance is not a
variable_fixing.Instance.
zeroing
property
Section titled “
zeroing
property
”zeroing: zeroing
property (qubosolver.types.instance.Instance.zeroing)" href="#qubosolver.transforms.negative_bitflip.Instance.zeroing">zeroing.InstanceView of this instance as a zeroing instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, zeroing.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
zeroing.Instance–This instance, narrowed to the zeroing subclass.
Raises:
-
TypeError (external)–If this instance is not a
zeroing.Instance.
__init_subclass__
Section titled “
__init_subclass__
”__init_subclass__(**kwargs: object) -> NoneRegister cls under its _tag so load can dispatch to it.
Automatic, so a new Instance subclass never needs to be added to a
separate registry by hand.
Source code in qubosolver/types/instance.py
def __init_subclass__(cls, **kwargs: object) -> None: """Register `cls` under its `_tag` so [`load`][] can dispatch to it.
Automatic, so a new `Instance` subclass never needs to be added to a separate registry by hand. """ super().__init_subclass__(**kwargs) Instance._registry[cls._tag()] = cls
__len__
Section titled “
__len__
”__len__() -> intNumber of binary variables in the QUBO problem (same as size).
Source code in qubosolver/types/instance.py
def __len__(self) -> int: """Number of binary variables in the QUBO problem (same as [`size`][]).""" return self.sizecost(solution: qubosolver.Bitstring
module-attribute (qubosolver.types.linalg.Bitstring)" href="../bitstring/#qubosolver.Bitstring">Bitstring) -> floatCompute the QUBO objective for a candidate solution .
Parameters:
-
solution(Bitstring) –Binary vector of shape
(size,).
Returns:
-
float (external)–Scalar cost value.
Source code in qubosolver/types/instance.py
def cost(self, solution: Bitstring) -> float: """Compute the QUBO objective xTQxx^T Q xxTQx for a candidate solution xxx.
Args: solution: Binary vector xxx of shape ``(size,)``.
Returns: Scalar cost value. """ # Import here to avoid circular imports from qubosolver.utils import _costs
cost = _costs.quadratic_cost(solution, self.matrix) assert type(cost) is float # nosec B101 return cost
load
classmethod
Section titled “
load
classmethod
”load(file_like: FileLike[bytes]) -> SelfDeserialize an Instance previously saved with save.
Called on the base class qubosolver.Instance, it loads any instance with
automatic dispatch.
Called on a subclass (e.g.
variable_fixing.Instance.load(f)),
it additionally requires the loaded Instance to be an instance of that subclass
(raising TypeError (external) otherwise).
Parameters:
-
file_like(FileLike[bytes (external)]) –Source file path or readable binary file object, as produced by
save.
Returns:
-
Self–A new instance of whichever concrete type wrote the tag.
Raises:
-
ValueError (external)–If the stream is not a qubosolver file, or if its type tag is missing or unrecognized.
-
TypeError (external)–If the loaded instance's type is not
clsor a subclass thereof.
Example
from pathlib import Pathfrom qubosolver import Instancefrom qubosolver.transforms import variable_fixing
file = Path("instance.bin")instance = variable_fixing.Instance(Instance())
with file.open("wb") as f: instance.save(f)
# Three ways to load it back, from least to most strict:with file.open("rb") as f: loaded = Instance.load(f) # accepts any Instance subtype # loads, then narrows (fails after loading) loaded = Instance.load(f).variable_fixing loaded = variable_fixing.Instance.load(f) # narrows first (fails before loading)Source code in qubosolver/types/instance.py
@classmethoddef load(cls, file_like: FileLike[bytes]) -> Self: """Deserialize an [`Instance`][] previously saved with [`save`][].
Called on the base class [`qubosolver.Instance`][], it loads any instance with automatic dispatch.
Called on a subclass (e.g. [`variable_fixing.Instance.load(f)`][qubosolver.transforms.variable_fixing.Instance.load]), it additionally requires the loaded `Instance` to be an instance of that subclass (raising [`TypeError`][] otherwise).
Args: file_like: Source file path or readable binary file object, as produced by [`save`][].
Returns: A new instance of whichever concrete type wrote the tag.
Raises: ValueError: If the stream is not a qubosolver file, or if its type tag is missing or unrecognized. TypeError: If the loaded instance's type is not `cls` or a subclass thereof.
Example: ```python from pathlib import Path from qubosolver import Instance from qubosolver.transforms import variable_fixing
file = Path("instance.bin") instance = variable_fixing.Instance(Instance())
with file.open("wb") as f: instance.save(f)
# Three ways to load it back, from least to most strict: with file.open("rb") as f: loaded = Instance.load(f) # accepts any Instance subtype # loads, then narrows (fails after loading) loaded = Instance.load(f).variable_fixing loaded = variable_fixing.Instance.load(f) # narrows first (fails before loading) ``` """ with io_utils.open(file_like, "rb") as f: io_utils.load_header(f) tag = io_utils.load_string(f) target_cls = Instance._registry.get(tag) if target_cls is None: raise ValueError(f"Cannot load Instance: unrecognized type tag {tag!r}.") instance = target_cls._read_body(f) if not isinstance(instance, cls): raise TypeError( f"Cannot load {cls.__module__}.{cls.__qualname__}: " f"stream contains a {type(instance).__module__}.{type(instance).__qualname__}." ) return instancesave(file_like: FileLike[bytes]) -> NoneSerialize this instance to file_like, tagged with its type.
Parameters:
-
file_like(FileLike[bytes (external)]) –Destination — a file path (
str(external) oros.PathLike(external)), or a binary-writabletyping.IO(external) stream.
Example
from pathlib import Path
with Path("instance.bin").open("wb") as f: instance.save(f)Source code in qubosolver/types/instance.py
def save(self, file_like: FileLike[bytes]) -> None: """Serialize this instance to ``file_like``, tagged with its type.
Args: file_like: Destination — a file path ([`str`][] or [`os.PathLike`][]), or a binary-writable [`typing.IO`][] stream.
Example: ```python from pathlib import Path
with Path("instance.bin").open("wb") as f: instance.save(f) ``` """ with io_utils.open(file_like, "wb") as f: io_utils.save_header(f) io_utils.save_string(f, self._tag()) self._write_body(f)
apply
Section titled “
apply
”apply(instance: qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance, *, time_limit_s: float = 60.0, eps: float = 0.0) -> Instance (qubosolver.transforms.negative_bitflip.Instance)" href="#qubosolver.transforms.negative_bitflip.Instance">InstanceSolve the bit-flip ILP and apply the optimal flips to the QUBO matrix.
Wraps instance in a bit-flip Instance, solves the negative-weight
Integer Linear Program (ILP)
with GLPK (external), and replaces the matrix with its
flipped counterpart. When
instance has no negative off-diagonal coefficient, the wrapper is returned
unchanged (status stays "NONE" and flips stays all-zero). If
the solved flips would leave more negative weight than doing nothing
(a known GLPK edge case), they are rejected and replaced with a no-op
(status becomes "REJECTED_WORSE_THAN_NOOP").
Parameters:
-
instance(qubosolver.Instance) –The QUBO instance to preprocess.
-
time_limit_s(float (external), default:60.0) –GLPKsolver time limit in seconds. -
eps(float (external), default:0.0) –Tolerance below which a coefficient is treated as zero.
Returns:
-
Instance–A bit-flip instance carrying the flip vector and metrics.
Source code in qubosolver/transforms/negative_bitflip.py
def apply( instance: qubosolver.Instance, *, time_limit_s: float = 60.0, eps: float = 0.0,) -> Instance: """Solve the bit-flip ILP and apply the optimal flips to the QUBO matrix.
Wraps `instance` in a bit-flip [`Instance`][], solves the negative-weight **Integer Linear Program (ILP)** with [`GLPK`](https://github.com/bradfordboyle/pyglpk), and replaces the matrix with its flipped counterpart. When `instance` has no negative off-diagonal coefficient, the wrapper is returned unchanged (``status`` stays ``"NONE"`` and ``flips`` stays all-zero). If the solved flips would leave *more* negative weight than doing nothing (a known `GLPK` edge case), they are rejected and replaced with a no-op (``status`` becomes ``"REJECTED_WORSE_THAN_NOOP"``).
Args: instance: The QUBO instance to preprocess. time_limit_s: `GLPK` solver time limit in seconds. eps: Tolerance below which a coefficient is treated as zero.
Returns: A bit-flip instance carrying the flip vector and metrics. """ flipped_instance = Instance(instance)
Q = instance.matrix if instance.size == 0 or not _has_negative_offdiagonal(Q, eps=eps): return flipped_instance
flips, objective_value, status = _solve_bitflip_preprocessing_glpk( Q, time_limit_s=time_limit_s, eps=eps, ) metrics = _compute_negative_weight_metrics(Q, flips, eps)
if metrics["neg_weight_reduction_pct"] < 0.0: reduction_pct = metrics["neg_weight_reduction_pct"] logger.warning( f"Bit-flip preprocessing (status={status}) increased the remaining negative " f"off-diagonal weight instead of reducing it ({reduction_pct:.2f}% change); " f"falling back to no-op flips." ) flips.fill_(0) objective_value = float("nan") status = "REJECTED_WORSE_THAN_NOOP" metrics = _compute_negative_weight_metrics(Q, flips, eps)
Q_flipped, offset = _transform_qubo_with_bitflips(Q, flips)
flipped_instance._matrix = Q_flipped flipped_instance.flips = flips flipped_instance.metrics = metrics flipped_instance.metrics["objective_value"] = objective_value flipped_instance.status = status flipped_instance.offset = offset
return flipped_instancelift(flipped_solution: Solution
dataclass (qubosolver.types.Solution)" href="../qubosolver/#qubosolver.Solution">Solution, flipped_instance: Instance (qubosolver.transforms.negative_bitflip.Instance)" href="#qubosolver.transforms.negative_bitflip.Instance">Instance) -> Solution
dataclass (qubosolver.types.Solution)" href="../qubosolver/#qubosolver.Solution">SolutionMap a solution of the bit-flipped QUBO instance back onto the original variables.
Undoes the flips recorded on flipped_instance (y_i -> x_i) and recomputes
costs against the original (unflipped) matrix. When no bit flip was applied,
returns a deep copy of flipped_solution unchanged.
Parameters:
-
flipped_solution(Solution) –Solution obtained on the bit-flipped instance.
-
flipped_instance(Instance) –The bit-flipped instance produced by
apply.
Returns:
-
Solution–A new solution over the original variables.
Source code in qubosolver/transforms/negative_bitflip.py
def lift(flipped_solution: Solution, flipped_instance: Instance) -> Solution: """Map a solution of the bit-flipped QUBO instance back onto the original variables.
Undoes the flips recorded on `flipped_instance` (``y_i -> x_i``) and recomputes costs against the original (unflipped) matrix. When no bit flip was applied, returns a deep copy of `flipped_solution` unchanged.
Args: flipped_solution: Solution obtained on the bit-flipped instance. flipped_instance: The bit-flipped instance produced by [`apply`][].
Returns: A new solution over the original variables. """ flipped = torch.any(flipped_instance.flips != 0) if not flipped: return copy.deepcopy(flipped_solution)
solution = Solution() solution.bitstrings = _apply_bitflips(flipped_solution.bitstrings, flipped_instance.flips) solution.costs = vector.tensor( [flipped_instance._parent_instance.cost(b) for b in solution.bitstrings] ) solution.counts = flipped_solution.counts solution.probabilities = flipped_solution.probabilities
return solution
qubosolver.transforms.variable_fixing
Section titled “
qubosolver.transforms.variable_fixing
”Variable-fixing transforms for QUBO problem reduction.
Variable fixing eliminates variables from a QUBO instance before solving by proving, from the structure of the objective matrix alone, that certain variables must be 0 or 1 in any optimal solution. Reducing the problem size this way can significantly cut the resources required by the solver.
Typical usage:
from qubosolver.transforms import variable_fixingfrom qubosolver.solving import brute_force
reduced_instance = variable_fixing.apply_recursively(instance)reduced_solution = brute_force.solve(reduced_instance)solution = variable_fixing.lift(reduced_solution, reduced_instance)Classes:
-
Instance–A QUBO instance with variable-fixing history.
Functions:
-
apply–Apply each fixation rule once and reduce the QUBO matrix accordingly.
-
apply_recursively–Apply fixation rules repeatedly until no further variables can be fixed.
-
hansen_fixing–Identify variables that can be fixed using Hansen's bounding criterion.
-
lift–Reconstruct the full solution by reinserting fixed variables.
Attributes:
-
Rule(TypeAlias (external)) –A function that inspects a QUBO instance and returns the variables it can fix,
Rule
module-attribute
Section titled “
Rule
module-attribute
”Rule: TypeAlias = Callable[[ qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance], dict[int, int]]A function that inspects a QUBO instance and returns the variables it can fix, as a mapping from variable index to the value (0 or 1) it is fixed to.
Instance
Section titled “
Instance
”Instance(parent_instance: qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance)A QUBO instance with variable-fixing history.
Wraps a parent qubosolver.Instance and
tracks which variables were fixed (and to which value) so the original
solution can be reconstructed via lift.
Parameters:
-
parent_instance(qubosolver.Instance) –The original (unreduced) QUBO instance. A deep copy is kept internally for later reconstruction.
Methods:
-
__init_subclass__–Register
clsunder its_tagsoloadcan dispatch to it. -
__len__–Number of binary variables in the QUBO problem (same as
size). -
cost–Compute the QUBO objective for a candidate solution .
-
load– -
save–Serialize this instance to
file_like, tagged with its type.
Attributes:
-
fixed_indices(list (external)[dict (external)[int (external), int (external)]]) –Fixation history: one dict per
applycall, mapping index → fixed value. -
matrix(Matrix) –The QUBO symmetric matrix.
-
n_fixed_indices(int (external)) –Total number of variables fixed across all fixation rounds.
-
negative_bitflip(negative_bitflip.Instance) –View of this instance as a negative-bitflip instance.
-
size(int (external)) –Number of binary variables in the QUBO problem.
-
variable_fixing(variable_fixing.Instance) –View of this instance as a variable-fixing instance.
-
zeroing(zeroing.Instance) –View of this instance as a zeroing instance.
Source code in qubosolver/transforms/variable_fixing.py
def __init__(self, parent_instance: qubosolver.Instance) -> None: """Initialize from a parent QUBO instance.
Args: parent_instance: The original (unreduced) QUBO instance. A deep copy is kept internally for later reconstruction. """ super().__init__( parent_instance.matrix.detach().clone(), ) self._parent_instance = copy.deepcopy(parent_instance) self._fixed_indices: list[dict[int, int]] = []
fixed_indices
property
Section titled “
fixed_indices
property
”fixed_indices: list[dict[int, int]]Fixation history: one dict per apply call, mapping index → fixed value.
matrix
property
Section titled “
matrix
property
”matrix: qubosolver.Matrix
module-attribute (qubosolver.types.linalg.Matrix)" href="../matrix/#qubosolver.Matrix">MatrixThe QUBO symmetric matrix.
Returns:
-
Matrix–QUBO symmetric matrix of shape
(size, size).
Raises:
-
AssertionError (external)–If the internal tensor is not 2-D or not square.
n_fixed_indices
property
Section titled “
n_fixed_indices
property
”n_fixed_indices: intTotal number of variables fixed across all fixation rounds.
negative_bitflip
property
Section titled “
negative_bitflip
property
”negative_bitflip: negative_bitflip
property (qubosolver.types.instance.Instance.negative_bitflip)" href="#qubosolver.transforms.negative_bitflip.Instance.negative_bitflip">negative_bitflip.InstanceView of this instance as a negative-bitflip instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, negative_bitflip.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
negative_bitflip.Instance–This instance, narrowed to the negative-bitflip subclass.
Raises:
-
TypeError (external)–If this instance is not a
negative_bitflip.Instance.
size
property
Section titled “
size
property
”size: intNumber of binary variables in the QUBO problem.
variable_fixing
property
Section titled “
variable_fixing
property
”variable_fixing: variable_fixing
property (qubosolver.types.instance.Instance.variable_fixing)" href="#qubosolver.transforms.negative_bitflip.Instance.variable_fixing">variable_fixing.InstanceView of this instance as a variable-fixing instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, variable_fixing.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
variable_fixing.Instance–This instance, narrowed to the variable-fixing subclass.
Raises:
-
TypeError (external)–If this instance is not a
variable_fixing.Instance.
zeroing
property
Section titled “
zeroing
property
”zeroing: zeroing
property (qubosolver.types.instance.Instance.zeroing)" href="#qubosolver.transforms.negative_bitflip.Instance.zeroing">zeroing.InstanceView of this instance as a zeroing instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, zeroing.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
zeroing.Instance–This instance, narrowed to the zeroing subclass.
Raises:
-
TypeError (external)–If this instance is not a
zeroing.Instance.
__init_subclass__
Section titled “
__init_subclass__
”__init_subclass__(**kwargs: object) -> NoneRegister cls under its _tag so load can dispatch to it.
Automatic, so a new Instance subclass never needs to be added to a
separate registry by hand.
Source code in qubosolver/types/instance.py
def __init_subclass__(cls, **kwargs: object) -> None: """Register `cls` under its `_tag` so [`load`][] can dispatch to it.
Automatic, so a new `Instance` subclass never needs to be added to a separate registry by hand. """ super().__init_subclass__(**kwargs) Instance._registry[cls._tag()] = cls
__len__
Section titled “
__len__
”__len__() -> intNumber of binary variables in the QUBO problem (same as size).
Source code in qubosolver/types/instance.py
def __len__(self) -> int: """Number of binary variables in the QUBO problem (same as [`size`][]).""" return self.sizecost(solution: qubosolver.Bitstring
module-attribute (qubosolver.types.linalg.Bitstring)" href="../bitstring/#qubosolver.Bitstring">Bitstring) -> floatCompute the QUBO objective for a candidate solution .
Parameters:
-
solution(Bitstring) –Binary vector of shape
(size,).
Returns:
-
float (external)–Scalar cost value.
Source code in qubosolver/types/instance.py
def cost(self, solution: Bitstring) -> float: """Compute the QUBO objective xTQxx^T Q xxTQx for a candidate solution xxx.
Args: solution: Binary vector xxx of shape ``(size,)``.
Returns: Scalar cost value. """ # Import here to avoid circular imports from qubosolver.utils import _costs
cost = _costs.quadratic_cost(solution, self.matrix) assert type(cost) is float # nosec B101 return cost
load
classmethod
Section titled “
load
classmethod
”load(file_like: FileLike[bytes]) -> SelfDeserialize an Instance previously saved with save.
Called on the base class qubosolver.Instance, it loads any instance with
automatic dispatch.
Called on a subclass (e.g.
variable_fixing.Instance.load(f)),
it additionally requires the loaded Instance to be an instance of that subclass
(raising TypeError (external) otherwise).
Parameters:
-
file_like(FileLike[bytes (external)]) –Source file path or readable binary file object, as produced by
save.
Returns:
-
Self–A new instance of whichever concrete type wrote the tag.
Raises:
-
ValueError (external)–If the stream is not a qubosolver file, or if its type tag is missing or unrecognized.
-
TypeError (external)–If the loaded instance's type is not
clsor a subclass thereof.
Example
from pathlib import Pathfrom qubosolver import Instancefrom qubosolver.transforms import variable_fixing
file = Path("instance.bin")instance = variable_fixing.Instance(Instance())
with file.open("wb") as f: instance.save(f)
# Three ways to load it back, from least to most strict:with file.open("rb") as f: loaded = Instance.load(f) # accepts any Instance subtype # loads, then narrows (fails after loading) loaded = Instance.load(f).variable_fixing loaded = variable_fixing.Instance.load(f) # narrows first (fails before loading)Source code in qubosolver/types/instance.py
@classmethoddef load(cls, file_like: FileLike[bytes]) -> Self: """Deserialize an [`Instance`][] previously saved with [`save`][].
Called on the base class [`qubosolver.Instance`][], it loads any instance with automatic dispatch.
Called on a subclass (e.g. [`variable_fixing.Instance.load(f)`][qubosolver.transforms.variable_fixing.Instance.load]), it additionally requires the loaded `Instance` to be an instance of that subclass (raising [`TypeError`][] otherwise).
Args: file_like: Source file path or readable binary file object, as produced by [`save`][].
Returns: A new instance of whichever concrete type wrote the tag.
Raises: ValueError: If the stream is not a qubosolver file, or if its type tag is missing or unrecognized. TypeError: If the loaded instance's type is not `cls` or a subclass thereof.
Example: ```python from pathlib import Path from qubosolver import Instance from qubosolver.transforms import variable_fixing
file = Path("instance.bin") instance = variable_fixing.Instance(Instance())
with file.open("wb") as f: instance.save(f)
# Three ways to load it back, from least to most strict: with file.open("rb") as f: loaded = Instance.load(f) # accepts any Instance subtype # loads, then narrows (fails after loading) loaded = Instance.load(f).variable_fixing loaded = variable_fixing.Instance.load(f) # narrows first (fails before loading) ``` """ with io_utils.open(file_like, "rb") as f: io_utils.load_header(f) tag = io_utils.load_string(f) target_cls = Instance._registry.get(tag) if target_cls is None: raise ValueError(f"Cannot load Instance: unrecognized type tag {tag!r}.") instance = target_cls._read_body(f) if not isinstance(instance, cls): raise TypeError( f"Cannot load {cls.__module__}.{cls.__qualname__}: " f"stream contains a {type(instance).__module__}.{type(instance).__qualname__}." ) return instancesave(file_like: FileLike[bytes]) -> NoneSerialize this instance to file_like, tagged with its type.
Parameters:
-
file_like(FileLike[bytes (external)]) –Destination — a file path (
str(external) oros.PathLike(external)), or a binary-writabletyping.IO(external) stream.
Example
from pathlib import Path
with Path("instance.bin").open("wb") as f: instance.save(f)Source code in qubosolver/types/instance.py
def save(self, file_like: FileLike[bytes]) -> None: """Serialize this instance to ``file_like``, tagged with its type.
Args: file_like: Destination — a file path ([`str`][] or [`os.PathLike`][]), or a binary-writable [`typing.IO`][] stream.
Example: ```python from pathlib import Path
with Path("instance.bin").open("wb") as f: instance.save(f) ``` """ with io_utils.open(file_like, "wb") as f: io_utils.save_header(f) io_utils.save_string(f, self._tag()) self._write_body(f)
apply
Section titled “
apply
”apply(instance: qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance, fixation_rules: Sequence[ Rule
module-attribute (qubosolver.transforms.variable_fixing.Rule)" href="#qubosolver.transforms.variable_fixing.Rule">Rule] = ( hansen_fixing (qubosolver.transforms.variable_fixing.hansen_fixing)" href="#qubosolver.transforms.variable_fixing.hansen_fixing">hansen_fixing,), *, inplace: bool = False) -> Instance (qubosolver.transforms.variable_fixing.Instance)" href="#qubosolver.transforms.variable_fixing.Instance">InstanceApply each fixation rule once and reduce the QUBO matrix accordingly.
Each rule in fixation_rules is called in order; variables it identifies
are immediately fixed and the matrix is reduced before the next rule runs.
Parameters:
-
instance(qubosolver.Instance) –The QUBO instance to reduce.
-
fixation_rules(Sequence (external)[Rule], default:(hansen_fixing,)) –Ordered sequence of
Rulecallables. -
inplace(bool (external), default:False) –If
False(default), wrapsinstancein a new variable-fixingInstancebefore modifying it.
Returns:
-
Instance–The reduced instance with updated fixation history.
Source code in qubosolver/transforms/variable_fixing.py
def apply( instance: qubosolver.Instance, fixation_rules: Sequence[Rule] = (hansen_fixing,), *, inplace: bool = False,) -> Instance: """Apply each fixation rule once and reduce the QUBO matrix accordingly.
Each rule in `fixation_rules` is called in order; variables it identifies are immediately fixed and the matrix is reduced before the next rule runs.
Args: instance: The QUBO instance to reduce. fixation_rules: Ordered sequence of [`Rule`][] callables. inplace: If ``False`` (default), wraps `instance` in a new variable-fixing [`Instance`][] before modifying it.
Returns: The reduced instance with updated fixation history. """ if not inplace: instance = Instance(instance)
instance = instance.variable_fixing
for rule in fixation_rules: fixed = rule(instance) _reduce_qubo(instance, fixed, inplace=True)
if instance.size == 0: logger.info("Variable fixing reduced the instance to zero variables.")
return instance
apply_recursively
Section titled “
apply_recursively
”apply_recursively(instance: qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance, fixation_rules: Sequence[ Rule
module-attribute (qubosolver.transforms.variable_fixing.Rule)" href="#qubosolver.transforms.variable_fixing.Rule">Rule] = ( hansen_fixing (qubosolver.transforms.variable_fixing.hansen_fixing)" href="#qubosolver.transforms.variable_fixing.hansen_fixing">hansen_fixing,), *, inplace: bool = False) -> Instance (qubosolver.transforms.variable_fixing.Instance)" href="#qubosolver.transforms.variable_fixing.Instance">InstanceApply fixation rules repeatedly until no further variables can be fixed.
Calls apply in a loop; stops when a full pass over all rules
fixes no additional variables.
Parameters:
-
instance(qubosolver.Instance) –The QUBO instance to reduce.
-
fixation_rules(Sequence (external)[Rule], default:(hansen_fixing,)) –Ordered sequence of [
Rule] callables. -
inplace(bool (external), default:False) –If
False(default), wrapsinstancein a new variable-fixingInstancebefore modifying it.
Returns:
-
Instance–The fully reduced instance.
Source code in qubosolver/transforms/variable_fixing.py
def apply_recursively( instance: qubosolver.Instance, fixation_rules: Sequence[Rule] = (hansen_fixing,), *, inplace: bool = False,) -> Instance: """Apply fixation rules repeatedly until no further variables can be fixed.
Calls [`apply`][] in a loop; stops when a full pass over all rules fixes no additional variables.
Args: instance: The QUBO instance to reduce. fixation_rules: Ordered sequence of [`Rule`] callables. inplace: If ``False`` (default), wraps `instance` in a new variable-fixing [`Instance`][] before modifying it.
Returns: The fully reduced instance. """ if not inplace: instance = Instance(instance)
instance = instance.variable_fixing
while True: prev_n_fixations = len(instance._fixed_indices) apply(instance, fixation_rules, inplace=True) n_fixations = len(instance._fixed_indices) assert n_fixations >= prev_n_fixations # nosec B101 if n_fixations == prev_n_fixations: return instance
hansen_fixing
Section titled “
hansen_fixing
”hansen_fixing(instance: qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance) -> dict[int, int]Identify variables that can be fixed using Hansen's bounding criterion.
For each variable i, computes a lower bound
c_i + 2 * sum(min(0, Q_ij)) and an upper bound
c_i + 2 * sum(max(0, Q_ij)) from the diagonal and off-diagonal
elements of the QUBO matrix. A variable is fixed to 0 when its lower
bound is non-negative (it cannot improve the objective by being 1) and
to 1 when its upper bound is non-positive (it can only improve it).
Parameters:
-
instance(qubosolver.Instance) –The QUBO instance to analyze.
Returns:
-
dict (external)[int (external), int (external)]–Mapping of variable index to fixed value (
0or1). Variables that cannot be fixed are omitted.
Source code in qubosolver/transforms/variable_fixing.py
def hansen_fixing(instance: qubosolver.Instance) -> dict[int, int]: """Identify variables that can be fixed using Hansen's bounding criterion.
For each variable *i*, computes a lower bound ``c_i + 2 * sum(min(0, Q_ij))`` and an upper bound ``c_i + 2 * sum(max(0, Q_ij))`` from the diagonal and off-diagonal elements of the QUBO matrix. A variable is fixed to 0 when its lower bound is non-negative (it cannot improve the objective by being 1) and to 1 when its upper bound is non-positive (it can only improve it).
Args: instance: The QUBO instance to analyze.
Returns: Mapping of variable index to fixed value (``0`` or ``1``). Variables that cannot be fixed are omitted. """ fixed_dict: dict[int, int] = {} size: int = cast(int, instance.size) epsilon: float = 1e-8 # Tolerance to avoid floating-point precision issues
for i in range(size): ci = instance.matrix[i, i].item() # Diagonal element
q_minus = sum(min(0, instance.matrix[i, j].item()) for j in range(size) if j != i) q_plus = sum(max(0, instance.matrix[i, j].item()) for j in range(size) if j != i)
if ci + q_minus * 2 >= -epsilon: fixed_dict[i] = 0 elif ci + q_plus * 2 <= epsilon: fixed_dict[i] = 1
return fixed_dictlift(reduced_solution: Solution
dataclass (qubosolver.types.Solution)" href="../qubosolver/#qubosolver.Solution">Solution, reduced_instance: Instance (qubosolver.transforms.variable_fixing.Instance)" href="#qubosolver.transforms.variable_fixing.Instance">Instance) -> Solution
dataclass (qubosolver.types.Solution)" href="../qubosolver/#qubosolver.Solution">SolutionReconstruct the full solution by reinserting fixed variables.
Reverses the fixation history stored in reduced_instance: fixed variables
are reinserted at their original positions in each bitstring, and costs
are recomputed against the original (unreduced) QUBO matrix.
If no variables were fixed, returns a deep copy of reduced_solution
unchanged.
Parameters:
-
reduced_solution(Solution) –Solution obtained from solving the reduced QUBO.
-
reduced_instance(Instance) –The reduced instance carrying the fixation history and a reference to the original instance.
Returns:
-
Solution–A new solution with full-length bitstrings and costs evaluated against the original QUBO matrix. Counts and probabilities are carried over from
reduced_solution.
Source code in qubosolver/transforms/variable_fixing.py
def lift(reduced_solution: Solution, reduced_instance: Instance) -> Solution: """Reconstruct the full solution by reinserting fixed variables.
Reverses the fixation history stored in `reduced_instance`: fixed variables are reinserted at their original positions in each bitstring, and costs are recomputed against the original (unreduced) QUBO matrix.
If no variables were fixed, returns a deep copy of `reduced_solution` unchanged.
Args: reduced_solution: Solution obtained from solving the reduced QUBO. reduced_instance: The reduced instance carrying the fixation history and a reference to the original instance.
Returns: A new solution with full-length bitstrings and costs evaluated against the original QUBO matrix. Counts and probabilities are carried over from `reduced_solution`. """ if not reduced_solution: return Solution()
bitstrings_list = reduced_solution.bitstrings.tolist()
def reinsert_fixed_variables(bitstring: list[int]) -> list[int]: for fixation_dict in reversed(reduced_instance._fixed_indices): for position, bit_value in sorted(fixation_dict.items()): bitstring.insert(position, bit_value) return bitstring
bits_to_reinsert = sum(len(fixation_dict) for fixation_dict in reduced_instance._fixed_indices) assert (bits_to_reinsert + len(bitstrings_list[0])) == reduced_instance._parent_instance.size # nosec B101
if bits_to_reinsert == 0: return copy.deepcopy(reduced_solution)
solution = Solution()
solution.bitstrings = bitstrings.tensor( [reinsert_fixed_variables(bitstring) for bitstring in bitstrings_list] ) solution.costs = vector.tensor( [reduced_instance._parent_instance.cost(b) for b in solution.bitstrings] ) solution.counts = reduced_solution.counts solution.probabilities = reduced_solution.probabilities
return solution
qubosolver.transforms.zeroing
Section titled “
qubosolver.transforms.zeroing
”Zeroing fallback for QUBO negative off-diagonal coefficients.
Bit-flip preprocessing (see
transforms.negative_bitflip) removes as much negative
off-diagonal weight as possible, but some negative coefficients may remain when
the problem is not fully bipartisable. Quantum (Rydberg) solvers cannot embed
such coefficients, so this module offers a last-resort approximation:
apply sets every remaining negative
off-diagonal coefficient to zero and records which positions were zeroed in a
zeroing Instance.
from qubosolver.transforms import negative_bitflip, zeroing
reduced_instance = negative_bitflip.apply(instance, time_limit_s=60.0)# drop any negative coefficient bit flips could not removezeroed_instance = zeroing.apply(reduced_instance)print(zeroed_instance.zeroed_edges) # (N, 2) tensor of zeroed (i, j) index pairsClasses:
Functions:
-
apply–Set remaining negative off-diagonal coefficients to zero.
-
lift–Map a solution of the zeroed QUBO back onto the pre-zeroing problem.
Instance
Section titled “
Instance
”Instance(parent_instance: qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance)A QUBO Instance recording zeroing history.
Records which off-diagonal coefficients were set to zero by
apply by keeping the matrix of the
removed negative coefficients (negative_matrix) rather than a single
flag. Because the QUBO matrix is symmetric, each zeroed interaction appears
twice in negative_matrix but once in
zeroed_edges.
Parameters:
-
parent_instance(qubosolver.Instance) –The QUBO instance to extend with zeroing state.
Methods:
-
__init_subclass__–Register
clsunder its_tagsoloadcan dispatch to it. -
__len__–Number of binary variables in the QUBO problem (same as
size). -
cost–Compute the QUBO objective for a candidate solution .
-
load– -
save–Serialize this instance to
file_like, tagged with its type.
Attributes:
-
matrix(Matrix) –The QUBO symmetric matrix.
-
negative_bitflip(negative_bitflip.Instance) –View of this instance as a negative-bitflip instance.
-
negative_matrix(Matrix) –Matrix of removed negative coefficients: same (symmetric) shape as the
-
size(int (external)) –Number of binary variables in the QUBO problem.
-
variable_fixing(variable_fixing.Instance) –View of this instance as a variable-fixing instance.
-
zeroed_edges(torch (external).Tensor (external)) –The zeroed interactions as an
(N, 2)tensor of(i, j)index pairs. -
zeroing(zeroing.Instance) –View of this instance as a zeroing instance.
Source code in qubosolver/transforms/zeroing.py
def __init__(self, parent_instance: qubosolver.Instance) -> None: """Initialize from a QUBO instance, before any zeroing.
Args: parent_instance: The QUBO instance to extend with zeroing state. """ super().__init__(parent_instance.matrix.detach().clone()) self._parent_instance = copy.deepcopy(parent_instance) self.negative_matrix: Matrix = torch.zeros_like(self._matrix) """Matrix of removed negative coefficients: same (symmetric) shape as the QUBO matrix, holding the original values at zeroed positions and 0 elsewhere."""
matrix
property
Section titled “
matrix
property
”matrix: qubosolver.Matrix
module-attribute (qubosolver.types.linalg.Matrix)" href="../matrix/#qubosolver.Matrix">MatrixThe QUBO symmetric matrix.
Returns:
-
Matrix–QUBO symmetric matrix of shape
(size, size).
Raises:
-
AssertionError (external)–If the internal tensor is not 2-D or not square.
negative_bitflip
property
Section titled “
negative_bitflip
property
”negative_bitflip: negative_bitflip
property (qubosolver.types.instance.Instance.negative_bitflip)" href="#qubosolver.transforms.negative_bitflip.Instance.negative_bitflip">negative_bitflip.InstanceView of this instance as a negative-bitflip instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, negative_bitflip.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
negative_bitflip.Instance–This instance, narrowed to the negative-bitflip subclass.
Raises:
-
TypeError (external)–If this instance is not a
negative_bitflip.Instance.
negative_matrix
instance-attribute
Section titled “
negative_matrix
instance-attribute
”negative_matrix: qubosolver.Matrix
module-attribute (qubosolver.types.Matrix)" href="../matrix/#qubosolver.Matrix">Matrix = torch.zeros_like(self._matrix)Matrix of removed negative coefficients: same (symmetric) shape as the QUBO matrix, holding the original values at zeroed positions and 0 elsewhere.
size
property
Section titled “
size
property
”size: intNumber of binary variables in the QUBO problem.
variable_fixing
property
Section titled “
variable_fixing
property
”variable_fixing: variable_fixing
property (qubosolver.types.instance.Instance.variable_fixing)" href="#qubosolver.transforms.negative_bitflip.Instance.variable_fixing">variable_fixing.InstanceView of this instance as a variable-fixing instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, variable_fixing.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
variable_fixing.Instance–This instance, narrowed to the variable-fixing subclass.
Raises:
-
TypeError (external)–If this instance is not a
variable_fixing.Instance.
zeroed_edges
property
Section titled “
zeroed_edges
property
”zeroed_edges: torch.TensorThe zeroed interactions as an (N, 2) tensor of (i, j) index pairs.
Each symmetric pair is reported once (i < j); N is the number of
zeroed off-diagonal interactions.
zeroing
property
Section titled “
zeroing
property
”zeroing: zeroing
property (qubosolver.types.instance.Instance.zeroing)" href="#qubosolver.transforms.negative_bitflip.Instance.zeroing">zeroing.InstanceView of this instance as a zeroing instance.
Convenience property to avoid the boilerplate of
assert isinstance(instance, zeroing.Instance) before calling
a method specific to that subclass. It exists purely to satisfy static
type checkers (mypy) and enable IDE code completion — the runtime
isinstance (external) check and the TypeError (external) below just mirror the guarantee that
the assert (external)
would otherwise provide.
Returns:
-
zeroing.Instance–This instance, narrowed to the zeroing subclass.
Raises:
-
TypeError (external)–If this instance is not a
zeroing.Instance.
__init_subclass__
Section titled “
__init_subclass__
”__init_subclass__(**kwargs: object) -> NoneRegister cls under its _tag so load can dispatch to it.
Automatic, so a new Instance subclass never needs to be added to a
separate registry by hand.
Source code in qubosolver/types/instance.py
def __init_subclass__(cls, **kwargs: object) -> None: """Register `cls` under its `_tag` so [`load`][] can dispatch to it.
Automatic, so a new `Instance` subclass never needs to be added to a separate registry by hand. """ super().__init_subclass__(**kwargs) Instance._registry[cls._tag()] = cls
__len__
Section titled “
__len__
”__len__() -> intNumber of binary variables in the QUBO problem (same as size).
Source code in qubosolver/types/instance.py
def __len__(self) -> int: """Number of binary variables in the QUBO problem (same as [`size`][]).""" return self.sizecost(solution: qubosolver.Bitstring
module-attribute (qubosolver.types.linalg.Bitstring)" href="../bitstring/#qubosolver.Bitstring">Bitstring) -> floatCompute the QUBO objective for a candidate solution .
Parameters:
-
solution(Bitstring) –Binary vector of shape
(size,).
Returns:
-
float (external)–Scalar cost value.
Source code in qubosolver/types/instance.py
def cost(self, solution: Bitstring) -> float: """Compute the QUBO objective xTQxx^T Q xxTQx for a candidate solution xxx.
Args: solution: Binary vector xxx of shape ``(size,)``.
Returns: Scalar cost value. """ # Import here to avoid circular imports from qubosolver.utils import _costs
cost = _costs.quadratic_cost(solution, self.matrix) assert type(cost) is float # nosec B101 return cost
load
classmethod
Section titled “
load
classmethod
”load(file_like: FileLike[bytes]) -> SelfDeserialize an Instance previously saved with save.
Called on the base class qubosolver.Instance, it loads any instance with
automatic dispatch.
Called on a subclass (e.g.
variable_fixing.Instance.load(f)),
it additionally requires the loaded Instance to be an instance of that subclass
(raising TypeError (external) otherwise).
Parameters:
-
file_like(FileLike[bytes (external)]) –Source file path or readable binary file object, as produced by
save.
Returns:
-
Self–A new instance of whichever concrete type wrote the tag.
Raises:
-
ValueError (external)–If the stream is not a qubosolver file, or if its type tag is missing or unrecognized.
-
TypeError (external)–If the loaded instance's type is not
clsor a subclass thereof.
Example
from pathlib import Pathfrom qubosolver import Instancefrom qubosolver.transforms import variable_fixing
file = Path("instance.bin")instance = variable_fixing.Instance(Instance())
with file.open("wb") as f: instance.save(f)
# Three ways to load it back, from least to most strict:with file.open("rb") as f: loaded = Instance.load(f) # accepts any Instance subtype # loads, then narrows (fails after loading) loaded = Instance.load(f).variable_fixing loaded = variable_fixing.Instance.load(f) # narrows first (fails before loading)Source code in qubosolver/types/instance.py
@classmethoddef load(cls, file_like: FileLike[bytes]) -> Self: """Deserialize an [`Instance`][] previously saved with [`save`][].
Called on the base class [`qubosolver.Instance`][], it loads any instance with automatic dispatch.
Called on a subclass (e.g. [`variable_fixing.Instance.load(f)`][qubosolver.transforms.variable_fixing.Instance.load]), it additionally requires the loaded `Instance` to be an instance of that subclass (raising [`TypeError`][] otherwise).
Args: file_like: Source file path or readable binary file object, as produced by [`save`][].
Returns: A new instance of whichever concrete type wrote the tag.
Raises: ValueError: If the stream is not a qubosolver file, or if its type tag is missing or unrecognized. TypeError: If the loaded instance's type is not `cls` or a subclass thereof.
Example: ```python from pathlib import Path from qubosolver import Instance from qubosolver.transforms import variable_fixing
file = Path("instance.bin") instance = variable_fixing.Instance(Instance())
with file.open("wb") as f: instance.save(f)
# Three ways to load it back, from least to most strict: with file.open("rb") as f: loaded = Instance.load(f) # accepts any Instance subtype # loads, then narrows (fails after loading) loaded = Instance.load(f).variable_fixing loaded = variable_fixing.Instance.load(f) # narrows first (fails before loading) ``` """ with io_utils.open(file_like, "rb") as f: io_utils.load_header(f) tag = io_utils.load_string(f) target_cls = Instance._registry.get(tag) if target_cls is None: raise ValueError(f"Cannot load Instance: unrecognized type tag {tag!r}.") instance = target_cls._read_body(f) if not isinstance(instance, cls): raise TypeError( f"Cannot load {cls.__module__}.{cls.__qualname__}: " f"stream contains a {type(instance).__module__}.{type(instance).__qualname__}." ) return instancesave(file_like: FileLike[bytes]) -> NoneSerialize this instance to file_like, tagged with its type.
Parameters:
-
file_like(FileLike[bytes (external)]) –Destination — a file path (
str(external) oros.PathLike(external)), or a binary-writabletyping.IO(external) stream.
Example
from pathlib import Path
with Path("instance.bin").open("wb") as f: instance.save(f)Source code in qubosolver/types/instance.py
def save(self, file_like: FileLike[bytes]) -> None: """Serialize this instance to ``file_like``, tagged with its type.
Args: file_like: Destination — a file path ([`str`][] or [`os.PathLike`][]), or a binary-writable [`typing.IO`][] stream.
Example: ```python from pathlib import Path
with Path("instance.bin").open("wb") as f: instance.save(f) ``` """ with io_utils.open(file_like, "wb") as f: io_utils.save_header(f) io_utils.save_string(f, self._tag()) self._write_body(f)
apply
Section titled “
apply
”apply(instance: qubosolver" href="../qubosolver/#qubosolver">qubosolver. Instance (qubosolver.Instance)" href="../qubosolver/#qubosolver.Instance">Instance) -> Instance (qubosolver.transforms.zeroing.Instance)" href="#qubosolver.transforms.zeroing.Instance">InstanceSet remaining negative off-diagonal coefficients to zero.
Approximates the QUBO by dropping any negative off-diagonal coefficient that
bit flips could not remove, so a quantum solver can embed it. Returns a
Instance whose
negative_matrix
holds the removed coefficients (an all-zero matrix when nothing was zeroed).
Parameters:
-
instance(qubosolver.Instance) –The QUBO instance to zero.
Returns:
-
Instance–A zeroing instance.
Source code in qubosolver/transforms/zeroing.py
def apply(instance: qubosolver.Instance) -> Instance: """Set remaining negative off-diagonal coefficients to zero.
Approximates the QUBO by dropping any negative off-diagonal coefficient that bit flips could not remove, so a quantum solver can embed it. Returns a [`Instance`][qubosolver.transforms.zeroing.Instance] whose [`negative_matrix`][qubosolver.transforms.zeroing.Instance.negative_matrix] holds the removed coefficients (an all-zero matrix when nothing was zeroed).
Args: instance: The QUBO instance to zero.
Returns: A zeroing instance. """ zeroed_instance = Instance(instance)
Q = instance.matrix n = Q.shape[0] offdiag_mask = ~torch.eye(n, dtype=torch.bool, device=Q.device) negative_mask = offdiag_mask & (Q < 0)
zeroed_instance.negative_matrix[negative_mask] = Q[negative_mask] zeroed_instance._matrix[negative_mask] = 0.0
return zeroed_instancelift(zeroed_solution: Solution
dataclass (qubosolver.types.Solution)" href="../qubosolver/#qubosolver.Solution">Solution, zeroed_instance: Instance (qubosolver.transforms.zeroing.Instance)" href="#qubosolver.transforms.zeroing.Instance">Instance) -> Solution
dataclass (qubosolver.types.Solution)" href="../qubosolver/#qubosolver.Solution">SolutionMap a solution of the zeroed QUBO back onto the pre-zeroing problem.
Zeroing only drops coefficients; it does not rename or remove variables, so
the bitstrings are carried over unchanged. Costs are recomputed against the
pre-zeroing matrix (_parent_instance) so they reflect the true, non-
approximated objective rather than the zeroed one. When nothing was zeroed,
returns a deep copy of zeroed_solution unchanged.
Parameters:
-
zeroed_solution(Solution) –Solution obtained on the zeroed QUBO.
-
zeroed_instance(Instance) –The zeroed instance produced by
apply.
Returns:
-
Solution–A new solution with costs evaluated against the pre-zeroing matrix.
Source code in qubosolver/transforms/zeroing.py
def lift(zeroed_solution: Solution, zeroed_instance: Instance) -> Solution: """Map a solution of the zeroed QUBO back onto the pre-zeroing problem.
Zeroing only drops coefficients; it does not rename or remove variables, so the bitstrings are carried over unchanged. Costs are recomputed against the pre-zeroing matrix (``_parent_instance``) so they reflect the true, non- approximated objective rather than the zeroed one. When nothing was zeroed, returns a deep copy of `zeroed_solution` unchanged.
Args: zeroed_solution: Solution obtained on the zeroed QUBO. zeroed_instance: The zeroed instance produced by [`apply`][].
Returns: A new solution with costs evaluated against the pre-zeroing matrix. """ if not zeroed_instance.zeroed_edges.numel(): return copy.deepcopy(zeroed_solution)
solution = Solution() solution.bitstrings = zeroed_solution.bitstrings solution.costs = vector.tensor( [zeroed_instance._parent_instance.cost(b) for b in solution.bitstrings] ) solution.counts = zeroed_solution.counts solution.probabilities = zeroed_solution.probabilities
return solution