diff --git a/src/lava/lib/optimization/solvers/generic/read_gate/models.py b/src/lava/lib/optimization/solvers/generic/read_gate/models.py index 77e709a3..676e8592 100644 --- a/src/lava/lib/optimization/solvers/generic/read_gate/models.py +++ b/src/lava/lib/optimization/solvers/generic/read_gate/models.py @@ -2,13 +2,13 @@ # SPDX-License-Identifier: BSD-3-Clause # See: https://spdx.org/licenses/ import numpy as np - from lava.lib.optimization.solvers.generic.read_gate.process import ReadGate + from lava.magma.core.decorator import implements, requires from lava.magma.core.model.py.model import PyLoihiProcessModel from lava.magma.core.model.py.ports import PyInPort, PyOutPort, PyRefPort from lava.magma.core.model.py.type import LavaPyType -from lava.magma.core.resources import CPU +from lava.magma.core.resources import CPU, LMT from lava.magma.core.sync.protocols.loihi_protocol import LoihiProtocol @@ -52,36 +52,37 @@ def readgate_run_post_mgmt(self): def get_readgate_members(num_in_ports): - in_ports = { - f"cost_in_{id}": LavaPyType(PyInPort.VEC_DENSE, np.int32, - precision=32) + readgate_members = dict(min_cost=None, min_cost_id=None, solution=None) + + in_ports_py = { + f"cost_in_{id}": LavaPyType(PyInPort.VEC_DENSE, np.int32, precision=32) for id in range(num_in_ports) } - readgate_members = { - "target_cost": LavaPyType(int, np.int32, 32), - "best_solution": LavaPyType(int, np.int32, 32), - "cost_out": LavaPyType(PyOutPort.VEC_DENSE, np.int32, - precision=32), - "solution_out": LavaPyType(PyOutPort.VEC_DENSE, np.int32, - precision=32), - "send_pause_request": LavaPyType( + + class_members_py = dict( + target_cost=LavaPyType(int, np.int32, 32), + best_solution=LavaPyType(int, np.int32, 32), + cost_out=LavaPyType(PyOutPort.VEC_DENSE, np.int32, precision=32), + solution_out=LavaPyType(PyOutPort.VEC_DENSE, np.int32, precision=32), + send_pause_request=LavaPyType( PyOutPort.VEC_DENSE, np.int32, precision=32 ), - "solution_reader": LavaPyType( - PyRefPort.VEC_DENSE, np.int32, precision=32 - ), - "min_cost": None, - "min_cost_id": None, - "solution": None, - "post_guard": readgate_post_guard, - "run_spk": readgate_run_spk, - "run_post_mgmt": readgate_run_post_mgmt, - } - readgate_members.update(in_ports) + solution_reader=LavaPyType(PyRefPort.VEC_DENSE, np.int32, precision=32), + ) + + cpu_specific = dict( + post_guard=readgate_post_guard, + run_spk=readgate_run_spk, + run_post_mgmt=readgate_run_post_mgmt, + ) + + readgate_members.update(in_ports_py) + readgate_members.update(class_members_py) + readgate_members.update(cpu_specific) return readgate_members -def get_read_gate_model_class(num_in_ports: int): +def get_read_gate_py_model_class(num_in_ports: int): """Produce CPU model for the ReadGate process. The model verifies if better payload (cost) has been notified by the @@ -89,19 +90,18 @@ def get_read_gate_model_class(num_in_ports: int): out to the upstream process the new payload (cost) and the network state. """ - ReadGatePyModelBase = type( - "ReadGatePyModel", - (PyLoihiProcessModel,), + super_class = PyLoihiProcessModel + resource = CPU + ReadGateModelBase = type( + "ReadGateModel", + (super_class,), get_readgate_members(num_in_ports), ) - ReadGatePyModelImpl = implements(ReadGate, protocol=LoihiProtocol)( - ReadGatePyModelBase + ReadGateModelImpl = implements(ReadGate, protocol=LoihiProtocol)( + ReadGateModelBase ) - ReadGatePyModel = requires(CPU)(ReadGatePyModelImpl) - return ReadGatePyModel - - -ReadGatePyModel = get_read_gate_model_class(num_in_ports=1) + ReadGateModel = requires(resource)(ReadGateModelImpl) + return ReadGateModel @implements(ReadGate, protocol=LoihiProtocol) @@ -110,13 +110,14 @@ class ReadGatePyModelD(PyLoihiProcessModel): """CPU model for the ReadGate process. The model verifies if better payload (cost) has been notified by the - downstream processes, if so, it reads those processes state and sends out to + downstream processes, if so, it reads those processes state and sends + out to the upstream process the new payload (cost) and the network state. """ + target_cost: int = LavaPyType(int, np.int32, 32) best_solution: int = LavaPyType(int, np.int32, 32) - cost_in: PyInPort = LavaPyType(PyInPort.VEC_DENSE, np.int32, - precision=32) + cost_in: PyInPort = LavaPyType(PyInPort.VEC_DENSE, np.int32, precision=32) cost_out: PyOutPort = LavaPyType( PyOutPort.VEC_DENSE, np.int32, precision=32 ) @@ -142,7 +143,7 @@ def run_spk(self): self.min_cost = cost[0] self.cost_out.send(np.array([0])) elif self.solution is not None: - timestep = - np.array([self.time_step]) + timestep = -np.array([self.time_step]) if self.min_cost <= self.target_cost: self._req_pause = True self.cost_out.send(np.array([self.min_cost])) diff --git a/src/lava/lib/optimization/solvers/generic/sconfig.py b/src/lava/lib/optimization/solvers/generic/sconfig.py deleted file mode 100644 index b8300e34..00000000 --- a/src/lava/lib/optimization/solvers/generic/sconfig.py +++ /dev/null @@ -1 +0,0 @@ -num_in_ports = 1 diff --git a/src/lava/lib/optimization/solvers/generic/solver.py b/src/lava/lib/optimization/solvers/generic/solver.py index e32421e5..92fb0644 100644 --- a/src/lava/lib/optimization/solvers/generic/solver.py +++ b/src/lava/lib/optimization/solvers/generic/solver.py @@ -1,40 +1,26 @@ # Copyright (C) 2021 Intel Corporation # SPDX-License-Identifier: BSD-3-Clause # See: https://spdx.org/licenses/ -import numpy as np import typing as ty - from dataclasses import dataclass + +import numpy as np from lava.lib.optimization.problems.problems import OptimizationProblem from lava.lib.optimization.solvers.generic.builder import SolverProcessBuilder -from lava.lib.optimization.solvers.generic.hierarchical_processes import ( - NEBMAbstract, - NEBMSimulatedAnnealingAbstract, -) - -from lava.lib.optimization.solvers.generic.scif.models import ( - PyModelQuboScifFixed, -) +from lava.lib.optimization.solvers.generic.hierarchical_processes import \ + NEBMAbstract, NEBMSimulatedAnnealingAbstract from lava.lib.optimization.solvers.generic.nebm.models import NEBMPyModel +from lava.lib.optimization.solvers.generic.nebm.process import NEBM, \ + NEBMSimulatedAnnealing +from lava.lib.optimization.solvers.generic.scif.models import \ + PyModelQuboScifFixed from lava.lib.optimization.solvers.generic.scif.process import QuboScif -from lava.lib.optimization.solvers.generic.nebm.process import NEBM -from lava.lib.optimization.solvers.generic.cost_integrator.process import ( - CostIntegrator, -) -from lava.lib.optimization.solvers.generic.nebm.process import ( - NEBMSimulatedAnnealing, -) -from lava.lib.optimization.solvers.generic.sub_process_models import ( - NEBMAbstractModel, - NEBMSimulatedAnnealingAbstractModel, -) - -from lava.magma.core.resources import ( - AbstractComputeResource, - CPU, - Loihi2NeuroCore, - NeuroCore, -) +from lava.lib.optimization.solvers.generic.sub_process_models import \ + NEBMAbstractModel, NEBMSimulatedAnnealingAbstractModel +from lava.lib.optimization.solvers.generic.types_optim import BACKENDS_TYPE, \ + HP_TYPE, CPUS, NEUROCORES, BACKEND_MSG +from lava.magma.core.resources import AbstractComputeResource, CPU, \ + Loihi2NeuroCore from lava.magma.core.run_conditions import RunSteps from lava.magma.core.run_configs import Loihi1SimCfg, Loihi2HwCfg from lava.magma.core.sync.protocol import AbstractSyncProtocol @@ -45,17 +31,13 @@ from lava.utils.profiler import Profiler try: - from lava.lib.optimization.solvers.generic.read_gate.ncmodels import ( - ReadGateCModel, - ) + from lava.lib.optimization.solvers.generic.read_gate.ncmodels import \ + ReadGateCModel from lava.proc.dense.ncmodels import NcModelDense - from lava.lib.optimization.solvers.generic.nebm.ncmodels import ( - NEBMNcModel, - NEBMSimulatedAnnealingNcModel, - ) - from lava.lib.optimization.solvers.generic.cost_integrator.ncmodels import ( - CostIntegratorNcModel, - ) + from lava.lib.optimization.solvers.generic.nebm.ncmodels import \ + NEBMNcModel, NEBMSimulatedAnnealingNcModel + from lava.lib.optimization.solvers.generic.cost_integrator.ncmodels \ + import CostIntegratorNcModel except ImportError: class ReadGateCModel: @@ -74,27 +56,6 @@ class CostIntegratorNcModel: pass -from lava.lib.optimization.solvers.generic.read_gate.models import ( - ReadGatePyModel, -) - -BACKENDS = ty.Union[CPU, Loihi2NeuroCore, NeuroCore, str] -HP_TYPE = ty.Union[ty.Dict, ty.List[ty.Dict]] -CPUS = [CPU, "CPU"] -NEUROCORES = [Loihi2NeuroCore, NeuroCore, "Loihi2"] - -BACKEND_MSG = f""" was requested as backend. However, -the solver currently supports only Loihi 2 and CPU backends. -These can be specified by calling solve with any of the following: -backend = "CPU" -backend = "Loihi2" -backend = CPU -backend = Loihi2NeuroCore -backend = NeuroCoreS -The explicit resource classes can be imported from -lava.magma.core.resources""" - - @dataclass class SolverConfig: """ @@ -103,38 +64,38 @@ class SolverConfig: Parameters ---------- timeout: int - Maximum number of iterations (timesteps) to be run. If set to -1 - then the solver will run continuously in non-blocking mode until a - solution is found. + Maximum number of iterations (timesteps) to be run. If set to -1 + then the solver will run continuously in non-blocking mode until a + solution is found. target_cost: int, optional - A cost value provided by the user as a target for the solution to be - found by the solver, when a solution with such cost is found and - read, execution ends. + A cost value provided by the user as a target for the solution to be + found by the solver, when a solution with such cost is found and + read, execution ends. backend: BACKENDS, optional - Specifies the backend where the main solver network will be - deployed. + Specifies the backend where the main solver network will be + deployed. hyperparameters: - ty.Union[ty.Dict, ty.Dict[str, ty.Union[int, npt.ArrayLike]]], - optional. - A dictionary specifying values for steps_to_fire, noise_amplitude, - step_size and init_value. All but the last are integers, the initial - value is an array-like of initial values for the variables defining - the problem. + ty.Union[ty.Dict, ty.Dict[str, ty.Union[int, npt.ArrayLike]]], + optional. + A dictionary specifying values for steps_to_fire, noise_amplitude, + step_size and init_value. All but the last are integers, the initial + value is an array-like of initial values for the variables defining + the problem. probe_cost: bool - A boolean flag to request cost tracking through time. + A boolean flag to request cost tracking through time. probe_time: bool - A boolean flag to request time profiling, available only on "Loihi2" - backend. + A boolean flag to request time profiling, available only on "Loihi2" + backend. probe_energy: bool - A boolean flag to request time profiling, available only on "Loihi2" - backend. + A boolean flag to request time profiling, available only on "Loihi2" + backend. log_level: int - Select log verbosity (40: default, 20: verbose). + Select log verbosity (40: default, 20: verbose). """ timeout: int = 1e3 target_cost: int = 0 - backend: BACKENDS = CPU + backend: BACKENDS_TYPE = CPU hyperparameters: HP_TYPE = None probe_cost: bool = False probe_time: bool = False @@ -150,15 +111,15 @@ class SolverReport: Parameters ---------- best_cost: int - Best cost found during the execution. + Best cost found during the execution. best_state: np.ndarray - Candidate solution associated to the best cost. + Candidate solution associated to the best cost. best_timestep: int - Execution timestep during which the best solution was found. + Execution timestep during which the best solution was found. solver_config: SolverConfig - Solver configuraiton used. Refers to SolverConfig documentation. + Solver configuraiton used. Refers to SolverConfig documentation. profiler: Profiler - Profiler instance containing time, energy and activity measurements. + Profiler instance containing time, energy and activity measurements. """ best_cost: int = None @@ -179,9 +140,9 @@ def solve( Parameters ---------- problem: OptimizationProblem - Optimization problem to be solved. + Optimization problem to be solved. config: SolverConfig, optional - Solver configuraiton used. Refers to SolverConfig documentation. + Solver configuraiton used. Refers to SolverConfig documentation. """ solver = OptimizationSolver(problem) report = solver.solve(config=config) @@ -209,7 +170,7 @@ def __init__(self, problem: OptimizationProblem): Parameters ---------- problem: OptimizationProblem - Optimization problem to be solved. + Optimization problem to be solved. """ self.problem = problem self._process_builder = SolverProcessBuilder() @@ -225,12 +186,12 @@ def solve(self, config: SolverConfig = SolverConfig()) -> SolverReport: Parameters ---------- config: SolverConfig, optional - Solver configuration used. Refers to SolverConfig documentation. + Solver configuration used. Refers to SolverConfig documentation. Returns ---------- report: SolverReport - An object containing all the data generated by the execution. + An object containing all the data generated by the execution. """ run_condition, run_cfg = self._prepare_solver(config) self.solver_process.run(condition=run_condition, run_cfg=run_cfg) @@ -253,7 +214,8 @@ def _prepare_solver(self, config: SolverConfig): if config.probe_cost: if config.backend in NEUROCORES: # from lava.utils.loihi2_state_probes import StateProbe - # self._cost_tracker = StateProbe(self.solver_process.optimality + # self._cost_tracker = StateProbe( + # self.solver_process.optimality # ) raise NotImplementedError if config.backend in CPUS: @@ -278,7 +240,7 @@ def _create_solver_process(self, config: SolverConfig) -> None: Parameters ---------- config: SolverConfig - Solver configuraiton used. Refers to SolverConfig documentation. + Solver configuraiton used. Refers to SolverConfig documentation. """ requirements, protocol = self._get_requirements_and_protocol( backend=config.backend @@ -297,16 +259,16 @@ def _create_solver_process(self, config: SolverConfig) -> None: self.solver_process._log_config.level = config.log_level def _get_requirements_and_protocol( - self, backend: BACKENDS + self, backend: BACKENDS_TYPE ) -> ty.Tuple[AbstractComputeResource, AbstractSyncProtocol]: """ Figure out requirements and protocol for a given backend. Parameters ---------- - backend: BACKENDS - Specifies the backend for which requirements and protocol classes - will be returned. + backend: BACKENDS_TYPE + Specifies the backend for which requirements and protocol + classes will be returned. """ return [CPU] if backend in CPUS else [Loihi2NeuroCore], LoihiProtocol @@ -321,17 +283,15 @@ def _get_cost_tracking(self): return self._cost_tracker.time_series def _get_run_config( - self, backend: BACKENDS, probes=None, num_in_ports: int = None + self, backend: BACKENDS_TYPE, probes=None, num_in_ports: int = None ): from lava.lib.optimization.solvers.generic.read_gate.process import ( ReadGate, ) - from lava.lib.optimization.solvers.generic.read_gate.models import ( - get_read_gate_model_class, - ) - if backend in CPUS: - ReadGatePyModel = get_read_gate_model_class(num_in_ports) + from lava.lib.optimization.solvers.generic.read_gate.models \ + import get_read_gate_py_model_class + ReadGatePyModel = get_read_gate_py_model_class(num_in_ports) pdict = { self.solver_process: self.solver_model, ReadGate: ReadGatePyModel, @@ -344,16 +304,18 @@ def _get_run_config( exception_proc_model_map=pdict, select_sub_proc_model=True ) elif backend in NEUROCORES: + from lava.lib.optimization.solvers.generic.read_gate.ncmodels \ + import get_read_gate_c_model_class + ReadGateCModel = get_read_gate_c_model_class(num_in_ports, backend) pdict = { self.solver_process: self.solver_model, ReadGate: ReadGateCModel, - Dense: NcModelDense, + # Dense: NcModelDense, NEBMAbstract: NEBMAbstractModel, - NEBM: NEBMNcModel, + # NEBM: NEBMNcModel, NEBMSimulatedAnnealingAbstract: NEBMSimulatedAnnealingAbstractModel, NEBMSimulatedAnnealing: NEBMSimulatedAnnealingNcModel, - CostIntegrator: CostIntegratorNcModel, } return Loihi2HwCfg( exception_proc_model_map=pdict, @@ -381,7 +343,8 @@ def _get_results(self, config: SolverConfig): return best_state, int(best_cost), int(best_timestep) def _get_best_state(self, config: SolverConfig, idx: int): - if isinstance(config.hyperparameters, list): + if isinstance(config.hyperparameters, list) and len( + config.hyperparameters) > 1: raw_solution = np.asarray( self.solver_process.finders[int(idx)].variables_assignment.get() ).astype(np.int32) diff --git a/src/lava/lib/optimization/solvers/generic/types_optim.py b/src/lava/lib/optimization/solvers/generic/types_optim.py new file mode 100644 index 00000000..fff9f783 --- /dev/null +++ b/src/lava/lib/optimization/solvers/generic/types_optim.py @@ -0,0 +1,22 @@ +# Copyright (C) 2021 Intel Corporation +# SPDX-License-Identifier: BSD-3-Clause +# See: https://spdx.org/licenses/ +import typing as ty + +from lava.magma.core.resources import CPU, Loihi2NeuroCore, NeuroCore + +BACKENDS = [CPU, Loihi2NeuroCore, NeuroCore, "Loihi2", "CPU"] +BACKENDS_TYPE = ty.Union[CPU, Loihi2NeuroCore, NeuroCore, str] +HP_TYPE = ty.Union[ty.Dict, ty.List[ty.Dict]] +CPUS = [CPU, "CPU"] +NEUROCORES = [Loihi2NeuroCore, NeuroCore, "Loihi2"] +BACKEND_MSG = f""" was requested as backend. However, +the solver currently supports only Loihi 2 and CPU backends. +These can be specified by calling solve with any of the following: +backend = "CPU" +backend = "Loihi2" +backend = CPU +backend = Loihi2NeuroCore +backend = NeuroCoreS +The explicit resource classes can be imported from +lava.magma.core.resources""" diff --git a/tests/lava/lib/optimization/solvers/generic/integration/test_finder_reader_integration.py b/tests/lava/lib/optimization/solvers/generic/integration/test_finder_reader_integration.py index cafd4fca..c3ba4f7e 100644 --- a/tests/lava/lib/optimization/solvers/generic/integration/test_finder_reader_integration.py +++ b/tests/lava/lib/optimization/solvers/generic/integration/test_finder_reader_integration.py @@ -7,7 +7,7 @@ import numpy as np from lava.lib.optimization.problems.problems import OptimizationProblem, QUBO from lava.lib.optimization.solvers.generic.read_gate.models import \ - get_read_gate_model_class + get_read_gate_py_model_class from lava.lib.optimization.solvers.generic.read_gate.process import ReadGate from lava.lib.optimization.solvers.generic.solution_finder.process import ( SolutionFinder, @@ -24,7 +24,7 @@ from lava.magma.core.sync.protocols.loihi_protocol import LoihiProtocol from numpy import typing as npt -ReadGatePyModel = get_read_gate_model_class(1) +ReadGatePyModel = get_read_gate_py_model_class(1) class Mock: diff --git a/tests/lava/lib/optimization/solvers/generic/monitoring_processes/solution_readout/test_solution_readout.py b/tests/lava/lib/optimization/solvers/generic/monitoring_processes/solution_readout/test_solution_readout.py index 9599d535..5d129edc 100644 --- a/tests/lava/lib/optimization/solvers/generic/monitoring_processes/solution_readout/test_solution_readout.py +++ b/tests/lava/lib/optimization/solvers/generic/monitoring_processes/solution_readout/test_solution_readout.py @@ -10,7 +10,7 @@ .solution_readout.process import \ SolutionReadout from lava.lib.optimization.solvers.generic.read_gate.models import \ - get_read_gate_model_class + get_read_gate_py_model_class from lava.lib.optimization.solvers.generic.read_gate.process import ReadGate from lava.magma.core.run_conditions import RunContinuous from lava.magma.core.run_configs import Loihi2SimCfg @@ -34,7 +34,7 @@ def setUp(self) -> None: readgate.send_pause_request.connect(self.readout.timestep_in) # Execution configurations. - ReadGatePyModel = get_read_gate_model_class(1) + ReadGatePyModel = get_read_gate_py_model_class(1) pdict = {ReadGate: ReadGatePyModel, Spiker: SpikerModel} self.run_cfg = Loihi2SimCfg(exception_proc_model_map=pdict) self.readout._log_config.level = 20 diff --git a/tests/lava/lib/optimization/solvers/generic/solution_finder/test_solution_finder.py b/tests/lava/lib/optimization/solvers/generic/solution_finder/test_solution_finder.py index 39a64f43..ddd35463 100644 --- a/tests/lava/lib/optimization/solvers/generic/solution_finder/test_solution_finder.py +++ b/tests/lava/lib/optimization/solvers/generic/solution_finder/test_solution_finder.py @@ -6,7 +6,7 @@ import numpy as np from lava.lib.optimization.problems.problems import QUBO from lava.lib.optimization.solvers.generic.read_gate.models import \ - get_read_gate_model_class + get_read_gate_py_model_class from lava.lib.optimization.solvers.generic.read_gate.process import ReadGate from lava.lib.optimization.solvers.generic.solution_finder.process import ( SolutionFinder, @@ -47,7 +47,7 @@ def setUp(self) -> None: ) # Execution configurations. - ReadGatePyModel = get_read_gate_model_class(1) + ReadGatePyModel = get_read_gate_py_model_class(1) pdict = {ReadGate: ReadGatePyModel} self.run_cfg = Loihi2SimCfg(exception_proc_model_map=pdict) self.solution_finder._log_config.level = 20 diff --git a/tests/lava/lib/optimization/solvers/generic/solution_reader/test_solution_reader.py b/tests/lava/lib/optimization/solvers/generic/solution_reader/test_solution_reader.py index eac48d9c..26bd2ff5 100644 --- a/tests/lava/lib/optimization/solvers/generic/solution_reader/test_solution_reader.py +++ b/tests/lava/lib/optimization/solvers/generic/solution_reader/test_solution_reader.py @@ -8,7 +8,7 @@ SolutionReader, ) from lava.lib.optimization.solvers.generic.read_gate.models import \ - get_read_gate_model_class + get_read_gate_py_model_class from lava.lib.optimization.solvers.generic.read_gate.process import ReadGate from lava.magma.core.run_conditions import RunContinuous from lava.magma.core.run_configs import Loihi2SimCfg @@ -31,7 +31,7 @@ def setUp(self) -> None: self.solution_reader.ref_port.connect_var(spiker.payload) # Execution configurations. - ReadGatePyModel = get_read_gate_model_class(1) + ReadGatePyModel = get_read_gate_py_model_class(1) pdict = {ReadGate: ReadGatePyModel, Spiker: SpikerModel} self.run_cfg = Loihi2SimCfg(exception_proc_model_map=pdict) self.solution_reader._log_config.level = 20