Skip to content

Commit 5646027

Browse files
committed
v0.2.2 Wrapped LSHADE, some refactor
1 parent 8b358e9 commit 5646027

File tree

16 files changed

+566
-40
lines changed

16 files changed

+566
-40
lines changed

README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ maturin develop --release
4444
- maturin will build the Rust part, get all Python dependencies (for solver itself, not examples) and install greyjack to your venv
4545

4646
# RoadMap
47-
48-
- API for modelling pure math problems (classic solvers like)
49-
- Modern variations (modifications) of LSHADE (Differential evolution algorithms often appear in articles as sota approaches)
5047
- CMA, probably its modern variants, adaptations for tasks with integer and categorical variables (often appears in articles as sota approach)
5148
- Add more examples: Job-Shop, Pickup&Delivery, some continuous and MIP tasks, scheduling, etc
5249
- Composite termination criterion (for example: solving limit minutes N AND score not improving M seconds)

examples/pure_math/minlp/solve_minlp_gj.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,22 +17,35 @@
1717
from greyjack.agents.base.LoggingLevel import LoggingLevel
1818
from greyjack.agents.termination_strategies import *
1919

20-
from examples.pure_math.minlp.gj_files.rsyn0840m import *
20+
from examples.pure_math.minlp.gj_files.sssd import *
2121

2222
if __name__ == "__main__":
2323

24+
# for standard LP, QP problems fomulations better use classic solvers (they are specially made for them)
25+
# one needs metaheuristics, when classic solvers struggle, too difficult or just impossible to use:
26+
# (difficult functions, fast development, lack of expressiveness (explore OOP API of GreyJack Solver to
27+
# understand how to express problems by another, much more comfort and easy way), adaptability to new requirements, clarity of
28+
# processes during optimization, explainability, etc, etc)
29+
# here I use metaheuristics on standard LP, QP problems by using MathModel API just for benchmarks
2430
math_model = build_math_model()
2531

2632
#termination_strategy = StepsLimit(step_count_limit=1000)
2733
#termination_strategy = TimeSpentLimit(time_seconds_limit=60)
2834
termination_strategy = ScoreNoImprovement(time_seconds_limit=15)
2935
#termination_strategy = ScoreLimit(score_to_compare=[0, 0])
36+
37+
agent = LSHADE(population_size=128, history_archive_size=100,
38+
p_best_rate=0.2, tabu_entity_rate=0.2,
39+
mutation_rate_multiplier=1.0, move_probas=None,
40+
memory_pruning_rate=0.0, guarantee_of_change_size=0,
41+
initial_f=0.5, initial_cr=0.02, initial_mutation_proba=0.5,
42+
migration_rate=0.00001, migration_frequency=100, termination_strategy=termination_strategy)
43+
"""agent = GeneticAlgorithm(population_size=256, crossover_probability=0.8, p_best_rate=0.5,
44+
tabu_entity_rate=0.8, mutation_rate_multiplier=1.0, move_probas=[0.5, 0.5, 0.0, 0.0, 0.0, 0.0],
45+
migration_rate=0.00001, migration_frequency=10, termination_strategy=termination_strategy)"""
3046
"""agent = TabuSearch(neighbours_count=1000, tabu_entity_rate=0.8,
3147
mutation_rate_multiplier=1.0, move_probas=[0.5, 0.5, 0.0, 0.0, 0.0, 0.0],
3248
compare_to_global_frequency=10, termination_strategy=termination_strategy)"""
33-
agent = GeneticAlgorithm(population_size=256, crossover_probability=0.8, p_best_rate=0.5,
34-
tabu_entity_rate=0.8, mutation_rate_multiplier=1.0, move_probas=[0.5, 0.5, 0.0, 0.0, 0.0, 0.0],
35-
migration_rate=0.00001, migration_frequency=10, termination_strategy=termination_strategy)
3649
"""agent = LateAcceptance(late_acceptance_size=200, tabu_entity_rate=0.8,
3750
mutation_rate_multiplier=1.0, move_probas=[0.5, 0.5, 0.0, 0.0, 0.0, 0.0],
3851
compare_to_global_frequency=10000, termination_strategy=termination_strategy)"""
@@ -42,7 +55,7 @@
4255

4356
solver = SolverPureMath(math_model, agent,
4457
ParallelizationBackend.Multiprocessing, LoggingLevel.FreshOnly,
45-
n_jobs=10, score_precision=[6, 2])
58+
n_jobs=10, score_precision=[4, 2])
4659
solution = solver.solve()
4760
math_model.explain_solution( solution )
4861

examples/pure_math/minlp/solve_minlp_scip.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@
214214
sys.path.append(str(project_dir_path))
215215

216216
from pyomo.environ import *
217-
from examples.pure_math.minlp.minlp_files.rsyn0840m import *
217+
from examples.pure_math.minlp.minlp_files.sssd import *
218218

219219
solver = SolverFactory('scip', executable="D:\\Soft\\SCIPOptSuite 9.1.0\\bin\\scip.exe")
220220
solution = solver.solve(model)

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: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[package]
22
name = "greyjack"
3-
version = "0.2.1"
4-
edition = "2024"
3+
version = "0.2.2"
4+
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
77
[lib]
@@ -25,5 +25,5 @@ polars = { version = "0.46.0", features = ["lazy", "ndarray", "serde", "abs"] }
2525
[profile.release]
2626
#lto = true
2727
#codegen-units = 1
28-
debug = true
29-
opt-level = 3
28+
#debug = true
29+
#opt-level = 3

greyjack/README.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,6 @@ maturin develop --release
4444
- maturin will build the Rust part, get all Python dependencies (for solver itself, not examples) and install greyjack to your venv
4545

4646
# RoadMap
47-
48-
- API for modelling pure math problems (classic solvers like)
49-
- Modern variations (modifications) of LSHADE (Differential evolution algorithms often appear in articles as sota approaches)
5047
- CMA, probably its modern variants, adaptations for tasks with integer and categorical variables (often appears in articles as sota approach)
5148
- Add more examples: Job-Shop, Pickup&Delivery, some continuous and MIP tasks, scheduling, etc
5249
- Composite termination criterion (for example: solving limit minutes N AND score not improving M seconds)

greyjack/greyjack/agents/LSHADE.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
2+
from greyjack.agents.base.Agent import Agent
3+
from greyjack.agents.metaheuristic_bases.LSHADEBase import LSHADEBase
4+
from greyjack.score_calculation.score_requesters.OOPScoreRequester import OOPScoreRequester
5+
from greyjack.score_calculation.score_requesters.PureMathScoreRequester import PureMathScoreRequester
6+
from greyjack.cotwin.CotwinBase import CotwinBase
7+
from greyjack.pure_math.MathModel import MathModel
8+
9+
class LSHADE(Agent):
10+
11+
"""
12+
Classic Tanabe-Fukunaga version of LSHADE (https://metahack.org/CEC2014-Tanabe-Fukunaga.pdf)
13+
with my own modifications to make it better work in common (not only for continuous tasks, but also for MIP).
14+
For pure integer tasks works much badder than Tabu, GenAlg, LateAcc. Don't use LSHADE for purely integer tasks.
15+
Later will be modified further. From 2014 there were a lot of modifications invented by researchers.
16+
17+
WARNING! Don't use this metaheuristic with already initialized values (it will stuck due to sampling from history mechanism)!
18+
"""
19+
20+
def __init__(
21+
self, population_size=128, history_archive_size=100,
22+
p_best_rate=0.2, tabu_entity_rate=0.2,
23+
mutation_rate_multiplier=1.0, move_probas=None,
24+
memory_pruning_rate=0.0, guarantee_of_change_size=1.0,
25+
initial_f=0.5, initial_cr=0.02, initial_mutation_proba=0.5,
26+
migration_rate=0.00001, migration_frequency=10, termination_strategy=None
27+
):
28+
29+
super().__init__(migration_rate, migration_frequency, termination_strategy, compare_to_global_frequency=1)
30+
31+
self.population_size = population_size
32+
self.history_archive_size = history_archive_size
33+
self.p_best_rate = p_best_rate
34+
self.memory_pruning_rate = memory_pruning_rate
35+
self.guarantee_of_change_size = guarantee_of_change_size
36+
self.initial_f = initial_f
37+
self.initial_cr = initial_cr
38+
self.initial_mutation_proba = initial_mutation_proba
39+
40+
self.tabu_entity_rate = tabu_entity_rate
41+
self.mutation_rate_multiplier = mutation_rate_multiplier
42+
self.move_probas = move_probas
43+
44+
self.is_win_from_comparing_with_global = False
45+
46+
def _build_metaheuristic_base(self):
47+
48+
# when I use issubclass() solver dies silently, so check specific attributes
49+
if hasattr(self.cotwin, "planning_entities"):
50+
self.score_requester = OOPScoreRequester(self.cotwin)
51+
score_variant = self.cotwin.score_calculator.score_variant
52+
elif isinstance(self.cotwin, MathModel):
53+
self.score_requester = PureMathScoreRequester(self.cotwin)
54+
score_variant = self.cotwin.score_variant
55+
self.cotwin.score_calculator.is_incremental = False
56+
else:
57+
raise Exception("Cotwin must be either subclass of CotwinBase, either be instance of MathModel")
58+
59+
semantic_groups_dict = self.score_requester.variables_manager.semantic_groups_map.copy()
60+
discrete_ids = self.score_requester.variables_manager.discrete_ids
61+
62+
self.metaheuristic_base = LSHADEBase.new(
63+
score_variant,
64+
self.score_requester.variables_manager,
65+
66+
self.population_size,
67+
self.history_archive_size,
68+
self.p_best_rate,
69+
self.memory_pruning_rate,
70+
self.guarantee_of_change_size,
71+
self.initial_f,
72+
self.initial_cr,
73+
self.initial_mutation_proba,
74+
75+
self.tabu_entity_rate,
76+
semantic_groups_dict,
77+
self.mutation_rate_multiplier,
78+
self.move_probas.copy() if self.move_probas else None,
79+
discrete_ids,
80+
)
81+
82+
# to remove redundant clonning
83+
self.metaheuristic_name = self.metaheuristic_base.metaheuristic_name
84+
self.metaheuristic_kind = self.metaheuristic_base.metaheuristic_kind
85+
86+
return self

greyjack/greyjack/agents/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
from greyjack.agents.TabuSearch import TabuSearch
44
from greyjack.agents.GeneticAlgorithm import GeneticAlgorithm
55
from greyjack.agents.LateAcceptance import LateAcceptance
6-
from greyjack.agents.SimulatedAnnealing import SimulatedAnnealing
6+
from greyjack.agents.SimulatedAnnealing import SimulatedAnnealing
7+
from greyjack.agents.LSHADE import LSHADE

greyjack/greyjack/agents/base/Agent.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -297,13 +297,13 @@ def _send_updates_universal(self):
297297
"round_robin_status_dict": self.round_robin_status_dict,
298298
"request_type": "put_updates",
299299
"migrants": migrants}
300-
if self.metaheuristic_base.metaheuristic_name == "LSHADE":
300+
"""if self.metaheuristic_base.metaheuristic_name == "LSHADE":
301301
if len(self.history_archive) > 0:
302302
rand_id = random.randint(0, len(self.history_archive) - 1)
303303
request["history_archive"] = self.history_archive[rand_id].as_list()
304304
#request["history_archive"] = self.history_archive[-1]
305305
else:
306-
request["history_archive"] = None
306+
request["history_archive"] = None"""
307307

308308
request_serialized = pickle.dumps(request)
309309
try:
@@ -338,14 +338,14 @@ def _get_updates_universal(self):
338338
self.agent_to_agent_socket_receiver.send(pickle.dumps("Successfully received updates"))
339339
updates_reply = pickle.loads( updates_reply )
340340

341-
if self.metaheuristic_base.metaheuristic_name == "LSHADE":
341+
"""if self.metaheuristic_base.metaheuristic_name == "LSHADE":
342342
history_migrant = updates_reply["history_archive"]
343343
if (history_migrant is not None and len(self.history_archive) > 0):
344344
history_migrant = self.individual_type.from_list(history_migrant)
345345
rand_id = random.randint(0, len(self.history_archive) - 1)
346346
#if updates_reply["history_archive"] < self.history_archive[-1]:
347347
if history_migrant < self.history_archive[rand_id]:
348-
self.history_archive[rand_id] = history_migrant
348+
self.history_archive[rand_id] = history_migrant"""
349349

350350
migrants = updates_reply["migrants"]
351351
migrants = self.individual_type.convert_lists_to_individuals(migrants)
@@ -398,13 +398,13 @@ def _send_updates_linux(self):
398398
"round_robin_status_dict": self.round_robin_status_dict,
399399
"request_type": "put_updates",
400400
"migrants": migrants}
401-
if self.metaheuristic_base.metaheuristic_name == "LSHADE":
401+
"""if self.metaheuristic_base.metaheuristic_name == "LSHADE":
402402
if len(self.history_archive) > 0:
403403
rand_id = random.randint(0, len(self.history_archive) - 1)
404404
request["history_archive"] = self.history_archive[rand_id].as_list()
405405
#request["history_archive"] = self.history_archive[-1]
406406
else:
407-
request["history_archive"] = None
407+
request["history_archive"] = None"""
408408

409409
try:
410410
self.agent_to_agent_pipe_sender.send( request )
@@ -426,14 +426,14 @@ def _get_updates_linux(self):
426426
return
427427
self.agent_to_agent_pipe_receiver.send("Successfully received updates")
428428

429-
if self.metaheuristic_base.metaheuristic_name == "LSHADE":
429+
"""if self.metaheuristic_base.metaheuristic_name == "LSHADE":
430430
history_migrant = updates_reply["history_archive"]
431431
if (history_migrant is not None and len(self.history_archive) > 0):
432432
history_migrant = self.individual_type.from_list(history_migrant)
433433
rand_id = random.randint(0, len(self.history_archive) - 1)
434434
#if updates_reply["history_archive"] < self.history_archive[-1]:
435435
if history_migrant < self.history_archive[rand_id]:
436-
self.history_archive[rand_id] = history_migrant
436+
self.history_archive[rand_id] = history_migrant"""
437437

438438
migrants = updates_reply["migrants"]
439439
migrants = self.individual_type.convert_lists_to_individuals(migrants)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
2+
from greyjack.greyjack import LSHADESimple, LSHADEHardSoft, LSHADEHardMediumSoft
3+
from greyjack.score_calculation.scores.ScoreVariants import ScoreVariants
4+
5+
class LSHADEBase:
6+
def new(score_variant, variables_manager_py, population_size, history_archive_size, p_best_rate, memory_pruning_rate, guarantee_of_change_size,
7+
initial_f, initial_cr, initial_mutation_proba, tabu_entity_rate, semantic_groups_dict,
8+
mutation_rate_multiplier, move_probas, discrete_ids):
9+
if score_variant == ScoreVariants.SimpleScore:
10+
return LSHADESimple(variables_manager_py, population_size, history_archive_size, p_best_rate, memory_pruning_rate, guarantee_of_change_size,
11+
initial_f, initial_cr, initial_mutation_proba, tabu_entity_rate, semantic_groups_dict,
12+
mutation_rate_multiplier, move_probas, discrete_ids)
13+
if score_variant == ScoreVariants.HardSoftScore:
14+
return LSHADEHardSoft(variables_manager_py, population_size, history_archive_size, p_best_rate, memory_pruning_rate, guarantee_of_change_size,
15+
initial_f, initial_cr, initial_mutation_proba, tabu_entity_rate, semantic_groups_dict,
16+
mutation_rate_multiplier, move_probas, discrete_ids)
17+
if score_variant == ScoreVariants.HardMediumSoftScore:
18+
return LSHADEHardMediumSoft(variables_manager_py, population_size, history_archive_size, p_best_rate, memory_pruning_rate, guarantee_of_change_size,
19+
initial_f, initial_cr, initial_mutation_proba, tabu_entity_rate, semantic_groups_dict,
20+
mutation_rate_multiplier, move_probas, discrete_ids)
21+
22+
raise Exception("score_variant unrecognized")

greyjack/greyjack/pure_math/MathModel.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ def get_individual_hard_scores(self, absolute):
3535

3636
def get_sum_hard_score(self, absolute):
3737

38+
#print()
39+
#pprint(self.variables)
40+
#print()
41+
3842
try:
3943
individual_hard_scores = self.get_individual_hard_scores(absolute)
4044
sum_hard_score = 0.0

greyjack/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ features = ["pyo3/extension-module"]
77

88
[project]
99
name = "greyjack"
10-
version = "0.2.1"
10+
version = "0.2.2"
1111
requires-python = ">=3.9"
1212
dependencies = [
1313
"dill",

0 commit comments

Comments
 (0)