Skip to content

Commit 8cf4354

Browse files
committed
v0.1.2 Simulated Annealing
1 parent 3edc592 commit 8cf4354

File tree

20 files changed

+377
-114
lines changed

20 files changed

+377
-114
lines changed

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ There are 2 editions of GreyJack Solver:
1414
- Python edition
1515
- [Rust edition](https://github.com/CameleoGrey/greyjack-solver-rust)
1616

17-
# Key Features of GreyJack Solver (Rust version)
17+
# Key Features of GreyJack Solver
1818

1919
- **Unmatched Comfort, Expressiveness, Flexibility and speed of developing** Designed to express almost any optimization problem with maximum comfortability and clarity.
2020
- **Universality** Supports a wide range of constraint problems, including continuous, integer, and mixed-integer challenges. Additionally, thanks to Polars, you can optimize virtually any process that can be represented as table data.
@@ -24,7 +24,7 @@ There are 2 editions of GreyJack Solver:
2424
- **Support for Population and Local Search Algorithms** GreyJack Solver supports a wide range of metaheuristics, including population-based and local search algorithms, with highly flexible settings. You can easily find, select, and configure the approach that best fits your problem, delivering optimal results.
2525
- **Easy Integration** The observer mechanism (see examples) simplifies integration, making it straightforward to incorporate GreyJack Solver into your existing workflows..
2626

27-
# Get started with GreyJack Solver in Rust
27+
# Get started with GreyJack Solver
2828

2929
```
3030
pip install greyjack
@@ -45,7 +45,6 @@ maturin develop --release
4545

4646
# RoadMap
4747

48-
- SimulatedAnnealing
4948
- API for modelling pure math problems (classic solvers like)
5049
- Modern variations (modifications) of LSHADE (Differential evolution algorithms often appear in articles as sota approaches)
5150
- CMA, probably its modern variants, adaptations for tasks with integer and categorical variables (often appears in articles as sota approach)

examples/object_oriented/nqueens/score/PlainScoreCalculatorNQueens.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def all_different(self, planning_entity_dfs, problem_fact_dfs):
3939
)
4040
.with_columns((pl.col("row_conflicts_count") + pl.col("desc_conflicts_count") + pl.col("asc_conflicts_count")).alias("sum_conflicts"))
4141
.sort("sample_id")
42-
.collect()
42+
.collect() # you can use engine="gpu" on Linux or by WSL
4343
)
4444

4545
scores = same_row_id_counts["sum_conflicts"].to_list()

examples/object_oriented/nqueens/scripts/solve_nqueens.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,33 @@
1515
from greyjack.Solver import Solver
1616
from greyjack.agents.base.LoggingLevel import LoggingLevel
1717
from greyjack.agents.base.ParallelizationBackend import ParallelizationBackend
18-
from greyjack.agents.TabuSearch import TabuSearch
19-
from greyjack.agents.GeneticAlgorithm import GeneticAlgorithm
20-
from greyjack.agents.LateAcceptance import LateAcceptance
18+
from greyjack.agents import *
2119

2220
if __name__ == "__main__":
2321

2422
# build domain model
25-
domain_builder = DomainBuilderNQueens(1024, random_seed=45)
23+
domain_builder = DomainBuilderNQueens(10000, random_seed=45)
2624
cotwin_builder = CotwinBuilderNQueens(use_incremental_score_calculator=True)
2725

2826
#termination_strategy = StepsLimit(step_count_limit=1000)
29-
#termination_strategy = TimeSpentLimit(time_seconds_limit=60)
27+
termination_strategy = TimeSpentLimit(time_seconds_limit=60)
3028
#termination_strategy = ScoreNoImprovement(time_seconds_limit=15)
31-
termination_strategy = ScoreLimit(score_to_compare=[0])
29+
#termination_strategy = ScoreLimit(score_to_compare=[0])
3230
agent = TabuSearch(neighbours_count=20, tabu_entity_rate=0.0,
3331
mutation_rate_multiplier=None, move_probas=[0, 1, 0, 0, 0, 0],
3432
compare_to_global=True, migration_frequency=10, termination_strategy=termination_strategy)
3533
"""agent = GeneticAlgorithm(population_size=128, crossover_probability=0.5, p_best_rate=0.05,
3634
tabu_entity_rate=0.0, mutation_rate_multiplier=1.0, move_probas=[0, 1, 0, 0, 0, 0],
3735
migration_rate=0.00001, migration_frequency=1, termination_strategy=termination_strategy)"""
38-
"""agent = LateAcceptance(late_acceptance_size=1, tabu_entity_rate=0.0,
36+
"""agent = LateAcceptance(late_acceptance_size=10, tabu_entity_rate=0.0,
3937
mutation_rate_multiplier=None, move_probas=[0, 1, 0, 0, 0, 0],
4038
termination_strategy=termination_strategy)"""
39+
"""agent = SimulatedAnnealing(initial_temperature=[1.0], cooling_rate=0.9999, tabu_entity_rate=0.0,
40+
mutation_rate_multiplier=None, move_probas=[0, 1, 0, 0, 0, 0],
41+
compare_to_global=True, migration_frequency=10, termination_strategy=termination_strategy)"""
4142

4243
solver = Solver(domain_builder, cotwin_builder, agent,
43-
ParallelizationBackend.Multiprocessing, LoggingLevel.Info,
44+
ParallelizationBackend.Multiprocessing, LoggingLevel.FreshOnly,
4445
n_jobs=10, score_precision=[0])
4546
solution = solver.solve()
4647
#print( "Cotwin solution looks that: " )

examples/object_oriented/tsp/score/PlainScoreCalculatorTSP.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from greyjack.score_calculation.scores.ScoreVariants import ScoreVariants
66
import polars as pl
77
from numba import jit
8+
#from numba import cuda # there is possibility to write some constraints on gpu outside Polars engine
89

910
class PlainScoreCalculatorTSP( PlainScoreCalculator ):
1011

@@ -27,12 +28,13 @@ def no_duplicating_stops_constraint(self, planning_entity_dfs, problem_fact_dfs)
2728

2829
duplicate_counts = (
2930
path_stops_df
31+
.lazy()
3032
.group_by("sample_id")
3133
.agg((pl.col("location_list_id").count() - pl.col("location_list_id").n_unique()).alias("duplicates_count"))
3234
.group_by("sample_id")
3335
.agg(pl.col("duplicates_count").sum())
3436
.sort("sample_id")
35-
37+
.collect() # you can use engine="gpu" on Linux or by WSL
3638
)
3739

3840
scores = duplicate_counts["duplicates_count"].to_list()
@@ -100,6 +102,7 @@ def minimize_distance(self, planning_entity_dfs, problem_fact_dfs):
100102
# instead rewriting on Rust (to not increase complexity of project and still take huge performance boost)
101103
@staticmethod
102104
@jit(nopython=True, cache=True)
105+
#@cuda.jit()
103106
def compute_path_distances(data_matrix, distance_matrix):
104107

105108
unique_sample_ids = np.unique(data_matrix[:, 0])

examples/object_oriented/tsp/scripts/solve_tsp.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@
3737
cotwin_builder = CotwinBuilder(use_incremental_score_calculator=True)
3838

3939
#termination_strategy = StepsLimit(step_count_limit=1000)
40-
#termination_strategy = TimeSpentLimit(time_seconds_limit=60)
41-
termination_strategy = ScoreNoImprovement(time_seconds_limit=15)
40+
termination_strategy = TimeSpentLimit(time_seconds_limit=60)
41+
#termination_strategy = ScoreNoImprovement(time_seconds_limit=15)
4242
#termination_strategy = ScoreLimit(score_to_compare=[0])
43-
agent = TabuSearch(neighbours_count=128, tabu_entity_rate=0.2,
43+
agent = TabuSearch(neighbours_count=128, tabu_entity_rate=0.5,
4444
mutation_rate_multiplier=None, move_probas=[0.0, 0.2, 0.2, 0.2, 0.2, 0.2],
4545
compare_to_global=False, migration_frequency=10, termination_strategy=termination_strategy)
4646
"""agent = GeneticAlgorithm(population_size=128, crossover_probability=0.5, p_best_rate=0.05,
@@ -49,9 +49,12 @@
4949
"""agent = LateAcceptance(late_acceptance_size=64, tabu_entity_rate=0.2,
5050
mutation_rate_multiplier=None, move_probas=[0.0, 0.2, 0.2, 0.2, 0.2, 0.2],
5151
termination_strategy=termination_strategy)"""
52+
"""agent = SimulatedAnnealing(initial_temperature=[1.0, 1.0], cooling_rate=0.9999, tabu_entity_rate=0.2,
53+
mutation_rate_multiplier=None, move_probas=[0, 0.2, 0.2, 0.2, 0.2, 0.2],
54+
compare_to_global=True, migration_frequency=10, termination_strategy=termination_strategy)"""
5255

5356
solver = Solver(domain_builder, cotwin_builder, agent,
54-
ParallelizationBackend.Multiprocessing, LoggingLevel.FreshOnly,
57+
ParallelizationBackend.Multiprocessing, LoggingLevel.Info,
5558
n_jobs=10, score_precision=[0, 0])
5659
solution = solver.solve()
5760

examples/object_oriented/vrp/score/PlainScoreCalculatorVRP.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from greyjack.score_calculation.scores.ScoreVariants import ScoreVariants
66
import polars as pl
77
from numba import jit
8+
#from numba import cuda # there is possibility to write some constraints on gpu outside Polars engine
89

910
class PlainScoreCalculatorVRP(PlainScoreCalculator):
1011

@@ -30,10 +31,13 @@ def build_common_df(self, planning_entity_dfs, problem_fact_dfs):
3031

3132
customers_df = customers_df.rename({"customer_vec_id": "customer_id"})
3233
common_df = (planning_stops_df
33-
.with_row_index("index", offset=None)
34-
.join(vehicle_df, on="vehicle_id", how="inner")
35-
.join(customers_df, on="customer_id", how="inner")
36-
.sort(["sample_id", "vehicle_id", "index"], descending=[False, False, False]))
34+
.lazy()
35+
.with_row_index("index", offset=None)
36+
.join(vehicle_df.lazy(), on="vehicle_id", how="inner")
37+
.join(customers_df.lazy(), on="customer_id", how="inner")
38+
.sort(["sample_id", "vehicle_id", "index"], descending=[False, False, False])
39+
.collect() # you can use engine="gpu" on Linux or by WSL
40+
)
3741

3842
self.utility_objects["common_df"] = common_df
3943

@@ -45,11 +49,13 @@ def no_duplicating_stops_constraint(self, planning_entity_dfs, problem_fact_dfs)
4549

4650
duplicate_counts = (
4751
path_stops_df
52+
.lazy()
4853
.group_by("sample_id")
4954
.agg((pl.col("customer_id").count() - pl.col("customer_id").n_unique()).alias("duplicates_count"))
5055
.group_by("sample_id")
5156
.agg(pl.col("duplicates_count").sum())
5257
.sort("sample_id")
58+
.collect() # you can use engine="gpu" on Linux or by WSL
5359
)
5460

5561
scores = duplicate_counts["duplicates_count"].to_numpy()
@@ -64,13 +70,15 @@ def capacity_constraint(self, planning_entity_dfs, problem_fact_dfs):
6470

6571
capacity_penalties = (
6672
common_df
73+
.lazy()
6774
.group_by(["sample_id", "vehicle_id"])
6875
.agg(pl.col("demand").sum().alias("sum_trip_demand"))
69-
.join(vehicle_df, on="vehicle_id", how="inner")
76+
.join(vehicle_df.lazy(), on="vehicle_id", how="inner")
7077
.with_columns((pl.col("capacity") - pl.col("sum_trip_demand")).alias("capacity_difference"))
7178
.filter(pl.col("capacity_difference") < 0)
7279
.group_by("sample_id")
7380
.agg(pl.col("capacity_difference").abs().sum().alias("capacity_constraint_penalty"))
81+
.collect() # you can use engine="gpu" on Linux or by WSL
7482
)
7583

7684
bad_sample_ids = capacity_penalties["sample_id"].to_list()

examples/object_oriented/vrp/scripts/solve_vrp.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,21 @@
4141
cotwin_builder = CotwinBuilder(use_incremental_score_calculator=True, use_greed_init=True)
4242

4343
#termination_strategy = StepsLimit(step_count_limit=1000)
44-
#termination_strategy = TimeSpentLimit(time_seconds_limit=60)
45-
termination_strategy = ScoreNoImprovement(time_seconds_limit=15)
44+
termination_strategy = TimeSpentLimit(time_seconds_limit=60)
45+
#termination_strategy = ScoreNoImprovement(time_seconds_limit=15)
4646
#termination_strategy = ScoreLimit(score_to_compare=[0])
47-
agent = TabuSearch(neighbours_count=128, tabu_entity_rate=0.0,
47+
agent = TabuSearch(neighbours_count=128, tabu_entity_rate=0.8,
4848
mutation_rate_multiplier=None, move_probas=[0.5, 0.5, 0.0, 0.0, 0.0, 0.0],
49-
compare_to_global=False, migration_frequency=10, termination_strategy=termination_strategy)
49+
compare_to_global=True, migration_frequency=10, termination_strategy=termination_strategy)
5050
"""agent = GeneticAlgorithm(population_size=512, crossover_probability=0.5, p_best_rate=0.2,
5151
tabu_entity_rate=0.0, mutation_rate_multiplier=None, move_probas=[0.5, 0.5, 0.0, 0.0, 0.0, 0.0],
5252
migration_rate=0.00001, migration_frequency=10, termination_strategy=termination_strategy)"""
53-
"""agent = LateAcceptance(late_acceptance_size=64, tabu_entity_rate=0.05,
53+
"""agent = LateAcceptance(late_acceptance_size=32, tabu_entity_rate=0.8,
5454
mutation_rate_multiplier=None, move_probas=[0.5, 0.5, 0.0, 0.0, 0.0, 0.0],
5555
termination_strategy=termination_strategy)"""
56+
"""agent = SimulatedAnnealing(initial_temperature=[1.0, 1.0, 1.0], cooling_rate=0.9999, tabu_entity_rate=0.0,
57+
mutation_rate_multiplier=None, move_probas=[0.5, 0.5, 0.0, 0.0, 0.0, 0.0],
58+
compare_to_global=True, migration_frequency=10, termination_strategy=termination_strategy)"""
5659

5760
solver = Solver(domain_builder, cotwin_builder, agent,
5861
ParallelizationBackend.Multiprocessing, LoggingLevel.FreshOnly,

greyjack/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

greyjack/Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "greyjack"
3-
version = "0.1.1"
4-
edition = "2021"
3+
version = "0.1.2"
4+
edition = "2024"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77
[lib]
@@ -22,8 +22,8 @@ polars = { version = "0.46.0", features = ["lazy", "ndarray", "serde", "abs"] }
2222
# if you build lib from source code
2323
# uncomment to gain max performance (increases calculation speed about 5-10%,
2424
# but also increases build time ~20x times)
25-
[profile.release]
26-
lto = true
27-
codegen-units = 1
28-
debug = true
29-
opt-level = 3
25+
#[profile.release]
26+
#lto = true
27+
#codegen-units = 1
28+
#debug = true
29+
#opt-level = 3

greyjack/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ There are 2 editions of GreyJack Solver:
1414
- Python edition
1515
- [Rust edition](https://github.com/CameleoGrey/greyjack-solver-rust)
1616

17-
# Key Features of GreyJack Solver (Rust version)
17+
# Key Features of GreyJack Solver
1818

1919
- **Unmatched Comfort, Expressiveness, Flexibility and speed of developing** Designed to express almost any optimization problem with maximum comfortability and clarity.
2020
- **Universality** Supports a wide range of constraint problems, including continuous, integer, and mixed-integer challenges. Additionally, thanks to Polars, you can optimize virtually any process that can be represented as table data.
@@ -24,7 +24,7 @@ There are 2 editions of GreyJack Solver:
2424
- **Support for Population and Local Search Algorithms** GreyJack Solver supports a wide range of metaheuristics, including population-based and local search algorithms, with highly flexible settings. You can easily find, select, and configure the approach that best fits your problem, delivering optimal results.
2525
- **Easy Integration** The observer mechanism (see examples) simplifies integration, making it straightforward to incorporate GreyJack Solver into your existing workflows..
2626

27-
# Get started with GreyJack Solver in Rust
27+
# Get started with GreyJack Solver
2828

2929
```
3030
pip install greyjack
@@ -45,7 +45,6 @@ maturin develop --release
4545

4646
# RoadMap
4747

48-
- SimulatedAnnealing
4948
- API for modelling pure math problems (classic solvers like)
5049
- Modern variations (modifications) of LSHADE (Differential evolution algorithms often appear in articles as sota approaches)
5150
- CMA, probably its modern variants, adaptations for tasks with integer and categorical variables (often appears in articles as sota approach)

greyjack/greyjack/Solver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def solve(self):
165165
steps_count += 1
166166
new_best_string = "New best score!" if new_best_flag else ""
167167
if self.logging_level == LoggingLevel.FreshOnly and new_best_flag:
168-
self.logger.info(f"Solutions received: {steps_count} Best score: {self.global_top_individual.score}, Solving time: {total_time:.6f} {new_best_string}")
168+
self.logger.info(f"Agent: {agent_id:4} Step {local_step} Best score: {self.global_top_individual.score}, Solving time: {total_time:.6f} {new_best_string}")
169169

170170
if len(self.observers) >= 1:
171171
self._notify_observers(self.global_top_individual)
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
2+
from greyjack.agents.base.Agent import Agent
3+
from greyjack.agents.metaheuristic_bases.SimulatedAnnealingBase import SimulatedAnnealingBase
4+
from greyjack.score_calculation.score_requesters.OOPScoreRequester import OOPScoreRequester
5+
6+
7+
class SimulatedAnnealing(Agent):
8+
def __init__(
9+
self,
10+
initial_temperature,
11+
cooling_rate,
12+
tabu_entity_rate,
13+
mutation_rate_multiplier=None,
14+
move_probas=None,
15+
compare_to_global=False,
16+
migration_frequency=None,
17+
termination_strategy=None,
18+
):
19+
20+
super().__init__(1.0, migration_frequency, termination_strategy)
21+
22+
self.population_size = 1
23+
self.initial_temperature = initial_temperature
24+
self.cooling_rate = cooling_rate
25+
self.tabu_entity_rate = tabu_entity_rate
26+
self.mutation_rate_multiplier = mutation_rate_multiplier
27+
self.move_probas = move_probas
28+
29+
# If true - stucks more often in local minimums, but converges much faster
30+
# may be useful in multiple stages solving
31+
self.is_win_from_comparing_with_global = compare_to_global
32+
33+
def _build_metaheuristic_base(self):
34+
self.score_requester = OOPScoreRequester(self.cotwin)
35+
semantic_groups_dict = self.score_requester.variables_manager.semantic_groups_map.copy()
36+
discrete_ids = self.score_requester.variables_manager.discrete_ids.copy()
37+
38+
"""
39+
new(score_variant, variables_manager_py,
40+
initial_temperature, tabu_entity_rate,
41+
semantic_groups_dict, cooling_rate=None,
42+
mutation_rate_multiplier=None,
43+
move_probas=None, discrete_ids=None):
44+
"""
45+
46+
self.metaheuristic_base = SimulatedAnnealingBase.new(
47+
self.cotwin.score_calculator.score_variant,
48+
self.score_requester.variables_manager,
49+
self.initial_temperature,
50+
self.tabu_entity_rate,
51+
semantic_groups_dict,
52+
self.cooling_rate,
53+
self.mutation_rate_multiplier,
54+
self.move_probas.copy() if self.move_probas else None,
55+
discrete_ids,
56+
)
57+
58+
# to remove redundant clonning
59+
self.metaheuristic_name = self.metaheuristic_base.metaheuristic_name
60+
self.metaheuristic_kind = self.metaheuristic_base.metaheuristic_kind
61+
62+
return self

greyjack/greyjack/agents/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22

33
from greyjack.agents.TabuSearch import TabuSearch
44
from greyjack.agents.GeneticAlgorithm import GeneticAlgorithm
5-
from greyjack.agents.LateAcceptance import LateAcceptance
5+
from greyjack.agents.LateAcceptance import LateAcceptance
6+
from greyjack.agents.SimulatedAnnealing import SimulatedAnnealing

greyjack/greyjack/agents/base/Agent.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ def _build_cotwin(self):
117117

118118
def _define_individual_type(self):
119119
self.score_variant = self.cotwin.score_calculator.score_variant
120-
self.individual_type = Individual.get_related_individual_type(self.cotwin.score_calculator.score_variant)
120+
self.individual_type = Individual.get_related_individual_type(self.score_variant)
121121

122122
# implements by concrete metaheuristics
123123
def _build_metaheuristic_base(self):

0 commit comments

Comments
 (0)