Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
ITMO-NSS-team
GOLEM
Commits
fe385008
Commit
fe385008
authored
1 year ago
by
Sergey Kasyanov
Browse files
Options
Download
Email Patches
Plain Diff
wip
parent
f68a0396
parallel-population-processing
No related merge requests found
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
golem/core/optimisers/genetic/gp_optimizer_new.py
+116
-0
golem/core/optimisers/genetic/gp_optimizer_new.py
golem/core/optimisers/genetic/pool.py
+137
-0
golem/core/optimisers/genetic/pool.py
with
253 additions
and
0 deletions
+253
-0
golem/core/optimisers/genetic/gp_optimizer_new.py
0 → 100644
View file @
fe385008
from
typing
import
Sequence
,
Union
,
Any
from
golem.core.dag.graph
import
Graph
from
golem.core.optimisers.genetic.gp_params
import
GPAlgorithmParameters
from
golem.core.optimisers.genetic.operators.crossover
import
Crossover
,
SinglePredefinedGraphCrossover
from
golem.core.optimisers.genetic.operators.elitism
import
Elitism
from
golem.core.optimisers.genetic.operators.inheritance
import
Inheritance
from
golem.core.optimisers.genetic.operators.mutation
import
Mutation
,
SinglePredefinedGraphMutation
from
golem.core.optimisers.genetic.operators.operator
import
PopulationT
,
EvaluationOperator
from
golem.core.optimisers.genetic.operators.regularization
import
Regularization
from
golem.core.optimisers.genetic.operators.reproduction
import
ReproductionController
from
golem.core.optimisers.genetic.operators.selection
import
Selection
from
golem.core.optimisers.genetic.parameters.graph_depth
import
AdaptiveGraphDepth
from
golem.core.optimisers.genetic.parameters.operators_prob
import
init_adaptive_operators_prob
from
golem.core.optimisers.genetic.parameters.population_size
import
init_adaptive_pop_size
,
PopulationSize
from
golem.core.optimisers.objective.objective
import
Objective
from
golem.core.optimisers.opt_history_objects.individual
import
Individual
from
golem.core.optimisers.optimization_parameters
import
GraphRequirements
from
golem.core.optimisers.optimizer
import
GraphGenerationParams
from
golem.core.optimisers.populational_optimizer
import
PopulationalOptimizer
class
EvoGraphOptimizer
(
PopulationalOptimizer
):
"""
Multi-objective evolutionary graph optimizer named GPComp
"""
def
__init__
(
self
,
objective
:
Objective
,
initial_graphs
:
Sequence
[
Union
[
Graph
,
Any
]],
requirements
:
GraphRequirements
,
graph_generation_params
:
GraphGenerationParams
,
graph_optimizer_params
:
GPAlgorithmParameters
):
super
().
__init__
(
objective
,
initial_graphs
,
requirements
,
graph_generation_params
,
graph_optimizer_params
)
# Define genetic operators
self
.
regularization
=
Regularization
(
graph_optimizer_params
,
graph_generation_params
)
self
.
selection
=
Selection
(
graph_optimizer_params
)
self
.
crossover
=
SinglePredefinedGraphCrossover
(
graph_optimizer_params
,
requirements
,
graph_generation_params
)
self
.
mutation
=
SinglePredefinedGraphMutation
(
graph_optimizer_params
,
requirements
,
graph_generation_params
)
self
.
inheritance
=
Inheritance
(
graph_optimizer_params
,
self
.
selection
)
self
.
elitism
=
Elitism
(
graph_optimizer_params
)
self
.
operators
=
[
self
.
regularization
,
self
.
selection
,
self
.
crossover
,
self
.
mutation
,
self
.
inheritance
,
self
.
elitism
]
self
.
reproducer
=
ReproductionController
(
parameters
=
graph_optimizer_params
,
selection
=
self
.
selection
,
mutation
=
self
.
mutation
,
crossover
=
self
.
crossover
,
verifier
=
self
.
graph_generation_params
.
verifier
)
# Define adaptive parameters
self
.
_pop_size
:
PopulationSize
=
init_adaptive_pop_size
(
graph_optimizer_params
,
self
.
generations
)
self
.
_operators_prob
=
init_adaptive_operators_prob
(
graph_optimizer_params
)
self
.
_graph_depth
=
AdaptiveGraphDepth
(
self
.
generations
,
start_depth
=
requirements
.
start_depth
,
max_depth
=
requirements
.
max_depth
,
max_stagnation_gens
=
graph_optimizer_params
.
adaptive_depth_max_stagnation
,
adaptive
=
graph_optimizer_params
.
adaptive_depth
)
# Define initial parameters
self
.
requirements
.
max_depth
=
self
.
_graph_depth
.
initial
self
.
graph_optimizer_params
.
pop_size
=
self
.
_pop_size
.
initial
self
.
initial_individuals
=
[
Individual
(
graph
,
metadata
=
requirements
.
static_individual_metadata
)
for
graph
in
self
.
initial_graphs
]
def
_initial_population
(
self
,
evaluator
:
EvaluationOperator
):
""" Initializes the initial population """
# Adding of initial assumptions to history as zero generation
self
.
_update_population
(
evaluator
(
self
.
initial_individuals
),
'initial_assumptions'
)
# pop_size = self.graph_optimizer_params.pop_size
#
# if len(self.initial_individuals) < pop_size:
# self.initial_individuals += self.reproducer._reproduce(population=self.initial_individuals,
# evaluator=evaluator)
# # Adding of extended population to history
# self._update_population(self.initial_individuals, 'extended_initial_assumptions')
def
_evolve_population
(
self
,
evaluator
:
EvaluationOperator
)
->
PopulationT
:
""" Method realizing full evolution cycle """
# Defines adaptive changes to algorithm parameters
# like pop_size and operator probabilities
self
.
_update_requirements
()
# Regularize previous population
individuals_to_select
=
self
.
regularization
(
self
.
population
,
evaluator
)
# Reproduce from previous pop to get next population
new_population
=
self
.
reproducer
.
reproduce
(
individuals_to_select
,
evaluator
)
# Adaptive agent experience collection & learning
# Must be called after reproduction (that collects the new experience)
experience
=
self
.
mutation
.
agent_experience
experience
.
collect_results
(
new_population
)
self
.
mutation
.
agent
.
partial_fit
(
experience
)
# Use some part of previous pop in the next pop
new_population
=
self
.
inheritance
(
self
.
population
,
new_population
)
new_population
=
self
.
elitism
(
self
.
generations
.
best_individuals
,
new_population
)
return
new_population
def
_update_requirements
(
self
):
if
not
self
.
generations
.
is_any_improved
:
self
.
graph_optimizer_params
.
mutation_prob
,
self
.
graph_optimizer_params
.
crossover_prob
=
\
self
.
_operators_prob
.
next
(
self
.
population
)
self
.
log
.
info
(
f
'Next mutation proba:
{
self
.
graph_optimizer_params
.
mutation_prob
}
; '
f
'Next crossover proba:
{
self
.
graph_optimizer_params
.
crossover_prob
}
'
)
self
.
graph_optimizer_params
.
pop_size
=
self
.
_pop_size
.
next
(
self
.
population
)
self
.
requirements
.
max_depth
=
self
.
_graph_depth
.
next
()
self
.
log
.
info
(
f
'Next population size:
{
self
.
graph_optimizer_params
.
pop_size
}
; '
f
'max graph depth:
{
self
.
requirements
.
max_depth
}
'
)
# update requirements in operators
for
operator
in
self
.
operators
:
operator
.
update_requirements
(
self
.
graph_optimizer_params
,
self
.
requirements
)
This diff is collapsed.
Click to expand it.
golem/core/optimisers/genetic/pool.py
0 → 100644
View file @
fe385008
from
dataclasses
import
dataclass
from
enum
import
Enum
,
auto
from
typing
import
Optional
,
List
,
Any
,
Callable
class
ParametersTypesEnum
(
Enum
):
UNKNOWN
=
auto
()
OPTIMIZER
=
auto
()
POOL
=
auto
()
NODE
=
auto
()
def
__ge__
(
self
,
other
):
if
self
.
__class__
is
other
.
__class__
:
return
self
.
value
>=
other
.
value
return
NotImplemented
def
__gt__
(
self
,
other
):
if
self
.
__class__
is
other
.
__class__
:
return
self
.
value
>
other
.
value
return
NotImplemented
def
__next__
(
self
):
return
ParametersTypesEnum
(
self
.
value
+
1
)
# class Parameters:
# def __init__(self, type_: ParametersTypesEnum, data: Optional[dict] = None):
# data = data or dict()
#
# for k in data:
# if isinstance(data[k], dict):
# data[k] = Parameters(next(type_), data[k])
# self.type = type_
# self.__data = data
#
# def __getitem__(self, keys):
# data = self.__data
# for key in keys:
# data = data[key]
# return data
#
# def __setitem__(self, keys, value):
# data = self.__data
# for key in keys[:-1]:
# if key not in data:
# data[key] = Parameters(next(self.type))
# data = data[key]
# data[keys[-1]] = value
#
# def __repr__(self):
# def pp(parameters, indent=0):
# return '\n' + '\n'.join(f"{' ' * indent}'{key}': {value.type.name + pp(value, indent + 2) if isinstance(value, self.__class__) else value}"
# for key, value in parameters.__data.items())
# return self.type.name + pp(self)
#
# def __iter__(self):
# return (x for x in self.__data.keys())
#
# def items(self):
# return (x for x in self.__data.items())
#
# def filter_by_type(self, type_: ParametersTypesEnum):
# return [pars for name, pars in self.items()
# if isinstance(pars, Parameters) and pars.type is type_]
class
Parameters
:
pass
@
dataclass
class
OptimizerParameters
(
Parameters
):
pool_parameters
:
List
[
'PoolParameters'
]
n_jobs
:
int
=
-
1
@
dataclass
class
PoolParameters
(
Parameters
):
name
:
str
constructor
:
Callable
n_jobs
:
int
nodes
:
List
[
'Node'
]
scheme
:
'Scheme'
task_constructor
:
Callable
task_history
:
List
[
Any
]
class
Optimizer
:
def
__init__
(
self
,
parameters
:
OptimizerParameters
):
self
.
parameters
=
parameters
def
_evolve_population
(
self
):
common_parameters
=
self
.
parameters
for
pool_params
in
common_parameters
.
pool_parameters
:
pool
=
pool_params
.
constructor
(
pool_params
,
common_parameters
)
common_parameters
.
update
(
pool
.
run
())
class
Pool
:
""" Pool of nodes """
def
__init__
(
self
,
pool_parameters
:
PoolParameters
,
parameters
:
OptimizerParameters
):
self
.
name
=
pool_parameters
.
name
self
.
nodes_map
=
{
node
.
name
:
node
for
node
in
pool_parameters
.
nodes
}
self
.
task
=
pool_parameters
.
task
self
.
scheme
=
pool_parameters
.
scheme
# TODO error if there are some nodes with same name
def
__call__
(
self
,
task
:
Task
):
if
not
task
.
next
in
self
.
nodes_map
:
raise
ValueError
((
f
"Pool
{
self
.
name
}
. Unknown node
{
task
.
next
}
. "
f
"Existing nodes:
{
', '
.
join
(
self
.
nodes_map
)
}
."
))
processed_task
=
task
.
run_on_node
(
self
.
nodes_map
[
task
.
next
])
return
processed_task
class
Node
:
""" Node with operation """
def
__init__
(
self
,
name
:
str
,
operation
:
Callable
):
self
.
name
=
name
self
.
operation
=
operation
def
__call__
(
self
,
*
args
,
**
kwargs
):
return
self
.
operation
(
*
args
,
**
kwargs
)
class
Task
:
""" Data with parameters for operation """
def
__init__
(
self
,
data
:
Any
,
parameters
:
Any
):
self
.
data
=
data
self
.
parameters
=
parameters
def
run_on_node
(
self
,
node
:
Node
):
result
=
node
(
self
.
data
,
self
.
parameters
)
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment
Menu
Projects
Groups
Snippets
Help