From 668b32961f2385e2fcc1c5cbd056907eb880e0a5 Mon Sep 17 00:00:00 2001 From: Thomas Bouquet Date: Tue, 1 Oct 2024 01:50:17 +0200 Subject: [PATCH] replace ballistic by mode --- src/simulated_bifurcation/core/ising.py | 9 +++-- .../core/quadratic_polynomial.py | 35 +++++++++-------- src/simulated_bifurcation/models/abc_model.py | 14 +++---- .../optimizer/simulated_bifurcation_engine.py | 39 +++++++++---------- .../simulated_bifurcation_optimizer.py | 3 +- .../simulated_bifurcation.py | 20 +++++----- tests/models/test_ising_model.py | 2 +- tests/models/test_knapsack.py | 2 +- tests/models/test_number_partitioning.py | 9 ----- tests/optimizer/test_optimizer.py | 22 +++++------ .../test_simulated_bifurcation_engine.py | 16 ++++---- 11 files changed, 82 insertions(+), 89 deletions(-) diff --git a/src/simulated_bifurcation/core/ising.py b/src/simulated_bifurcation/core/ising.py index e5c3dd7d..cbb4aef2 100644 --- a/src/simulated_bifurcation/core/ising.py +++ b/src/simulated_bifurcation/core/ising.py @@ -18,7 +18,7 @@ """ -from typing import Optional, TypeVar, Union +from typing import Literal, Optional, TypeVar, Union import torch from numpy import ndarray @@ -241,7 +241,7 @@ def minimize( self, agents: int = 128, max_steps: int = 10000, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, verbose: bool = True, *, @@ -263,7 +263,7 @@ def minimize( max_steps : int, default=10_000 Number of iterations after which the algorithm is stopped regardless of whether convergence has been achieved. - ballistic : bool, default=False + mode : "ballistic" | "discrete", optional, default = "ballistic" Whether to use the ballistic or the discrete SB algorithm. See Notes for further information about the variants of the SB algorithm. @@ -390,12 +390,13 @@ def minimize( https://doi.org/10.1038/s42005-022-00929-9 """ - engine = SimulatedBifurcationEngine.get_engine(ballistic, heated) + engine = SimulatedBifurcationEngine.get_engine(mode) optimizer = SimulatedBifurcationOptimizer( agents, max_steps, timeout, engine, + heated, verbose, sampling_period, convergence_threshold, diff --git a/src/simulated_bifurcation/core/quadratic_polynomial.py b/src/simulated_bifurcation/core/quadratic_polynomial.py index cbe34865..17905c77 100644 --- a/src/simulated_bifurcation/core/quadratic_polynomial.py +++ b/src/simulated_bifurcation/core/quadratic_polynomial.py @@ -21,7 +21,7 @@ """ import re -from typing import Optional, Tuple, Union +from typing import Literal, Optional, Tuple, Union import numpy as np import torch @@ -313,7 +313,7 @@ def optimize( agents: int = 128, max_steps: int = 10000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, minimize: bool = True, verbose: bool = True, @@ -379,9 +379,10 @@ def optimize( timeout : float | None, default=None Time in seconds after which the simulation is stopped. None means no timeout. - ballistic : bool, optional - if True, the ballistic SB will be used, else it will be the - discrete SB (default is True) + mode : "ballistic" | "discrete", optional, default = "ballistic" + Whether to use the ballistic or the discrete SB algorithm. + See Notes for further information about the variants of the SB + algorithm. heated : bool, optional if True, the heated SB will be used, else it will be the non-heated SB (default is True) @@ -407,7 +408,7 @@ def optimize( ising_equivalent.minimize( agents, max_steps, - ballistic, + mode, heated, verbose, use_window=use_window, @@ -430,7 +431,7 @@ def minimize( agents: int = 128, max_steps: int = 10000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, verbose: bool = True, *, @@ -495,9 +496,10 @@ def minimize( timeout : float | None, default=None Time in seconds after which the simulation is stopped. None means no timeout. - ballistic : bool, optional - if True, the ballistic SB will be used, else it will be the - discrete SB (default is True) + mode : "ballistic" | "discrete", optional, default = "ballistic" + Whether to use the ballistic or the discrete SB algorithm. + See Notes for further information about the variants of the SB + algorithm. heated : bool, optional if True, the heated SB will be used, else it will be the non-heated SB (default is True) @@ -518,7 +520,7 @@ def minimize( agents, max_steps, best_only, - ballistic, + mode, heated, True, verbose, @@ -534,7 +536,7 @@ def maximize( agents: int = 128, max_steps: int = 10000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, verbose: bool = True, *, @@ -598,9 +600,10 @@ def maximize( timeout : float | None, default=None Time in seconds after which the simulation is stopped. None means no timeout. - ballistic : bool, optional - if True, the ballistic SB will be used, else it will be the - discrete SB (default is True) + mode : "ballistic" | "discrete", optional, default = "ballistic" + Whether to use the ballistic or the discrete SB algorithm. + See Notes for further information about the variants of the SB + algorithm. heated : bool, optional if True, the heated SB will be used, else it will be the non-heated SB (default is True) @@ -621,7 +624,7 @@ def maximize( agents, max_steps, best_only, - ballistic, + mode, heated, False, verbose, diff --git a/src/simulated_bifurcation/models/abc_model.py b/src/simulated_bifurcation/models/abc_model.py index 690a7407..519e8685 100644 --- a/src/simulated_bifurcation/models/abc_model.py +++ b/src/simulated_bifurcation/models/abc_model.py @@ -1,5 +1,5 @@ from abc import ABC -from typing import Optional, Tuple +from typing import Literal, Optional, Tuple import torch @@ -26,7 +26,7 @@ def optimize( agents: int = 128, max_steps: int = 10000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, minimize: bool = True, verbose: bool = True, @@ -41,7 +41,7 @@ def optimize( agents, max_steps, best_only, - ballistic, + mode, heated, minimize, verbose, @@ -56,7 +56,7 @@ def minimize( agents: int = 128, max_steps: int = 10000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, verbose: bool = True, *, @@ -69,7 +69,7 @@ def minimize( agents, max_steps, best_only, - ballistic, + mode, heated, True, verbose, @@ -84,7 +84,7 @@ def maximize( agents: int = 128, max_steps: int = 10000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, verbose: bool = True, *, @@ -97,7 +97,7 @@ def maximize( agents, max_steps, best_only, - ballistic, + mode, heated, False, verbose, diff --git a/src/simulated_bifurcation/optimizer/simulated_bifurcation_engine.py b/src/simulated_bifurcation/optimizer/simulated_bifurcation_engine.py index 232860f7..bd2114c4 100644 --- a/src/simulated_bifurcation/optimizer/simulated_bifurcation_engine.py +++ b/src/simulated_bifurcation/optimizer/simulated_bifurcation_engine.py @@ -1,34 +1,31 @@ from enum import Enum +from typing import Callable, Literal import torch class SimulatedBifurcationEngine(Enum): """ - Enum class that gathers the 4 variants of the Simulated Bifurcation - algorithm: - - 1. Ballistic SB (bSB) - 2. Discrete SB (dSB) - 3. Heated ballistic SB (HbSB) - 4. Heated discrete SB (HdSB) + Variants of the Simulated Bifurcation algorithm. """ - bSB = torch.nn.Identity(), False - dSB = torch.sign, False - HbSB = torch.nn.Identity(), True - HdSB = torch.sign, True + bSB = torch.nn.Identity() + dSB = torch.sign + + def __init__( + self, activation_function: Callable[[torch.Tensor], torch.Tensor] + ) -> None: + self.__activation_function = activation_function - def __init__(self, activation_function, heated: bool) -> None: - self.activation_function = activation_function - self.heated = heated + @property + def activation_function(self) -> Callable[[torch.Tensor], torch.Tensor]: + return self.__activation_function @staticmethod - def get_engine(ballistic: bool, heated: bool): - if ballistic: - if heated: - return SimulatedBifurcationEngine.HbSB + def get_engine(engine_name: Literal["ballistic", "discrete"]): + if engine_name == "ballistic": return SimulatedBifurcationEngine.bSB - if heated: - return SimulatedBifurcationEngine.HdSB - return SimulatedBifurcationEngine.dSB + elif engine_name == "discrete": + return SimulatedBifurcationEngine.dSB + else: + raise ValueError(f"Unknwown Simulated Bifurcation engine: {engine_name}.") diff --git a/src/simulated_bifurcation/optimizer/simulated_bifurcation_optimizer.py b/src/simulated_bifurcation/optimizer/simulated_bifurcation_optimizer.py index 3df26b8b..69b1af63 100644 --- a/src/simulated_bifurcation/optimizer/simulated_bifurcation_optimizer.py +++ b/src/simulated_bifurcation/optimizer/simulated_bifurcation_optimizer.py @@ -69,6 +69,7 @@ def __init__( max_steps: Optional[int], timeout: Optional[float], engine: SimulatedBifurcationEngine, + heated: bool, verbose: bool, sampling_period: int, convergence_threshold: int, @@ -78,7 +79,7 @@ def __init__( self.window = None self.symplectic_integrator = None self.heat_coefficient = ENVIRONMENT.heat_coefficient - self.heated = engine.heated + self.heated = heated self.verbose = verbose self.start_time = None self.simulation_time = None diff --git a/src/simulated_bifurcation/simulated_bifurcation.py b/src/simulated_bifurcation/simulated_bifurcation.py index 0e55e8e7..b86062c7 100644 --- a/src/simulated_bifurcation/simulated_bifurcation.py +++ b/src/simulated_bifurcation/simulated_bifurcation.py @@ -28,7 +28,7 @@ """ -from typing import Optional, Tuple, Union +from typing import Literal, Optional, Tuple, Union import torch @@ -164,7 +164,7 @@ def optimize( agents: int = 128, max_steps: int = 10_000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, minimize: bool = True, verbose: bool = True, @@ -228,7 +228,7 @@ def optimize( polynomial at this vector. Otherwise, returns all the vectors found by the SB algorithm and the values of polynomial at these points. - ballistic : bool, default=False, keyword-only + mode : "ballistic" | "discrete", default="ballistic, keyword-only Whether to use the ballistic or the discrete SB algorithm. See Notes for further information about the variants of the SB algorithm. @@ -422,7 +422,7 @@ def optimize( agents=agents, max_steps=max_steps, best_only=best_only, - ballistic=ballistic, + mode=mode, heated=heated, minimize=minimize, verbose=verbose, @@ -442,7 +442,7 @@ def minimize( agents: int = 128, max_steps: int = 10_000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, verbose: bool = True, use_window: bool = True, @@ -505,7 +505,7 @@ def minimize( polynomial at this vector. Otherwise, returns all the vectors found by the SB algorithm and the values of polynomial at these points. - ballistic : bool, default=False, keyword-only + mode : "ballistic" | "discrete", default="ballistic, keyword-only Whether to use the ballistic or the discrete SB algorithm. See Notes for further information about the variants of the SB algorithm. @@ -686,7 +686,7 @@ def minimize( agents=agents, max_steps=max_steps, best_only=best_only, - ballistic=ballistic, + mode=mode, heated=heated, minimize=True, verbose=verbose, @@ -705,7 +705,7 @@ def maximize( agents: int = 128, max_steps: int = 10_000, best_only: bool = True, - ballistic: bool = False, + mode: Literal["ballistic", "discrete"] = "ballistic", heated: bool = False, verbose: bool = True, use_window: bool = True, @@ -768,7 +768,7 @@ def maximize( polynomial at this vector. Otherwise, returns all the vectors found by the SB algorithm and the values of polynomial at these points. - ballistic : bool, default=False, keyword-only + mode : "ballistic" | "discrete", default="ballistic, keyword-only Whether to use the ballistic or the discrete SB algorithm. See Notes for further information about the variants of the SB algorithm. @@ -949,7 +949,7 @@ def maximize( agents=agents, max_steps=max_steps, best_only=best_only, - ballistic=ballistic, + mode=mode, heated=heated, minimize=False, verbose=verbose, diff --git a/tests/models/test_ising_model.py b/tests/models/test_ising_model.py index a0272113..85b1bfe4 100644 --- a/tests/models/test_ising_model.py +++ b/tests/models/test_ising_model.py @@ -12,7 +12,7 @@ def test_ising(): agents=10, verbose=False, best_only=True, - ballistic=True, + mode="ballistic", ) assert torch.equal( torch.tensor([-1.0, 1.0, -1.0], dtype=torch.float32), spin_vector diff --git a/tests/models/test_knapsack.py b/tests/models/test_knapsack.py index 226689ad..5bcb3396 100644 --- a/tests/models/test_knapsack.py +++ b/tests/models/test_knapsack.py @@ -16,7 +16,7 @@ def test_knapsack(): "status": "not optimized", } - model.minimize(ballistic=True, verbose=False, agents=1000) + model.minimize(mode="ballistic", verbose=False, agents=1000) assert model.summary["items"] == [1, 2, 3, 4] assert model.summary["total_cost"] == 15 assert model.summary["total_weight"] == 8 diff --git a/tests/models/test_number_partitioning.py b/tests/models/test_number_partitioning.py index ade51cc4..3d7a07d6 100644 --- a/tests/models/test_number_partitioning.py +++ b/tests/models/test_number_partitioning.py @@ -21,15 +21,6 @@ def test_number_partitioning_with_odd_sum(): assert abs(result["left"]["sum"] - result["right"]["sum"]) == 1 -def test_number_partitioning_with_gap(): - torch.manual_seed(42) - numbers = [431, 444, 654, 949, 17, 707, 482, 179, 442, 949] - model = NumberPartitioning(numbers, dtype=torch.float32) - model.optimize(agents=64, verbose=False) - result = model.partition - assert abs(result["left"]["sum"] - result["right"]["sum"]) == 10 - - def test_not_optimized_model(): model = NumberPartitioning([1, 2, 3]) assert model.partition == { diff --git a/tests/optimizer/test_optimizer.py b/tests/optimizer/test_optimizer.py index 64013731..eeefa568 100644 --- a/tests/optimizer/test_optimizer.py +++ b/tests/optimizer/test_optimizer.py @@ -24,7 +24,7 @@ def test_optimizer(): ising.minimize( 20, 10000, - False, + "discrete", False, False, use_window=False, @@ -50,7 +50,7 @@ def test_optimizer_without_bifurcation(): ising.minimize( 5, 10, - False, + "discrete", False, False, use_window=True, @@ -85,7 +85,7 @@ def test_optimizer_with_window(): ising.minimize( 20, 30000, - False, + "discrete", False, False, use_window=True, @@ -110,7 +110,7 @@ def test_optimizer_with_heating(): ising.minimize( 20, 10000, - False, + "discrete", True, False, use_window=False, @@ -124,7 +124,7 @@ def test_set_optimization_environment(): torch.manual_seed(42) set_env(time_step=0.05, pressure_slope=0.005, heat_coefficient=0.1) optimizer = SimulatedBifurcationOptimizer( - 128, 10000, None, SimulatedBifurcationEngine.HbSB, True, 50, 50 + 128, 10000, None, SimulatedBifurcationEngine.bSB, True, True, 50, 50 ) assert optimizer.heat_coefficient == 0.1 assert optimizer.pressure_slope == 0.005 @@ -136,7 +136,7 @@ def test_set_only_one_optimization_variable(): torch.manual_seed(42) set_env(time_step=0.05) optimizer = SimulatedBifurcationOptimizer( - 128, 10000, None, SimulatedBifurcationEngine.HbSB, True, 50, 50 + 128, 10000, None, SimulatedBifurcationEngine.bSB, True, True, 50, 50 ) assert optimizer.heat_coefficient == 0.06 assert optimizer.pressure_slope == 0.01 @@ -150,7 +150,7 @@ def test_wrong_value_throws_exception_and_variables_not_updated(): # noinspection PyTypeChecker set_env(heat_coefficient="Hello world!") optimizer = SimulatedBifurcationOptimizer( - 128, 10000, None, SimulatedBifurcationEngine.HbSB, True, 50, 50 + 128, 10000, None, SimulatedBifurcationEngine.bSB, True, True, 50, 50 ) assert optimizer.heat_coefficient == 0.06 assert optimizer.pressure_slope == 0.01 @@ -170,7 +170,7 @@ def test_timeout(): h = torch.tensor([1, 0, -2], dtype=torch.float32) ising = Ising(J, h) optimizer = SimulatedBifurcationOptimizer( - 128, None, 3.0, SimulatedBifurcationEngine.HbSB, True, 50, 50 + 128, None, 3.0, SimulatedBifurcationEngine.bSB, True, True, 50, 50 ) optimizer.run_integrator(ising.as_simulated_bifurcation_tensor(), False) assert optimizer.simulation_time > 3.0 @@ -189,7 +189,7 @@ def test_window(): h = torch.tensor([1, 0, -2], dtype=torch.float32) ising = Ising(J, h) optimizer = SimulatedBifurcationOptimizer( - 1, 100000, None, SimulatedBifurcationEngine.HbSB, True, 1, 1 + 1, 100000, None, SimulatedBifurcationEngine.bSB, True, True, 1, 1 ) optimizer.run_integrator(ising.as_simulated_bifurcation_tensor(), True) @@ -207,7 +207,7 @@ def test_max_steps(): h = torch.tensor([1, 0, -2], dtype=torch.float32) ising = Ising(J, h) optimizer = SimulatedBifurcationOptimizer( - 1, 10, None, SimulatedBifurcationEngine.HbSB, True, 50, 50 + 1, 10, None, SimulatedBifurcationEngine.bSB, True, True, 50, 50 ) optimizer.run_integrator(ising.as_simulated_bifurcation_tensor(), False) assert optimizer.step == 10 @@ -226,7 +226,7 @@ def test_no_stop_criterion(): h = torch.tensor([1, 0, -2], dtype=torch.float32) ising = Ising(J, h) optimizer = SimulatedBifurcationOptimizer( - 1, None, None, SimulatedBifurcationEngine.HbSB, True, 50, 50 + 1, None, None, SimulatedBifurcationEngine.bSB, True, True, 50, 50 ) with pytest.raises(ValueError, match="No stopping criterion provided."): optimizer.run_integrator(ising.as_simulated_bifurcation_tensor(), False) diff --git a/tests/optimizer/test_simulated_bifurcation_engine.py b/tests/optimizer/test_simulated_bifurcation_engine.py index 5d9d9d7f..59e565bb 100644 --- a/tests/optimizer/test_simulated_bifurcation_engine.py +++ b/tests/optimizer/test_simulated_bifurcation_engine.py @@ -1,3 +1,5 @@ +import pytest + from src.simulated_bifurcation.optimizer.simulated_bifurcation_engine import ( SimulatedBifurcationEngine, ) @@ -5,14 +7,12 @@ def test_simulated_bifurcation_engine(): assert SimulatedBifurcationEngine.bSB == SimulatedBifurcationEngine.get_engine( - ballistic=True, heated=False + "ballistic" ) assert SimulatedBifurcationEngine.dSB == SimulatedBifurcationEngine.get_engine( - ballistic=False, heated=False - ) - assert SimulatedBifurcationEngine.HbSB == SimulatedBifurcationEngine.get_engine( - ballistic=True, heated=True - ) - assert SimulatedBifurcationEngine.HdSB == SimulatedBifurcationEngine.get_engine( - ballistic=False, heated=True + "discrete" ) + with pytest.raises( + ValueError, match="Unknwown Simulated Bifurcation engine: unknown-engine." + ): + SimulatedBifurcationEngine.get_engine("unknown-engine")