forked from ITMO-NSS-team/EPDE
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathright_part_selection.py
More file actions
185 lines (154 loc) · 8.95 KB
/
Copy pathright_part_selection.py
File metadata and controls
185 lines (154 loc) · 8.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jun 16 20:50:55 2021
@author: mike_ubuntu
"""
import time
import numpy as np
from copy import deepcopy
import warnings
import epde.globals as global_var
from epde.operators.utils.template import CompoundOperator
from epde.decorators import HistoryExtender
from epde.structure.main_structures import Term, Equation
class EqRightPartSelector(CompoundOperator):
'''
Operator for selection of the right part of the equation to emulate approximation of non-trivial function.
Works in the following manner: in a loop each term is considered as the right part, for this division the
fitness function value is calculated. The term, corresponding to the separation with the highest FF value is
saved as the correct right part.
Noteable attributes:
-----------
suboperators : dict
Inhereted from the CompoundOperator class
key - str, value - instance of a class, inhereted from the CompoundOperator.
Suboperators, performing tasks of equation processing. In this case, only one suboperator is present:
fitness_calculation, dedicated to calculation of fitness function value.
Methods:
-----------
apply(equation)
return None
Inplace detection of index of the best separation into right part, saved into ``equation.target_idx``
'''
key = 'FitnessCheckingRightPartSelector'
@HistoryExtender('\n -> The equation structure was detected: ', 'a')
def apply(self, objective : Equation, arguments : dict):
self_args, subop_args = self.parse_suboperator_args(arguments = arguments)
objective.reset_state(True)
while not (objective.simplified and objective.is_correct_right_part):
objective.is_correct_right_part = False
min_fitness = np.inf
weights_internal = np.zeros_like(objective.structure)
min_idx = 0
if not any(term.contains_variable(objective.main_var_to_explain) and term.contains_deriv(objective.main_var_to_explain) for term in objective.structure):
objective.restore_property(mandatory_family=objective.main_var_to_explain, deriv=True)
for target_idx, target_term in enumerate(objective.structure):
if not (objective.structure[target_idx].contains_variable(objective.main_var_to_explain) and objective.structure[target_idx].contains_deriv(objective.main_var_to_explain)):
continue
objective.target_idx = target_idx
fitness = self.suboperators['fitness_calculation'].apply(objective, arguments = subop_args['fitness_calculation'], force_out_of_place = True)
if fitness < min_fitness:
min_fitness = fitness
min_idx = target_idx
weights_internal = objective.weights_internal
else:
pass
objective.weights_internal = weights_internal
objective.target_idx = min_idx
self.simplify_equation(objective)
if objective.structure[objective.target_idx].contains_variable(objective.main_var_to_explain) and objective.structure[objective.target_idx].contains_deriv(objective.main_var_to_explain):
objective.is_correct_right_part = True
else:
objective.right_part_selected = True
def simplify_equation(self, objective: Equation):
# Get nonzero terms
nonzero_terms_mask = np.array([False if weight == 0 else True for weight in objective.weights_internal], dtype=np.integer)
nonrs_terms = [term for i, term in enumerate(objective.structure) if i != objective.target_idx]
nonzero_terms = [item for item, keep in zip(nonrs_terms, nonzero_terms_mask) if keep]
nonzero_terms.append(objective.structure[objective.target_idx])
equation_terms = objective.described_variables
# If amount nonzero terms is more than one -- get their intersection
if len(equation_terms) > 1:
common_factor = list(frozenset.intersection(*equation_terms))
common_dim = []
if len(common_factor) > 0:
# Find if this intersection in the same dimension (i.e. trigonometry functions) + it's minimal order
min_order = np.inf
for term in nonzero_terms:
for factor in term.structure:
if factor.cache_label[0] == common_factor[0][0]:
if len(factor.params) > 1:
common_dim.append(factor.params[-1])
if factor.cache_label[1][0] < min_order:
min_order = factor.cache_label[1][0]
if len(set(common_dim)) < 2:
# If dimension is the same -- reduce order of terms' factor
for term in nonzero_terms:
temp = deepcopy(term)
factors_simplified = []
for factor in term.structure:
if factor.cache_label[0] == common_factor[0][0]:
for i, value in enumerate(factor.params_description):
if factor.params_description[i]["name"] == "power":
factor.params[i] -= min_order
if factor.params[i] == 0:
factors_simplified.append(factor)
term.structure = [factor for factor in term.structure if factor not in factors_simplified]
term.reset_saved_state()
# If term's order became zero -- replace term
if (len(term.structure) == 0 or not term.contains_meaningful()):
term.randomize()
term.reset_saved_state()
while objective.structure.count(term) > 1 or term == temp:
term.randomize()
term.reset_saved_state()
return
objective.simplified = True
def use_default_tags(self):
self._tags = {'equation right part selection', 'gene level', 'contains suboperators', 'inplace'}
class RandomRHPSelector(CompoundOperator):
'''
Operator for selection of the right part of the equation to emulate approximation of non-trivial function.
Works in the following manner: in a loop each term is considered as the right part, for this division the
fitness function value is calculated. The term, corresponding to the separation with the highest FF value is
saved as the correct right part.
Noteable attributes:
-----------
suboperators : dict
Inhereted from the CompoundOperator class
key - str, value - instance of a class, inhereted from the CompoundOperator.
Suboperators, performing tasks of equation processing. In this case, only one suboperator is present:
fitness_calculation, dedicated to calculation of fitness function value.
Methods:
-----------
apply(equation)
return None
Inplace detection of index of the best separation into right part, saved into ``equation.target_idx``
'''
key = 'RandomRightPartSelector'
@HistoryExtender('\n -> The equation structure was detected: ', 'a')
def apply(self, objective : Equation, arguments : dict):
# print(f'CALLING RIGHT PART SELECTOR FOR {objective.text_form}')
self_args, subop_args = self.parse_suboperator_args(arguments = arguments)
if not objective.right_part_selected:
term_selection = [term_idx for term_idx, term in enumerate(objective.structure)
if term.contains_deriv(variable = objective.main_var_to_explain)]
if len(term_selection) == 0:
idx = np.random.choice([term_idx for term_idx, _ in enumerate(objective.structure)])
prev_term = objective.structure[idx]
while True:
candidate_term = Term(pool = prev_term.pool, mandatory_family = objective.main_var_to_explain,
max_factors_in_term = len(prev_term.structure),
create_derivs = True)
if candidate_term.contains_deriv(variable = objective.main_var_to_explain):
break
objective.structure[idx] = candidate_term
else:
idx = np.random.choice(term_selection)
objective.target_idx = idx
# print('Selected right part term', objective.structure[idx].name)
objective.reset_explaining_term(idx)
objective.right_part_selected = True
def use_default_tags(self):
self._tags = {'equation right part selection', 'gene level', 'contains suboperators', 'inplace'}