Spaces:
Runtime error
Runtime error
amirhosseinkarami
commited on
Commit
•
bae498f
1
Parent(s):
1139920
Add code files
Browse files- CompareRegressors.py +227 -0
- DESolver.py +64 -0
- DataUtils.py +801 -0
- FeynmanEqns.txt +19 -0
- LearnRules.py +146 -0
- LearnTriangles.py +252 -0
- MLP_Model.py +239 -0
- RegressorTest.py +115 -0
- Settings.py +277 -0
- SymbolicFunctionLearner.py +1449 -0
- gp_model.py +163 -0
CompareRegressors.py
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# 786/110
|
2 |
+
|
3 |
+
import os
|
4 |
+
import sys
|
5 |
+
import time
|
6 |
+
|
7 |
+
import matplotlib.pyplot as plt
|
8 |
+
import numpy as np
|
9 |
+
|
10 |
+
import DataUtils
|
11 |
+
import Settings as settings
|
12 |
+
from DataUtils import generate_random_eqn, multidim_dataset_from_eqn_list
|
13 |
+
from DataUtils import simple_eqn_to_str
|
14 |
+
from MLP_Model import MLP_Model
|
15 |
+
# from models.smtree_model import SMTree_Model
|
16 |
+
# from models.smftree_model import SMFTree_Model
|
17 |
+
# from models.smfftree_model import SMFFTree_Model
|
18 |
+
# from models.addoptree_model import AddOpTree_Model
|
19 |
+
# from models.crtree_model import CRTree_Model
|
20 |
+
# from models.sptree_model import SpTree_Model
|
21 |
+
# from models.lbl_model import LBLTree_Model
|
22 |
+
from SymbolicFunctionLearner import SFL
|
23 |
+
# from models.mcts_model import MCTS_Model
|
24 |
+
from gp_model import Genetic_Model
|
25 |
+
|
26 |
+
""" Test Hyperparameters """
|
27 |
+
num_eqns_to_create = 150
|
28 |
+
num_training = 1000
|
29 |
+
num_valid = 1000
|
30 |
+
num_test = 1000
|
31 |
+
allowable_ops = settings.function_set.copy()
|
32 |
+
num_vars = 1 # 10
|
33 |
+
num_max_levels = 3 # 4
|
34 |
+
|
35 |
+
settings.n_tree_layers = num_max_levels
|
36 |
+
settings.num_features = num_vars
|
37 |
+
settings.show_output = False
|
38 |
+
settings.keep_logs = False
|
39 |
+
settings.mode = "sr"
|
40 |
+
|
41 |
+
show_found_eqns = True
|
42 |
+
|
43 |
+
use_constants_in_eqns = True
|
44 |
+
|
45 |
+
if not os.path.exists('images'):
|
46 |
+
os.makedirs('images')
|
47 |
+
|
48 |
+
|
49 |
+
def plot_all_models(eqn_str, eqn_number, all_models, this_train_x, this_train_y,
|
50 |
+
this_test_x, this_test_y):
|
51 |
+
plt.figure()
|
52 |
+
plt.title('Compare models: {}'.format(eqn_str))
|
53 |
+
|
54 |
+
min_y = np.min(this_test_y)
|
55 |
+
max_y = np.max(this_test_y)
|
56 |
+
y_range = max_y - min_y
|
57 |
+
|
58 |
+
plt.scatter(this_train_x, this_train_y, color='xkcd:dark pink',
|
59 |
+
marker='o', s=100, label='Training set')
|
60 |
+
|
61 |
+
plt.scatter(this_test_x, this_test_y, color='gray', alpha=0.5, marker='.', label='Ground truth')
|
62 |
+
|
63 |
+
for i in range(len(all_models)):
|
64 |
+
this_model = all_models[i]
|
65 |
+
test_hat_y = this_model.predict(this_test_x)
|
66 |
+
plt.scatter(this_test_x,
|
67 |
+
test_hat_y, alpha=0.7, marker='.', label='{}'.format(this_model.name))
|
68 |
+
|
69 |
+
for xc in settings.train_scope:
|
70 |
+
plt.axvline(x=xc, color='k', linestyle='dashed', linewidth=2)
|
71 |
+
|
72 |
+
plt.ylim([min_y - 0.5 * y_range, max_y + 0.5 * y_range])
|
73 |
+
|
74 |
+
plt.legend()
|
75 |
+
plt.savefig("images/eqn_{}.png".format(eqn_number))
|
76 |
+
plt.close()
|
77 |
+
|
78 |
+
|
79 |
+
models_to_test = []
|
80 |
+
models_to_test.append(Genetic_Model)
|
81 |
+
models_to_test.append(MLP_Model)
|
82 |
+
# models_to_test.append(Tree_Model)
|
83 |
+
|
84 |
+
models_to_test.append(SFL)
|
85 |
+
# models_to_test.append(SMTree_Model)
|
86 |
+
# models_to_test.append(SMFTree_Model)
|
87 |
+
# models_to_test.append(SMFFTree_Model)
|
88 |
+
# models_to_test.append(AddOpTree_Model)
|
89 |
+
|
90 |
+
# models_to_test.append(SpTree_Model)
|
91 |
+
|
92 |
+
|
93 |
+
lists_of_error_scores = []
|
94 |
+
lists_of_iter_times = []
|
95 |
+
all_models = []
|
96 |
+
for model_type in models_to_test:
|
97 |
+
all_models.append(model_type())
|
98 |
+
lists_of_error_scores.append([])
|
99 |
+
lists_of_iter_times.append([])
|
100 |
+
|
101 |
+
# all_models.append(SpTree_Model(use_scopers=True))
|
102 |
+
# lists_of_error_scores.append([])
|
103 |
+
# lists_of_iter_times.append([])
|
104 |
+
|
105 |
+
seen_eqns = []
|
106 |
+
winning_entries = []
|
107 |
+
valid_err = 0
|
108 |
+
|
109 |
+
print("Starting program.")
|
110 |
+
print(settings.num_features)
|
111 |
+
|
112 |
+
start_time = time.time()
|
113 |
+
|
114 |
+
for eqn_n in range(1, num_eqns_to_create + 1):
|
115 |
+
print("----------------")
|
116 |
+
# Create some random equation
|
117 |
+
current_eqn_as_list = generate_random_eqn(allowable_ops, num_vars, num_max_levels,
|
118 |
+
allow_constants=use_constants_in_eqns)
|
119 |
+
current_eqn_as_str = simple_eqn_to_str(current_eqn_as_list)
|
120 |
+
|
121 |
+
# should be a new equation
|
122 |
+
while current_eqn_as_str in seen_eqns:
|
123 |
+
current_eqn_as_list = generate_random_eqn(allowable_ops, num_vars, num_max_levels,
|
124 |
+
allow_constants=use_constants_in_eqns)
|
125 |
+
current_eqn_as_str = simple_eqn_to_str(current_eqn_as_list)
|
126 |
+
seen_eqns.append(current_eqn_as_str)
|
127 |
+
|
128 |
+
print(current_eqn_as_list)
|
129 |
+
print("Random equation {} of {}".format(eqn_n, num_eqns_to_create))
|
130 |
+
print("True function: {}\n".format(current_eqn_as_str))
|
131 |
+
with open("images/compare_test_output.txt", "a") as output_file:
|
132 |
+
output_file.write("\n{}\nTrue equation:\n{}\n".format(eqn_n, current_eqn_as_str))
|
133 |
+
|
134 |
+
# Create a dataset with that equation
|
135 |
+
train_data_x, train_data_y = multidim_dataset_from_eqn_list(current_eqn_as_list, num_training, n_vars=num_vars)
|
136 |
+
test_data_x, test_data_y = multidim_dataset_from_eqn_list(current_eqn_as_list, num_test, n_vars=num_vars,
|
137 |
+
min_x=settings.test_scope[0],
|
138 |
+
max_x=settings.test_scope[1])
|
139 |
+
|
140 |
+
# train_data_x, train_data_y = create_dataset_from_eqn_list(current_eqn_as_list, num_vars, num_training,
|
141 |
+
# settings.train_scope[0], settings.train_scope[1])
|
142 |
+
# test_data_x, test_data_y = create_dataset_from_eqn_list(current_eqn_as_list, num_vars, num_test,
|
143 |
+
# settings.test_scope[0], settings.test_scope[1])
|
144 |
+
|
145 |
+
for model_i in range(len(all_models)):
|
146 |
+
model = all_models[model_i]
|
147 |
+
model.reset()
|
148 |
+
itertime_start = time.time()
|
149 |
+
# if False: # model_i >= len(all_models) - 2:
|
150 |
+
# model.repeat_train(train_data_x, train_data_y)
|
151 |
+
# else:
|
152 |
+
model_eqn, _, best_err = model.repeat_train(train_data_x, train_data_y,
|
153 |
+
test_x=test_data_x, test_y=test_data_y,
|
154 |
+
verbose=False)
|
155 |
+
if show_found_eqns:
|
156 |
+
print("{} function: {}".format(model.name, model_eqn)[:550])
|
157 |
+
|
158 |
+
# Test model on that equation
|
159 |
+
# test_err = model.test(test_data_x, test_data_y)
|
160 |
+
test_err = max(np.exp(-10), best_err) # data_utils.test_from_formula(model_eqn, test_data_x, test_data_y)
|
161 |
+
print(" ---> {} Test Error: {:.5f}".format(model.short_name, test_err))
|
162 |
+
lists_of_error_scores[model_i].extend([test_err])
|
163 |
+
lists_of_iter_times[model_i].append(time.time() - itertime_start)
|
164 |
+
sys.stdout.flush()
|
165 |
+
|
166 |
+
# y_gold_list = list(model.sess.run(model.y_gold,
|
167 |
+
# feed_dict={model.data_x: np.reshape(test_data_x[:4][:], [-1, num_vars]),
|
168 |
+
# model.data_y: np.reshape(test_data_y[:4][:], [-1, 1]),
|
169 |
+
# model.var_random_u: model.var_random_selector,
|
170 |
+
# model.op_random_u: model.op_random_selector}).reshape(1, -1)[0])
|
171 |
+
# y_hat_list2 = data_utils.predict_from_formula(model.get_simple_formula(digits=4), test_data_x[:4][:])
|
172 |
+
#
|
173 |
+
# print('Performance on sample validation data:')
|
174 |
+
# for feature_i in range(model.n_input_variables):
|
175 |
+
# print('x{}: '.format(feature_i + 1),
|
176 |
+
# ['{:7.4f}'.format(yyy[feature_i]) for yyy in test_data_x[:4][:]])
|
177 |
+
# print("-----------------------------------------------------")
|
178 |
+
# print('y_gold: ', ['{:7.4f}'.format(yyy) for yyy in y_gold_list])
|
179 |
+
# print('y_hat2: ', ['{:7.4f}'.format(yyy) for yyy in y_hat_list2])
|
180 |
+
|
181 |
+
if test_err < 0.035:
|
182 |
+
winning_entries.append("{} - {}".format(current_eqn_as_str, model.short_name))
|
183 |
+
|
184 |
+
with open("images/compare_test_output.txt", "a") as output_file:
|
185 |
+
output_file.write("{}: {}\n{}\n".format(model.short_name, test_err, model_eqn))
|
186 |
+
|
187 |
+
print()
|
188 |
+
|
189 |
+
DataUtils.plot_hist_of_errors(lists_of_error_scores, all_models, eqn_n)
|
190 |
+
|
191 |
+
# todo: something is wrong with this function!!
|
192 |
+
# todo: it plots the model as is (after last iter), not at best iter, of repeat_train
|
193 |
+
# DataUtils.plot_all_models_predicted_actual(all_models, test_data_x, test_data_y,
|
194 |
+
# set_name="Eqn {}: {}".format(eqn_n, current_eqn_as_str),
|
195 |
+
# fig_name="eqn_{}".format(eqn_n))
|
196 |
+
|
197 |
+
plt.figure()
|
198 |
+
x_axis = [i + 1 for i in range(eqn_n)]
|
199 |
+
for iter_times_i in range(len(lists_of_iter_times)):
|
200 |
+
plt.plot(x_axis, lists_of_iter_times[iter_times_i], label=all_models[iter_times_i].short_name)
|
201 |
+
plt.xlabel("Iteration")
|
202 |
+
plt.ylabel("Running time")
|
203 |
+
plt.legend()
|
204 |
+
plt.savefig("images/time_curve.png")
|
205 |
+
plt.close()
|
206 |
+
|
207 |
+
# if num_vars == 1:
|
208 |
+
# plot_all_models(current_eqn_as_str, eqn_n, all_models,
|
209 |
+
# train_data_x, train_data_y,
|
210 |
+
# test_data_x, test_data_y)
|
211 |
+
|
212 |
+
running_time = time.time() - start_time
|
213 |
+
print("Done. Took {} seconds.\n".format(running_time))
|
214 |
+
#
|
215 |
+
# plt.figure()
|
216 |
+
# plt.hist([list_of_error_scores_GP, list_of_error_scores_MLP, list_of_error_scores_Tree], label=['GP', 'MLP', 'Tree'])
|
217 |
+
# plt.legend(loc='upper right')
|
218 |
+
# plt.show()
|
219 |
+
#
|
220 |
+
# plt.figure()
|
221 |
+
# plt.hist(list_of_error_scores_GP, edgecolor='k', linewidth=1.2)
|
222 |
+
# plt.show()
|
223 |
+
#
|
224 |
+
# plt.figure()
|
225 |
+
# plt.hist(list_of_error_scores_GP, cumulative=True)
|
226 |
+
# plt.show()
|
227 |
+
#
|
DESolver.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""""""""""""""""""""""""""""""""
|
2 |
+
This file is for running.
|
3 |
+
Do not modify this file.
|
4 |
+
|
5 |
+
For running: DiffEqnSolver.py
|
6 |
+
For modifying: settings.py
|
7 |
+
"""""""""""""""""""""""""""""""""
|
8 |
+
|
9 |
+
import os
|
10 |
+
import time
|
11 |
+
|
12 |
+
import DataUtils
|
13 |
+
import Settings as settings
|
14 |
+
from SymbolicFunctionLearner import SFL
|
15 |
+
|
16 |
+
settings.mode = "de"
|
17 |
+
|
18 |
+
current_model = SFL()
|
19 |
+
if not os.path.exists('images'):
|
20 |
+
os.makedirs('images')
|
21 |
+
|
22 |
+
print('\nBeginning experiment: {}'.format(current_model.name))
|
23 |
+
|
24 |
+
print("{} tree layers.".format(settings.n_tree_layers))
|
25 |
+
print("{} features of {} component(s) each.".format(settings.num_features, settings.num_dims_per_feature))
|
26 |
+
print("{} components in output.".format(settings.n_dims_in_output))
|
27 |
+
print("{} operators: {}.".format(len(current_model.function_set),
|
28 |
+
current_model.function_set))
|
29 |
+
|
30 |
+
train_errors = []
|
31 |
+
valid_errors = []
|
32 |
+
test_errors = []
|
33 |
+
true_eqns = []
|
34 |
+
|
35 |
+
train_X = DataUtils.generate_data(settings.train_N, n_vars=current_model.n_input_variables,
|
36 |
+
avoid_zero=settings.avoid_zero)
|
37 |
+
valid_X = DataUtils.generate_data(settings.train_N, n_vars=current_model.n_input_variables,
|
38 |
+
avoid_zero=settings.avoid_zero)
|
39 |
+
test_X = DataUtils.generate_data(settings.test_N, n_vars=current_model.n_input_variables,
|
40 |
+
min_x=settings.test_scope[0],
|
41 |
+
max_x=settings.test_scope[1])
|
42 |
+
|
43 |
+
print("\n========================")
|
44 |
+
print("Starting Solver.")
|
45 |
+
print("==========================\n")
|
46 |
+
|
47 |
+
# Train the model from scratch several times, keeping the best one.
|
48 |
+
start_time = time.time()
|
49 |
+
best_model, best_iter, best_err = current_model.repeat_train(train_X,
|
50 |
+
num_repeats=settings.num_train_repeat_processes,
|
51 |
+
test_x=test_X)
|
52 |
+
running_time = time.time() - start_time
|
53 |
+
|
54 |
+
print("best_model: {}".format(best_model))
|
55 |
+
print("----------------------")
|
56 |
+
print("Finished DE. Took {:.2f} minutes.\n".format(running_time / 60))
|
57 |
+
|
58 |
+
|
59 |
+
print("Final solution found at attempt {}:".format(best_iter))
|
60 |
+
print("y = {}".format(best_model))
|
61 |
+
print("Test error: {}".format(best_err))
|
62 |
+
if best_err < 0.02:
|
63 |
+
print("Attained error less than 0.02 - great!")
|
64 |
+
print()
|
DataUtils.py
ADDED
@@ -0,0 +1,801 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""""""""""""""""""""""""""""""""
|
2 |
+
Do not run or modify this file.
|
3 |
+
|
4 |
+
For running: DiffEqnSolver.py
|
5 |
+
For modifying: settings.py
|
6 |
+
"""""""""""""""""""""""""""""""""
|
7 |
+
|
8 |
+
import matplotlib.pyplot as plt
|
9 |
+
import numpy as np
|
10 |
+
import sympy
|
11 |
+
import tensorflow as tf
|
12 |
+
from matplotlib import cm
|
13 |
+
from sympy import expand, sympify, lambdify
|
14 |
+
|
15 |
+
import Settings
|
16 |
+
|
17 |
+
|
18 |
+
def safe_abs(x):
|
19 |
+
return np.sqrt(x * x + Settings.eps)
|
20 |
+
|
21 |
+
|
22 |
+
def safe_div(x, y):
|
23 |
+
return np.sign(y) * x / safe_abs(y)
|
24 |
+
|
25 |
+
|
26 |
+
def tf_diff_abs(x):
|
27 |
+
return tf.sqrt(tf.square(x) + Settings.eps)
|
28 |
+
|
29 |
+
|
30 |
+
def tf_diff_sqrt(x):
|
31 |
+
return tf.sqrt(tf_diff_abs(x))
|
32 |
+
|
33 |
+
|
34 |
+
def tf_diff_log(x):
|
35 |
+
return tf.math.log(tf_diff_abs(x))
|
36 |
+
|
37 |
+
|
38 |
+
def our_tanh(x, factor=1000):
|
39 |
+
return factor * tf.tanh(x / factor)
|
40 |
+
|
41 |
+
|
42 |
+
def spike(x):
|
43 |
+
return 1.0 / (1 + 200 * tf.square(x))
|
44 |
+
# return tf.math.exp(-10 * tf.square(x))
|
45 |
+
|
46 |
+
|
47 |
+
def true_function(input_x):
|
48 |
+
return predict_from_formula(Settings.true_eqn, input_x)
|
49 |
+
|
50 |
+
|
51 |
+
def is_float(value):
|
52 |
+
try:
|
53 |
+
float(value)
|
54 |
+
return True
|
55 |
+
except ValueError:
|
56 |
+
return False
|
57 |
+
|
58 |
+
|
59 |
+
# Function to generate random equation as operator/input list
|
60 |
+
# Variables are numbered 1 ... n, and 0 does not appear
|
61 |
+
# Constants appear as [float] e.g [3.14]
|
62 |
+
def generate_random_eqn_two_list(op_list, n_vars, n_levels, allow_constants=True):
|
63 |
+
eqn_ops = list(np.random.choice(op_list, size=int(2 ** n_levels) - 1, replace=True))
|
64 |
+
|
65 |
+
if allow_constants:
|
66 |
+
eqn_vars = list(np.random.choice(range(1, max(int(n_vars * 1.6), n_vars + 2)),
|
67 |
+
size=int(2 ** n_levels), replace=True))
|
68 |
+
for i in range(len(eqn_vars)):
|
69 |
+
if eqn_vars[i] >= n_vars + 1:
|
70 |
+
eqn_vars[i] = [np.random.uniform(Settings.test_scope[0], Settings.test_scope[1])]
|
71 |
+
else:
|
72 |
+
eqn_vars = list(np.random.choice(range(1, 1 + n_vars), size=int(2 ** n_levels), replace=True))
|
73 |
+
return [eqn_ops, eqn_vars]
|
74 |
+
|
75 |
+
|
76 |
+
# Function to generate random equation as operator/input list and weight/bias list
|
77 |
+
# Variables are numbered 1 ... n, and 0 does not appear
|
78 |
+
# Constants appear in weight and bias lists.
|
79 |
+
# const_ratio determines how many weights are not 1, and how many biases are not 0
|
80 |
+
def generate_random_eqn(op_list, n_vars, n_levels, allow_constants=True, const_ratio=0.8):
|
81 |
+
eqn_ops = list(np.random.choice(op_list, size=int(2 ** n_levels) - 1, replace=True))
|
82 |
+
eqn_vars = list(np.random.choice(range(1, (n_vars + 1)), size=int(2 ** n_levels), replace=True))
|
83 |
+
max_bound = max(np.abs(Settings.test_scope[0]), np.abs(Settings.test_scope[1]))
|
84 |
+
eqn_weights = list(np.random.uniform(-1 * max_bound, max_bound, size=len(eqn_vars)))
|
85 |
+
eqn_biases = list(np.random.uniform(-1 * max_bound, max_bound, size=len(eqn_vars)))
|
86 |
+
|
87 |
+
if not allow_constants:
|
88 |
+
const_ratio = 0.0
|
89 |
+
random_const_chooser_w = np.random.uniform(0, 1, len(eqn_weights))
|
90 |
+
random_const_chooser_b = np.random.uniform(0, 1, len(eqn_biases))
|
91 |
+
|
92 |
+
for i in range(len(eqn_weights)):
|
93 |
+
if random_const_chooser_w[i] >= const_ratio:
|
94 |
+
eqn_weights[i] = 1
|
95 |
+
if random_const_chooser_b[i] >= const_ratio:
|
96 |
+
eqn_biases[i] = 0
|
97 |
+
|
98 |
+
return [eqn_ops, eqn_vars, eqn_weights, eqn_biases]
|
99 |
+
|
100 |
+
|
101 |
+
# Function to create a multidim input data set given an operator/input list
|
102 |
+
def generate_data(n_points, n_vars=Settings.num_features,
|
103 |
+
n_input_dims=Settings.num_dims_per_feature,
|
104 |
+
min_x=Settings.train_scope[0], max_x=Settings.train_scope[1],
|
105 |
+
avoid_zero=False):
|
106 |
+
if not avoid_zero:
|
107 |
+
x_data = [np.random.uniform(min_x, max_x, size=[n_input_dims, n_vars]) for _ in range(n_points)]
|
108 |
+
else:
|
109 |
+
x_data = []
|
110 |
+
for _ in range(n_points):
|
111 |
+
candidate = np.random.uniform(min_x, max_x, size=[n_input_dims, n_vars])
|
112 |
+
while np.linalg.norm(candidate) < 0.1:
|
113 |
+
candidate = np.random.uniform(min_x, max_x, size=[n_input_dims, n_vars])
|
114 |
+
x_data.append(candidate)
|
115 |
+
return np.array(x_data)
|
116 |
+
|
117 |
+
|
118 |
+
# Function to create a data set given an operator/input list
|
119 |
+
def create_dataset_from_eqn_list(eqn_as_list, n_vars, n_points, min_x, max_x):
|
120 |
+
x_data = [list(np.random.uniform(min_x, max_x, n_vars)) for _ in range(n_points)]
|
121 |
+
y_data = [evaluate_eqn_list_on_datum(eqn_as_list, x_data_i) + np.random.normal(0, 0.05) for x_data_i in x_data]
|
122 |
+
return [np.array(x_data), np.array(y_data)]
|
123 |
+
|
124 |
+
|
125 |
+
# Function to create a multidim data set given an operator/input list
|
126 |
+
def multidim_dataset_from_eqn_list(eqn_as_list, n_points,
|
127 |
+
n_vars=Settings.num_features,
|
128 |
+
n_input_dims=Settings.num_dims_per_feature,
|
129 |
+
n_output_dims=Settings.n_dims_in_output,
|
130 |
+
min_x=Settings.train_scope[0], max_x=Settings.train_scope[1],
|
131 |
+
avoid_zero=False):
|
132 |
+
if not avoid_zero:
|
133 |
+
x_data = [np.random.uniform(min_x, max_x, size=[n_input_dims, n_vars]) for _ in range(n_points)]
|
134 |
+
else:
|
135 |
+
x_data = []
|
136 |
+
for _ in range(n_points):
|
137 |
+
candidate = np.random.uniform(min_x, max_x, size=[n_input_dims, n_vars])
|
138 |
+
while np.linalg.norm(candidate) < 0.1:
|
139 |
+
candidate = np.random.uniform(min_x, max_x, size=[n_input_dims, n_vars])
|
140 |
+
x_data.append(candidate)
|
141 |
+
y_data = [evaluate_eqn_list_on_multidim_datum(eqn_as_list, x_data_i) # + np.random.normal(0, 0.05)
|
142 |
+
for x_data_i in x_data]
|
143 |
+
if n_output_dims == 1:
|
144 |
+
y_data = [np.mean(old_y) for old_y in y_data]
|
145 |
+
return [np.array(x_data), np.reshape(np.array(y_data), [n_points, n_output_dims, 1])]
|
146 |
+
|
147 |
+
|
148 |
+
def make_y_multi_safe(old_y, n_dims_per_input_var=1, n_dims_in_output=1):
|
149 |
+
if isinstance(old_y, list):
|
150 |
+
new_y = np.array(old_y)
|
151 |
+
new_y.reshape([-1, n_dims_in_output, 1])
|
152 |
+
else:
|
153 |
+
new_y = old_y.copy()
|
154 |
+
if len(new_y.shape) == 1:
|
155 |
+
assert (n_dims_in_output == 1)
|
156 |
+
new_y = [[[y_value] for _ in range(n_dims_per_input_var)] for y_value in new_y]
|
157 |
+
new_y = np.array(new_y)
|
158 |
+
elif len(new_y.shape) == 2:
|
159 |
+
assert (n_dims_in_output == 1)
|
160 |
+
new_y = [[y_value for _ in range(n_dims_per_input_var)] for y_value in new_y]
|
161 |
+
new_y = np.array(new_y)
|
162 |
+
elif new_y.shape[1] < n_dims_per_input_var:
|
163 |
+
assert (n_dims_in_output == 1)
|
164 |
+
new_y = [[y_value[0] for _ in range(n_dims_per_input_var)] for y_value in new_y]
|
165 |
+
new_y = np.array(new_y)
|
166 |
+
return new_y
|
167 |
+
|
168 |
+
|
169 |
+
# Function to evaluate equation (in two-list format) on a data point
|
170 |
+
def evaluate_eqn_list_on_datum_two_list(eqn_as_list, input_x):
|
171 |
+
eqn_ops = eqn_as_list[0]
|
172 |
+
eqn_vars = eqn_as_list[1]
|
173 |
+
current_op = eqn_ops[0]
|
174 |
+
|
175 |
+
if len(eqn_ops) == 1:
|
176 |
+
if type(eqn_vars[0]) is list:
|
177 |
+
left_side = eqn_vars[0][0]
|
178 |
+
else:
|
179 |
+
left_side = input_x[eqn_vars[0] - 1]
|
180 |
+
if type(eqn_vars[1]) is list:
|
181 |
+
right_side = eqn_vars[1][0]
|
182 |
+
else:
|
183 |
+
right_side = input_x[eqn_vars[1] - 1]
|
184 |
+
|
185 |
+
else:
|
186 |
+
split_point = int((len(eqn_ops) + 1) / 2)
|
187 |
+
left_ops = eqn_ops[1:split_point]
|
188 |
+
right_ops = eqn_ops[split_point:]
|
189 |
+
|
190 |
+
left_vars = eqn_vars[:split_point]
|
191 |
+
right_vars = eqn_vars[split_point:]
|
192 |
+
|
193 |
+
left_side = evaluate_eqn_list_on_datum_two_list([left_ops, left_vars], input_x)
|
194 |
+
right_side = evaluate_eqn_list_on_datum_two_list([right_ops, right_vars], input_x)
|
195 |
+
|
196 |
+
if current_op == 'id':
|
197 |
+
return left_side
|
198 |
+
|
199 |
+
if current_op == 'sqrt':
|
200 |
+
return np.sqrt(np.abs(left_side))
|
201 |
+
|
202 |
+
if current_op == 'log':
|
203 |
+
return np.log(np.sqrt(left_side * left_side + 1e-10))
|
204 |
+
|
205 |
+
if current_op == 'sin':
|
206 |
+
return np.sin(left_side)
|
207 |
+
|
208 |
+
if current_op == 'exp':
|
209 |
+
return np.exp(left_side)
|
210 |
+
|
211 |
+
if current_op == 'add':
|
212 |
+
return left_side + right_side
|
213 |
+
|
214 |
+
if current_op == 'mul':
|
215 |
+
return left_side * right_side
|
216 |
+
|
217 |
+
if current_op == 'sub':
|
218 |
+
return left_side - right_side
|
219 |
+
|
220 |
+
if current_op == 'div':
|
221 |
+
return safe_div(left_side, right_side)
|
222 |
+
|
223 |
+
return None
|
224 |
+
|
225 |
+
|
226 |
+
# Function to evaluate equation (in list format) on a data point
|
227 |
+
def evaluate_eqn_list_on_datum(eqn_as_list, input_x):
|
228 |
+
eqn_ops = eqn_as_list[0]
|
229 |
+
eqn_vars = eqn_as_list[1]
|
230 |
+
eqn_weights = eqn_as_list[2]
|
231 |
+
eqn_biases = eqn_as_list[3]
|
232 |
+
current_op = eqn_ops[0]
|
233 |
+
|
234 |
+
if len(eqn_ops) == 1:
|
235 |
+
left_side = eqn_weights[0] * input_x[eqn_vars[0] - 1] + eqn_biases[0]
|
236 |
+
right_side = eqn_weights[1] * input_x[eqn_vars[1] - 1] + eqn_biases[1]
|
237 |
+
|
238 |
+
else:
|
239 |
+
split_point = int((len(eqn_ops) + 1) / 2)
|
240 |
+
left_ops = eqn_ops[1:split_point]
|
241 |
+
right_ops = eqn_ops[split_point:]
|
242 |
+
|
243 |
+
left_vars = eqn_vars[:split_point]
|
244 |
+
right_vars = eqn_vars[split_point:]
|
245 |
+
|
246 |
+
left_weights = eqn_weights[:split_point]
|
247 |
+
right_weights = eqn_weights[split_point:]
|
248 |
+
|
249 |
+
left_biases = eqn_biases[:split_point]
|
250 |
+
right_biases = eqn_biases[split_point:]
|
251 |
+
|
252 |
+
left_side = evaluate_eqn_list_on_datum([left_ops, left_vars, left_weights, left_biases], input_x)
|
253 |
+
right_side = evaluate_eqn_list_on_datum([right_ops, right_vars, right_weights, right_biases], input_x)
|
254 |
+
|
255 |
+
if current_op == 'id':
|
256 |
+
return left_side
|
257 |
+
|
258 |
+
if current_op == 'sqrt':
|
259 |
+
return np.sqrt(np.abs(left_side))
|
260 |
+
|
261 |
+
if current_op == 'log':
|
262 |
+
return np.log(np.sqrt(left_side * left_side + 1e-10))
|
263 |
+
|
264 |
+
if current_op == 'sin':
|
265 |
+
return np.sin(left_side)
|
266 |
+
|
267 |
+
if current_op == 'exp':
|
268 |
+
return np.exp(left_side)
|
269 |
+
|
270 |
+
if current_op == 'add':
|
271 |
+
return left_side + right_side
|
272 |
+
|
273 |
+
if current_op == 'mul':
|
274 |
+
return left_side * right_side
|
275 |
+
|
276 |
+
if current_op == 'sub':
|
277 |
+
return left_side - right_side
|
278 |
+
|
279 |
+
if current_op == 'div':
|
280 |
+
return safe_div(left_side, right_side)
|
281 |
+
|
282 |
+
return None
|
283 |
+
|
284 |
+
|
285 |
+
# Function to evaluate equation (in two-list format) on a data point
|
286 |
+
def evaluate_eqn_list_on_multidim_datum_two_list(eqn_as_list, input_x):
|
287 |
+
eqn_ops = eqn_as_list[0]
|
288 |
+
eqn_vars = eqn_as_list[1]
|
289 |
+
current_op = eqn_ops[0]
|
290 |
+
|
291 |
+
if len(eqn_ops) == 1:
|
292 |
+
if type(eqn_vars[0]) is list:
|
293 |
+
left_side = eqn_vars[0][0]
|
294 |
+
else:
|
295 |
+
left_side = input_x[:, eqn_vars[0] - 1]
|
296 |
+
if type(eqn_vars[1]) is list:
|
297 |
+
right_side = eqn_vars[1][0]
|
298 |
+
else:
|
299 |
+
right_side = input_x[:, eqn_vars[1] - 1]
|
300 |
+
|
301 |
+
else:
|
302 |
+
split_point = int((len(eqn_ops) + 1) / 2)
|
303 |
+
left_ops = eqn_ops[1:split_point]
|
304 |
+
right_ops = eqn_ops[split_point:]
|
305 |
+
|
306 |
+
left_vars = eqn_vars[:split_point]
|
307 |
+
right_vars = eqn_vars[split_point:]
|
308 |
+
|
309 |
+
left_side = evaluate_eqn_list_on_multidim_datum_two_list([left_ops, left_vars], input_x)
|
310 |
+
right_side = evaluate_eqn_list_on_multidim_datum_two_list([right_ops, right_vars], input_x)
|
311 |
+
|
312 |
+
if current_op == 'id':
|
313 |
+
return left_side
|
314 |
+
|
315 |
+
if current_op == 'sqrt':
|
316 |
+
return np.sqrt(np.abs(left_side))
|
317 |
+
|
318 |
+
if current_op == 'log':
|
319 |
+
return np.log(np.sqrt(left_side * left_side + 1e-10))
|
320 |
+
|
321 |
+
if current_op == 'sin':
|
322 |
+
return np.sin(left_side)
|
323 |
+
|
324 |
+
if current_op == 'exp':
|
325 |
+
return np.exp(left_side)
|
326 |
+
|
327 |
+
if current_op == 'add':
|
328 |
+
return left_side + right_side
|
329 |
+
|
330 |
+
if current_op == 'mul':
|
331 |
+
return left_side * right_side
|
332 |
+
|
333 |
+
if current_op == 'sub':
|
334 |
+
return left_side - right_side
|
335 |
+
|
336 |
+
if current_op == 'div':
|
337 |
+
return safe_div(left_side, right_side)
|
338 |
+
return None
|
339 |
+
|
340 |
+
|
341 |
+
# Function to evaluate equation (in list format) on a data point
|
342 |
+
def evaluate_eqn_list_on_multidim_datum(eqn_as_list, input_x):
|
343 |
+
eqn_ops = eqn_as_list[0]
|
344 |
+
eqn_vars = eqn_as_list[1]
|
345 |
+
eqn_weights = eqn_as_list[2]
|
346 |
+
eqn_biases = eqn_as_list[3]
|
347 |
+
current_op = eqn_ops[0]
|
348 |
+
|
349 |
+
if len(eqn_ops) == 1:
|
350 |
+
left_side = eqn_weights[0] * input_x[:, eqn_vars[0] - 1] + eqn_biases[0]
|
351 |
+
right_side = eqn_weights[1] * input_x[:, eqn_vars[1] - 1] + eqn_biases[1]
|
352 |
+
|
353 |
+
else:
|
354 |
+
split_point = int((len(eqn_ops) + 1) / 2)
|
355 |
+
left_ops = eqn_ops[1:split_point]
|
356 |
+
right_ops = eqn_ops[split_point:]
|
357 |
+
|
358 |
+
left_vars = eqn_vars[:split_point]
|
359 |
+
right_vars = eqn_vars[split_point:]
|
360 |
+
|
361 |
+
left_weights = eqn_weights[:split_point]
|
362 |
+
right_weights = eqn_weights[split_point:]
|
363 |
+
|
364 |
+
left_biases = eqn_biases[:split_point]
|
365 |
+
right_biases = eqn_biases[split_point:]
|
366 |
+
|
367 |
+
left_side = evaluate_eqn_list_on_multidim_datum([left_ops, left_vars, left_weights, left_biases], input_x)
|
368 |
+
right_side = evaluate_eqn_list_on_multidim_datum([right_ops, right_vars, right_weights, right_biases], input_x)
|
369 |
+
|
370 |
+
if current_op == 'id':
|
371 |
+
return left_side
|
372 |
+
|
373 |
+
if current_op == 'sqrt':
|
374 |
+
return np.sqrt(np.abs(left_side))
|
375 |
+
|
376 |
+
if current_op == 'log':
|
377 |
+
return np.log(np.sqrt(left_side * left_side + 1e-10))
|
378 |
+
|
379 |
+
if current_op == 'sin':
|
380 |
+
return np.sin(left_side)
|
381 |
+
|
382 |
+
if current_op == 'exp':
|
383 |
+
return np.exp(left_side)
|
384 |
+
|
385 |
+
if current_op == 'add':
|
386 |
+
return left_side + right_side
|
387 |
+
|
388 |
+
if current_op == 'mul':
|
389 |
+
return left_side * right_side
|
390 |
+
|
391 |
+
if current_op == 'sub':
|
392 |
+
return left_side - right_side
|
393 |
+
|
394 |
+
if current_op == 'div':
|
395 |
+
return safe_div(left_side, right_side)
|
396 |
+
return None
|
397 |
+
|
398 |
+
|
399 |
+
|
400 |
+
def choices_to_init_weight_matrix(choice_list, all_choices):
|
401 |
+
init_weight_matrix = np.zeros(shape=[len(choice_list), len(all_choices)])
|
402 |
+
for row in range(len(choice_list)):
|
403 |
+
if choice_list[row] in all_choices:
|
404 |
+
init_weight_matrix[row][all_choices.index(choice_list[row])] = Settings.init_weight_value
|
405 |
+
elif isinstance(choice_list[row], str) and "not" in choice_list[row] and choice_list[row].index("not") == 0 and \
|
406 |
+
choice_list[row][len("not"):] in all_choices:
|
407 |
+
init_weight_matrix[row][all_choices.index(choice_list[row][len("not"):])] = -100
|
408 |
+
return init_weight_matrix.T
|
409 |
+
|
410 |
+
|
411 |
+
def predict_from_formula(formula_str, x_values):
|
412 |
+
if Settings.num_features == 1:
|
413 |
+
x_variables = [["x"]]
|
414 |
+
else:
|
415 |
+
x_variables = [["x{}".format(var_i + 1) for var_i in range(Settings.num_features)]]
|
416 |
+
f = lambdify(x_variables, formula_str, 'numpy')
|
417 |
+
if isinstance(x_values, list):
|
418 |
+
return [f(x_values[row_i]) for row_i in range(len(x_values))]
|
419 |
+
elif len(x_values.shape) == 2:
|
420 |
+
return [f(x_values[row_i, :]) for row_i in range(x_values.shape[0])]
|
421 |
+
else:
|
422 |
+
return [f(x_values[row_i, :, :].reshape([-1, 1])) for row_i in range(x_values.shape[0])]
|
423 |
+
|
424 |
+
|
425 |
+
def leaves_up_from_dfs_order(orig_list, num_layers):
|
426 |
+
if num_layers <= 1:
|
427 |
+
return orig_list
|
428 |
+
index_list = [[i, i+1] for i in range(0, int(2**(num_layers-1)), 2)]
|
429 |
+
|
430 |
+
# print("start with: {}".format(index_list))
|
431 |
+
last_value=index_list[-1][-1]
|
432 |
+
while len(index_list) > 1:
|
433 |
+
new_index_list = []
|
434 |
+
for j in range(int(len(index_list) / 2)):
|
435 |
+
last_value += 1
|
436 |
+
new_list = [last_value]
|
437 |
+
new_list.extend(index_list[2*j])
|
438 |
+
|
439 |
+
last_value += 1
|
440 |
+
new_list.append(last_value)
|
441 |
+
new_list.extend(index_list[2*j+1])
|
442 |
+
new_index_list.append(new_list)
|
443 |
+
index_list = new_index_list
|
444 |
+
|
445 |
+
# print(" index list is now: {}".format(index_list))
|
446 |
+
final_index_list = [last_value + 1]
|
447 |
+
final_index_list.extend(index_list[0])
|
448 |
+
|
449 |
+
ret_val = [i for i in range(len(final_index_list))]
|
450 |
+
for i in range(len(final_index_list)):
|
451 |
+
ret_val[final_index_list[i]] = orig_list[i]
|
452 |
+
return ret_val # [orig_list[i] for i in final_index_list]
|
453 |
+
|
454 |
+
|
455 |
+
def simplify_formula(formula_to_simplify, digits=None):
|
456 |
+
if len("{}".format(formula_to_simplify)) > 1500:
|
457 |
+
return "{}".format(expand(formula_to_simplify))
|
458 |
+
orig_form_str = sympify(formula_to_simplify)
|
459 |
+
if len("{}".format(orig_form_str)) > 1000:
|
460 |
+
return "{}".format(expand(orig_form_str))
|
461 |
+
|
462 |
+
if len("{}".format(orig_form_str)) < 700:
|
463 |
+
# orig_form_str = simplify(expand(orig_form_str))
|
464 |
+
orig_form_str = expand(orig_form_str)
|
465 |
+
|
466 |
+
rounded = orig_form_str
|
467 |
+
for a in sympy.preorder_traversal(orig_form_str):
|
468 |
+
if isinstance(a, sympy.Float):
|
469 |
+
if digits is not None:
|
470 |
+
if np.abs(a) < 10**(-1*digits):
|
471 |
+
rounded = rounded.subs(a, 0)
|
472 |
+
else:
|
473 |
+
rounded = rounded.subs(a, round(a, digits))
|
474 |
+
elif np.abs(a) < Settings.big_eps:
|
475 |
+
rounded = rounded.subs(a, 0)
|
476 |
+
|
477 |
+
return "{}".format(rounded)
|
478 |
+
|
479 |
+
|
480 |
+
def eqn_to_str_two_list(eqn_as_list, var_y_index=9999, unary_use_both=False):
|
481 |
+
eqn_ops = eqn_as_list[0]
|
482 |
+
eqn_vars = eqn_as_list[1]
|
483 |
+
current_op = eqn_ops[0]
|
484 |
+
# print("eqn_to_str:")
|
485 |
+
# print(eqn_ops)
|
486 |
+
# print(eqn_vars)
|
487 |
+
|
488 |
+
if len(eqn_ops) == 1:
|
489 |
+
|
490 |
+
if type(eqn_vars[0]) is list:
|
491 |
+
left_side = "{:.3f}".format(eqn_vars[0][0])
|
492 |
+
elif eqn_vars[0] == var_y_index:
|
493 |
+
left_side = "y"
|
494 |
+
else:
|
495 |
+
left_side = "x{}".format(eqn_vars[0])
|
496 |
+
if type(eqn_vars[1]) is list:
|
497 |
+
right_side = "{:.3f}".format(eqn_vars[1][0])
|
498 |
+
elif eqn_vars[1] == var_y_index:
|
499 |
+
right_side = "y"
|
500 |
+
else:
|
501 |
+
right_side = "x{}".format(eqn_vars[1])
|
502 |
+
|
503 |
+
|
504 |
+
else:
|
505 |
+
split_point = int((len(eqn_ops) + 1) / 2)
|
506 |
+
left_ops = eqn_ops[1:split_point]
|
507 |
+
right_ops = eqn_ops[split_point:]
|
508 |
+
|
509 |
+
left_vars = eqn_vars[:split_point]
|
510 |
+
right_vars = eqn_vars[split_point:]
|
511 |
+
|
512 |
+
left_side = eqn_to_str_two_list([left_ops, left_vars])
|
513 |
+
right_side = eqn_to_str_two_list([right_ops, right_vars])
|
514 |
+
|
515 |
+
left_is_float = False
|
516 |
+
right_is_float = False
|
517 |
+
left_value = np.nan
|
518 |
+
right_value = np.nan
|
519 |
+
|
520 |
+
if is_float(left_side):
|
521 |
+
left_value = float(left_side)
|
522 |
+
left_is_float = True
|
523 |
+
if is_float(right_side):
|
524 |
+
right_value = float(right_side)
|
525 |
+
right_is_float = True
|
526 |
+
|
527 |
+
if current_op == 'id':
|
528 |
+
return left_side
|
529 |
+
|
530 |
+
if current_op == 'sqrt':
|
531 |
+
if left_is_float:
|
532 |
+
return "{:.3f}".format(np.sqrt(np.abs(left_value)))
|
533 |
+
return "sqrt({})".format(left_side)
|
534 |
+
|
535 |
+
if current_op == 'log':
|
536 |
+
if left_is_float:
|
537 |
+
return "{:.3f}".format(np.math.log(safe_abs(left_value)))
|
538 |
+
return "log({})".format(left_side)
|
539 |
+
|
540 |
+
if current_op == 'sin':
|
541 |
+
if left_is_float:
|
542 |
+
return "{:.3f}".format(np.sin(left_value))
|
543 |
+
return "sin({})".format(left_side)
|
544 |
+
|
545 |
+
if current_op == 'exp':
|
546 |
+
if left_is_float:
|
547 |
+
return "{:.3f}".format(np.exp(left_value))
|
548 |
+
return "exp({})".format(left_side)
|
549 |
+
|
550 |
+
if current_op == 'add':
|
551 |
+
if left_is_float and right_is_float:
|
552 |
+
return "{:.3f}".format(left_value + right_value)
|
553 |
+
return "({} + {})".format(left_side, right_side)
|
554 |
+
|
555 |
+
if current_op == 'mul':
|
556 |
+
if left_is_float and right_is_float:
|
557 |
+
return "{:.3f}".format(left_value * right_value)
|
558 |
+
return "({} * {})".format(left_side, right_side)
|
559 |
+
|
560 |
+
if current_op == 'sub':
|
561 |
+
if left_is_float and right_is_float:
|
562 |
+
return "{:.3f}".format(left_value - right_value)
|
563 |
+
return "({} - {})".format(left_side, right_side)
|
564 |
+
|
565 |
+
if current_op == 'div':
|
566 |
+
if left_is_float and right_is_float:
|
567 |
+
return "{:.3f}".format(safe_div(left_value, right_value))
|
568 |
+
return "({} / {})".format(left_side, right_side)
|
569 |
+
|
570 |
+
return None
|
571 |
+
|
572 |
+
|
573 |
+
def eqn_to_str(eqn_as_list, var_y_index=9999, unary_use_both=False):
|
574 |
+
eqn_ops = eqn_as_list[0]
|
575 |
+
eqn_vars = eqn_as_list[1]
|
576 |
+
eqn_weights = eqn_as_list[2]
|
577 |
+
eqn_biases = eqn_as_list[3]
|
578 |
+
current_op = eqn_ops[0]
|
579 |
+
# print("eqn_to_str:")
|
580 |
+
# print(eqn_ops)
|
581 |
+
# print(eqn_vars)
|
582 |
+
|
583 |
+
if len(eqn_ops) == 1:
|
584 |
+
|
585 |
+
if eqn_vars[0] == var_y_index:
|
586 |
+
left_side = "y"
|
587 |
+
else:
|
588 |
+
left_side = "({} * x{} + {})".format(eqn_weights[0], eqn_vars[0], eqn_biases[0])
|
589 |
+
if eqn_vars[1] == var_y_index:
|
590 |
+
right_side = "y"
|
591 |
+
else:
|
592 |
+
right_side = "({} * x{} + {})".format(eqn_weights[1], eqn_vars[1], eqn_biases[1])
|
593 |
+
|
594 |
+
|
595 |
+
else:
|
596 |
+
split_point = int((len(eqn_ops) + 1) / 2)
|
597 |
+
left_ops = eqn_ops[1:split_point]
|
598 |
+
right_ops = eqn_ops[split_point:]
|
599 |
+
|
600 |
+
left_vars = eqn_vars[:split_point]
|
601 |
+
right_vars = eqn_vars[split_point:]
|
602 |
+
|
603 |
+
left_weights = eqn_weights[:split_point]
|
604 |
+
right_weights = eqn_weights[split_point:]
|
605 |
+
|
606 |
+
left_biases = eqn_biases[:split_point]
|
607 |
+
right_biases = eqn_biases[split_point:]
|
608 |
+
|
609 |
+
left_side = eqn_to_str([left_ops, left_vars, left_weights, left_biases])
|
610 |
+
right_side = eqn_to_str([right_ops, right_vars, right_weights, right_biases])
|
611 |
+
|
612 |
+
left_is_float = False
|
613 |
+
right_is_float = False
|
614 |
+
left_value = np.nan
|
615 |
+
right_value = np.nan
|
616 |
+
|
617 |
+
if is_float(left_side):
|
618 |
+
left_value = float(left_side)
|
619 |
+
left_is_float = True
|
620 |
+
if is_float(right_side):
|
621 |
+
right_value = float(right_side)
|
622 |
+
right_is_float = True
|
623 |
+
|
624 |
+
if current_op == 'id':
|
625 |
+
return left_side
|
626 |
+
|
627 |
+
if current_op == 'sqrt':
|
628 |
+
if left_is_float:
|
629 |
+
return "{:.3f}".format(np.sqrt(np.abs(left_value)))
|
630 |
+
return "sqrt({})".format(left_side)
|
631 |
+
|
632 |
+
if current_op == 'log':
|
633 |
+
if left_is_float:
|
634 |
+
return "{:.3f}".format(np.math.log(safe_abs(left_value)))
|
635 |
+
return "log({})".format(left_side)
|
636 |
+
|
637 |
+
if current_op == 'sin':
|
638 |
+
if left_is_float:
|
639 |
+
return "{:.3f}".format(np.sin(left_value))
|
640 |
+
return "sin({})".format(left_side)
|
641 |
+
|
642 |
+
if current_op == 'exp':
|
643 |
+
if left_is_float:
|
644 |
+
return "{:.3f}".format(np.exp(left_value))
|
645 |
+
return "exp({})".format(left_side)
|
646 |
+
|
647 |
+
if current_op == 'add':
|
648 |
+
if left_is_float and right_is_float:
|
649 |
+
return "{:.3f}".format(left_value + right_value)
|
650 |
+
return "({} + {})".format(left_side, right_side)
|
651 |
+
|
652 |
+
if current_op == 'mul':
|
653 |
+
if left_is_float and right_is_float:
|
654 |
+
return "{:.3f}".format(left_value * right_value)
|
655 |
+
return "({} * {})".format(left_side, right_side)
|
656 |
+
|
657 |
+
if current_op == 'sub':
|
658 |
+
if left_is_float and right_is_float:
|
659 |
+
return "{:.3f}".format(left_value - right_value)
|
660 |
+
return "({} - {})".format(left_side, right_side)
|
661 |
+
|
662 |
+
if current_op == 'div':
|
663 |
+
if left_is_float and right_is_float:
|
664 |
+
return "{:.3f}".format(safe_div(left_value, right_value))
|
665 |
+
return "({} / {})".format(left_side, right_side)
|
666 |
+
|
667 |
+
return None
|
668 |
+
|
669 |
+
def simple_eqn_to_str(eqn_as_list, var_y_index=9999):
|
670 |
+
return simplify_formula(eqn_to_str(eqn_as_list, var_y_index=var_y_index))
|
671 |
+
|
672 |
+
|
673 |
+
def get_samples(n_train, n_batch, train_x, train_y):
|
674 |
+
both_samples = np.random.choice(n_train, size=2*n_batch, replace=False)
|
675 |
+
|
676 |
+
sample = both_samples[:n_batch]
|
677 |
+
valid_sample = both_samples[n_batch:]
|
678 |
+
|
679 |
+
mini_batch_train_data_x = train_x[sample][:][:]
|
680 |
+
mini_batch_train_data_y = train_y[sample][:][:]
|
681 |
+
|
682 |
+
mini_valid_sample_x = train_x[valid_sample][:][:]
|
683 |
+
mini_valid_sample_y = train_y[valid_sample][:][:]
|
684 |
+
return mini_batch_train_data_x, mini_batch_train_data_y, mini_valid_sample_x, mini_valid_sample_y
|
685 |
+
|
686 |
+
|
687 |
+
###############################################
|
688 |
+
#
|
689 |
+
# Plotting functions
|
690 |
+
#
|
691 |
+
###############################################
|
692 |
+
|
693 |
+
|
694 |
+
def plot_1d_curve(train_x, train_y_true, train_y_pred, test_x, test_y_true, test_y_pred,
|
695 |
+
title="", file_suffix="", show_ground_truth=True):
|
696 |
+
plt.figure()
|
697 |
+
plt.title(title)
|
698 |
+
if show_ground_truth:
|
699 |
+
plt.scatter(train_x, train_y_true, color='gray', alpha=0.5, marker='.', label='Ground truth')
|
700 |
+
if test_x is not None:
|
701 |
+
plt.scatter(test_x, test_y_true, color='gray', alpha=0.5, marker='.')
|
702 |
+
|
703 |
+
if test_x is not None:
|
704 |
+
plt.scatter(test_x, test_y_pred, color='red', alpha=0.7, marker='.', label='Model (test set)')
|
705 |
+
plt.scatter(train_x, train_y_pred, color='blue', alpha=0.7, marker='.', label='Model (train set)')
|
706 |
+
|
707 |
+
for xc in Settings.train_scope:
|
708 |
+
plt.axvline(x=xc, color='k', linestyle='dashed', linewidth=2)
|
709 |
+
|
710 |
+
plt.xlabel("x")
|
711 |
+
plt.ylabel("y")
|
712 |
+
|
713 |
+
plt.legend()
|
714 |
+
plt.savefig("images/true_vs_pred_curve{}.png".format(file_suffix))
|
715 |
+
plt.close()
|
716 |
+
|
717 |
+
|
718 |
+
def plot_2d_curve(x_1, x_2, y, g,
|
719 |
+
title=""):
|
720 |
+
|
721 |
+
fig = plt.figure(figsize=(11, 5))
|
722 |
+
# ax = fig.gca(projection='3d')
|
723 |
+
ax = fig.add_subplot(1, 2, 1, projection='3d')
|
724 |
+
plt.title("Learned function")
|
725 |
+
|
726 |
+
surf = ax.plot_surface(x_1, x_2, y, cmap=cm.coolwarm,
|
727 |
+
linewidth=0, antialiased=False, label="Ground truth")
|
728 |
+
|
729 |
+
|
730 |
+
plt.xlabel("x")
|
731 |
+
plt.ylabel("y")
|
732 |
+
fig.colorbar(surf, shrink=0.5, aspect=5)
|
733 |
+
|
734 |
+
ax = fig.add_subplot(1, 2, 2, projection='3d')
|
735 |
+
plt.title("Residual (g)")
|
736 |
+
|
737 |
+
surf = ax.plot_surface(x_1, x_2, g, cmap=cm.coolwarm,
|
738 |
+
linewidth=0, antialiased=False, label="Ground truth")
|
739 |
+
|
740 |
+
plt.xlabel("x")
|
741 |
+
plt.ylabel("y")
|
742 |
+
fig.colorbar(surf, shrink=0.5, aspect=5)
|
743 |
+
|
744 |
+
# plt.show()
|
745 |
+
|
746 |
+
# plt.legend()
|
747 |
+
plt.savefig("images/pred_g_2d.png")
|
748 |
+
plt.close()
|
749 |
+
|
750 |
+
|
751 |
+
def plot_predicted_vs_actual(pred_y_train, true_y_train,
|
752 |
+
pred_y_test=None, true_y_test=None,
|
753 |
+
model_name="Model", set_name="", show=False):
|
754 |
+
plt.figure()
|
755 |
+
if set_name != "":
|
756 |
+
set_name = "({})".format(set_name)
|
757 |
+
plt.title('{}: Predicted vs. Actual {}'.format(model_name, set_name))
|
758 |
+
|
759 |
+
plt.scatter(true_y_train, true_y_train, color='gray', alpha=0.5, marker='.', label='Ground truth')
|
760 |
+
if pred_y_test is not None:
|
761 |
+
plt.scatter(true_y_test, true_y_test, color='gray', alpha=0.5, marker='.')
|
762 |
+
|
763 |
+
if pred_y_test is not None:
|
764 |
+
plt.scatter(true_y_test, pred_y_test, color="red", alpha=0.6, marker='.', label="{}: Test".format(model_name))
|
765 |
+
|
766 |
+
plt.scatter(true_y_train, pred_y_train, color="blue", alpha=0.7, marker='.', label="{}: Train".format(model_name))
|
767 |
+
|
768 |
+
plt.ylabel("Observed")
|
769 |
+
plt.xlabel("Expected")
|
770 |
+
plt.legend()
|
771 |
+
|
772 |
+
plt.savefig("images/single_predicted_vs_actual.png")
|
773 |
+
if show:
|
774 |
+
plt.show()
|
775 |
+
plt.close()
|
776 |
+
|
777 |
+
|
778 |
+
# acc_logs: list of accuracy logs to be plotted.
|
779 |
+
def plot_accuracy_over_time(iters_log, acc_logs, error_types):
|
780 |
+
if len(iters_log) < 2:
|
781 |
+
return
|
782 |
+
for i in range(len(acc_logs)):
|
783 |
+
plt.plot(iters_log[1:], acc_logs[i][1:], label=error_types[i])
|
784 |
+
plt.xlabel("Iteration")
|
785 |
+
plt.ylabel("Error Scores")
|
786 |
+
plt.yscale('log')
|
787 |
+
plt.legend()
|
788 |
+
plt.savefig("images/accuracy_log.png")
|
789 |
+
plt.close()
|
790 |
+
|
791 |
+
|
792 |
+
def plot_hist_of_errors(lists_of_error_scores, all_models, num_trials):
|
793 |
+
plt.figure()
|
794 |
+
plt.hist([np.log(errors_i) for errors_i in lists_of_error_scores],
|
795 |
+
label=[model.short_name for model in all_models])
|
796 |
+
plt.legend()
|
797 |
+
plt.title("Comparing errors of all methods over {} equations".format(num_trials))
|
798 |
+
plt.xlabel("Log of error")
|
799 |
+
plt.ylabel("Frequency")
|
800 |
+
plt.savefig("images/hist_of_errors.png")
|
801 |
+
plt.close()
|
FeynmanEqns.txt
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# eqn #; # levels; # vars; eqn str; true ops list
|
2 |
+
ballot; 1; 2; (x1-x2)/(x1+x2); ["div"]
|
3 |
+
sum; 1; 2; x1 + x2; ["id"]
|
4 |
+
range; 2; 2; x1**2 * sin(2*x2) / 9.81; ["mul", "sin", "mul"]
|
5 |
+
mm
|
6 |
+
I.11.19; 1; 6; x1*x2 + x3*x4 + x5*x6; ["mul"]
|
7 |
+
mmm
|
8 |
+
kin1; 2; 3; x1*x2 + 0.5 * x3 * x2**2; ["mul", "mul", "mul"]
|
9 |
+
mm
|
10 |
+
mmm
|
11 |
+
collision1; 2; 4; (2*x1*x3 - (x1-x2)*x4) / (x1 + x2); ["mul", "id", "div"]
|
12 |
+
mmm
|
13 |
+
I.12.1; 2; 3; x1 * x2 / (4 * pi * x3**2 / 137); ["mul", "mul", "div"]
|
14 |
+
I.10.7a; 3; 2; x1/sqrt(1-x2**2); ["id", "id", "mul", "id", "id", "sqrt", "div"]
|
15 |
+
mmm
|
16 |
+
I.8.14; 2; 4; sqrt((x2-x1)**2 +(x4-x3)**2); ["mul", "mul", "sqrt"]
|
17 |
+
I.6.20a; 2; 2; exp(-x1**2)/sqrt(2*pi); ["mul", "id", "exp"]
|
18 |
+
I.29.16; 3; 3; sqrt(x1**2 + x2**2 - 2*x1*x2*cos(x3)); ["mul", "sin", "mul", "id", "mul", "id", "sqrt"]
|
19 |
+
AreaTriangle; 2; 3; x1*x2*sin(x3)/2; ["mul", "sin", "mul"]
|
LearnRules.py
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import time
|
2 |
+
|
3 |
+
import numpy as np
|
4 |
+
|
5 |
+
import LearnTriangles
|
6 |
+
import Settings as settings
|
7 |
+
from SymbolicFunctionLearner import SFL
|
8 |
+
|
9 |
+
num_triangles = 1000
|
10 |
+
num_fake = 3000
|
11 |
+
max_domain = 5
|
12 |
+
num_trials = 500
|
13 |
+
num_smp_features = 2
|
14 |
+
settings.show_output = True
|
15 |
+
settings.keep_logs = True
|
16 |
+
|
17 |
+
settings.mode = "lr"
|
18 |
+
# settings.initialize_ops = ["mul", "mul", "mul", "mul", "id", "id", "id"]
|
19 |
+
# settings.initialize_ops = ["mul", "mul", "id"]
|
20 |
+
|
21 |
+
|
22 |
+
"""" These are the things to change"""
|
23 |
+
var_names = LearnTriangles.get_xy_var_names()
|
24 |
+
|
25 |
+
# real_data = LearnTriangles.get_right_triangle_data(num_triangles, max_domain)
|
26 |
+
# real_data = LearnTriangles.get_angle_data(num_triangles)
|
27 |
+
real_data = LearnTriangles.get_xy_data(num_triangles, max_domain)
|
28 |
+
real_y = [1 for v in real_data]
|
29 |
+
|
30 |
+
fake_data = LearnTriangles.get_fake_xy_data(num_triangles, max_domain)
|
31 |
+
# fake_data = LearnTriangles.get_fake_angles(num_triangles)
|
32 |
+
# fake_data = LearnTriangles.get_triangle_data(num_triangles, max_domain)
|
33 |
+
fake_y = [0 for _ in fake_data]
|
34 |
+
|
35 |
+
# real_test_data = LearnTriangles.get_right_triangle_data(num_triangles, max_domain * 2)
|
36 |
+
# real_test_data = LearnTriangles.get_angle_data(num_triangles)
|
37 |
+
real_test_data = LearnTriangles.get_xy_data(num_triangles, max_domain)
|
38 |
+
real_test_y = [1 for v in real_test_data]
|
39 |
+
|
40 |
+
# fake_test_data = LearnTriangles.get_triangle_data(num_triangles, max_domain * 2)
|
41 |
+
# fake_test_data = LearnTriangles.get_fake_angles(num_triangles)
|
42 |
+
fake_test_data = LearnTriangles.get_fake_xy_data(num_triangles, max_domain)
|
43 |
+
fake_test_y = [0 for _ in fake_test_data]
|
44 |
+
|
45 |
+
""" Don't change after this """
|
46 |
+
|
47 |
+
print("real data: ")
|
48 |
+
for r_d in real_data[:5]:
|
49 |
+
print(r_d)
|
50 |
+
print("real y:")
|
51 |
+
for r_d in real_y[:5]:
|
52 |
+
print(r_d)
|
53 |
+
print("fake data:")
|
54 |
+
for r_d in fake_data[:5]:
|
55 |
+
print(r_d)
|
56 |
+
print("fake y:")
|
57 |
+
for r_d in fake_y[:5]:
|
58 |
+
print(r_d)
|
59 |
+
|
60 |
+
full_data = real_data.copy()
|
61 |
+
full_data.extend(fake_data)
|
62 |
+
full_labels = real_y.copy()
|
63 |
+
full_labels.extend(fake_y)
|
64 |
+
|
65 |
+
full_test_data = real_test_data.copy()
|
66 |
+
full_test_data.extend(fake_test_data)
|
67 |
+
full_test_labels = real_test_y.copy()
|
68 |
+
full_test_labels.extend(fake_test_y)
|
69 |
+
|
70 |
+
# print("full data:\n{}".format(full_data))
|
71 |
+
# print("full y:\n{}".format(full_labels))
|
72 |
+
#
|
73 |
+
# for datum in real_data:
|
74 |
+
# print(datum[0]*datum[4] - datum[1]*datum[3])
|
75 |
+
|
76 |
+
|
77 |
+
our_results = []
|
78 |
+
|
79 |
+
settings.true_eqn = "0*x1"
|
80 |
+
settings.num_features = num_smp_features
|
81 |
+
|
82 |
+
model = SFL()
|
83 |
+
|
84 |
+
for trial_round in range(num_trials):
|
85 |
+
sampled_features = np.random.choice(range(len(real_data[0])), num_smp_features, replace=True)
|
86 |
+
|
87 |
+
sampled_features = [0, 1]
|
88 |
+
|
89 |
+
data = [[row[smp_i] for smp_i in sampled_features] for row in full_data]
|
90 |
+
test_data = [[row[smp_i] for smp_i in sampled_features] for row in full_test_data]
|
91 |
+
|
92 |
+
smp_var_names = [var_names[smp_i] for smp_i in sampled_features]
|
93 |
+
print("Trial round {} of {}.".format(trial_round + 1, num_trials))
|
94 |
+
print(" Using variables {}.".format(smp_var_names))
|
95 |
+
|
96 |
+
settings.fixed_x = []
|
97 |
+
settings.fixed_y = []
|
98 |
+
for line in data:
|
99 |
+
settings.fixed_x.append(line)
|
100 |
+
settings.fixed_y = full_labels
|
101 |
+
|
102 |
+
# print("fixed_x: {}, {}".format(len(settings.fixed_x), len(settings.fixed_x[0])))
|
103 |
+
# print("fixed_y: {}".format(len(settings.fixed_y)))
|
104 |
+
|
105 |
+
model.reset(var_names=smp_var_names)
|
106 |
+
|
107 |
+
# train_X = DataUtils.generate_data(settings.train_N, n_vars=model.n_input_variables,
|
108 |
+
# avoid_zero=settings.avoid_zero)
|
109 |
+
# valid_X = DataUtils.generate_data(settings.train_N, n_vars=model.n_input_variables,
|
110 |
+
# avoid_zero=settings.avoid_zero)
|
111 |
+
# test_X = DataUtils.generate_data(settings.test_N, n_vars=model.n_input_variables,
|
112 |
+
# min_x=settings.test_scope[0],
|
113 |
+
# max_x=settings.test_scope[1])
|
114 |
+
train_X = np.array(data)
|
115 |
+
train_Y = full_labels
|
116 |
+
|
117 |
+
test_X = np.array(test_data)
|
118 |
+
test_Y = full_test_labels
|
119 |
+
|
120 |
+
train_X = train_X.reshape([-1, settings.num_dims_per_feature, settings.num_features])
|
121 |
+
test_X = test_X.reshape([-1, settings.num_dims_per_feature, settings.num_features])
|
122 |
+
|
123 |
+
start_time = time.time()
|
124 |
+
best_model, best_iter, best_err = model.repeat_train(train_X, train_Y,
|
125 |
+
settings.num_train_repeat_processes,
|
126 |
+
test_x=test_X, test_y=test_Y)
|
127 |
+
running_time = time.time() - start_time
|
128 |
+
|
129 |
+
print("best_model: {}".format(best_model))
|
130 |
+
print("----------------------")
|
131 |
+
print("Finished this experiment. Took {:.2f} minutes.\n".format(running_time / 60))
|
132 |
+
|
133 |
+
our_results.append([best_err, best_model, smp_var_names])
|
134 |
+
our_results = sorted(our_results, key=lambda entry: entry[0])
|
135 |
+
|
136 |
+
output_file = open("images/triangle_output.txt", "w")
|
137 |
+
for entry in our_results:
|
138 |
+
output_file.write("{}\n{}\n{}\n\n".format(entry[0], entry[2], entry[1]))
|
139 |
+
output_file.close()
|
140 |
+
|
141 |
+
print("Final solution found at attempt {}:".format(best_iter))
|
142 |
+
print("y = {}".format(best_model))
|
143 |
+
print("Test error: {}".format(best_err))
|
144 |
+
if best_err < 0.02:
|
145 |
+
print("Attained error less than 0.02 - great!")
|
146 |
+
print()
|
LearnTriangles.py
ADDED
@@ -0,0 +1,252 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
|
3 |
+
|
4 |
+
def get_simple_data(num_points, max_domain):
|
5 |
+
full_data = []
|
6 |
+
for i in range(num_points):
|
7 |
+
new_datum = [np.random.uniform(0, max_domain),
|
8 |
+
np.random.uniform(0, max_domain),
|
9 |
+
np.random.uniform(0, max_domain)]
|
10 |
+
full_data.append(new_datum)
|
11 |
+
return full_data
|
12 |
+
|
13 |
+
|
14 |
+
def get_simple_var_names():
|
15 |
+
return ["a", "b", "c"]
|
16 |
+
|
17 |
+
|
18 |
+
def get_xy_var_names():
|
19 |
+
return ["x", "y"]
|
20 |
+
|
21 |
+
|
22 |
+
def get_angle_data(num_data):
|
23 |
+
full_data = []
|
24 |
+
for i in range(num_data):
|
25 |
+
theta = np.random.uniform(0, 2 * np.pi)
|
26 |
+
while np.abs(theta - np.pi / 2) < 1e-3 or np.abs(theta - 3 * np.pi / 2) < 1e-3:
|
27 |
+
theta = np.random.uniform(0, 2 * np.pi)
|
28 |
+
new_datum = [np.sin(theta), np.cos(theta), np.tan(theta)]
|
29 |
+
full_data.append(new_datum)
|
30 |
+
return full_data
|
31 |
+
|
32 |
+
|
33 |
+
def get_fake_angles(num_data):
|
34 |
+
full_data = []
|
35 |
+
for i in range(num_data):
|
36 |
+
[s, c] = np.random.uniform(-1, 1, 2)
|
37 |
+
t = np.random.uniform(-5, 5)
|
38 |
+
new_datum = [s, c, t]
|
39 |
+
full_data.append(new_datum)
|
40 |
+
return full_data
|
41 |
+
|
42 |
+
|
43 |
+
def get_xy_data(num_data, max_domain):
|
44 |
+
full_data = []
|
45 |
+
for i in range(num_data):
|
46 |
+
# t = np.random.uniform(0, 2 * np.pi)
|
47 |
+
# r = 2 + 3 * np.cos(t)
|
48 |
+
# # r = 5 * np.cos(2*t)/np.cos(t)
|
49 |
+
#
|
50 |
+
# x = r * np.cos(t)
|
51 |
+
# y = r * np.sin(t)
|
52 |
+
|
53 |
+
y = np.random.uniform(-3, 2)
|
54 |
+
sign = np.random.choice([-1, 1])
|
55 |
+
x = sign * np.sqrt(np.abs(2 * np.sin(y) + 5 - y ** 3))
|
56 |
+
full_data.append([x, y])
|
57 |
+
return full_data
|
58 |
+
|
59 |
+
|
60 |
+
def get_fake_xy_data(num_data, max_domain):
|
61 |
+
full_data = []
|
62 |
+
for i in range(num_data):
|
63 |
+
[x, y] = np.random.uniform(0, max_domain, 2)
|
64 |
+
full_data.append([x, y])
|
65 |
+
return full_data
|
66 |
+
|
67 |
+
|
68 |
+
|
69 |
+
def get_triangle_data(num_triangles, max_domain):
|
70 |
+
full_data = []
|
71 |
+
for i in range(num_triangles):
|
72 |
+
[x1, y1, x2, y2, x3, y3] = np.random.uniform(0, max_domain, 6)
|
73 |
+
|
74 |
+
a = np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
|
75 |
+
b = np.sqrt((x3 - x2) ** 2 + (y3 - y2) ** 2)
|
76 |
+
c = np.sqrt((x1 - x3) ** 2 + (y1 - y3) ** 2)
|
77 |
+
|
78 |
+
va = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c))
|
79 |
+
vb = np.arccos((c ** 2 + a ** 2 - b ** 2) / (2 * a * c))
|
80 |
+
vc = np.arccos((a ** 2 + b ** 2 - c ** 2) / (2 * b * a))
|
81 |
+
|
82 |
+
sa, sb, sc = np.sin([va, vb, vc])
|
83 |
+
ca, cb, cc = np.cos([va, vb, vc])
|
84 |
+
ta, tb, tc = np.tan([va, vb, vc])
|
85 |
+
|
86 |
+
new_datum = []
|
87 |
+
# new_datum.extend([x1, x2, x3])
|
88 |
+
# new_datum.extend([y1, y2, y3])
|
89 |
+
new_datum.extend([a, b, c])
|
90 |
+
# new_datum.extend([va, vb, vc])
|
91 |
+
# new_datum.extend([sa, sb, sc])
|
92 |
+
# new_datum.extend([ca, cb, cc])
|
93 |
+
# new_datum.extend([ta, tb, tc])
|
94 |
+
|
95 |
+
full_data.append(new_datum)
|
96 |
+
return full_data
|
97 |
+
|
98 |
+
|
99 |
+
def get_right_triangle_data(num_triangles, max_domain):
|
100 |
+
full_data = []
|
101 |
+
for i in range(num_triangles):
|
102 |
+
[s1, s2] = np.random.uniform(0, max_domain, 2)
|
103 |
+
c = max(s1, s2)
|
104 |
+
a = min(s1, s2)
|
105 |
+
b = np.sqrt(c * c - a * a)
|
106 |
+
|
107 |
+
va = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c))
|
108 |
+
vb = np.arccos((c ** 2 + a ** 2 - b ** 2) / (2 * a * c))
|
109 |
+
vc = np.arccos((a ** 2 + b ** 2 - c ** 2) / (2 * b * a))
|
110 |
+
|
111 |
+
sa, sb, sc = np.sin([va, vb, vc])
|
112 |
+
ca, cb, cc = np.cos([va, vb, vc])
|
113 |
+
ta, tb, tc = np.tan([va, vb, vc])
|
114 |
+
|
115 |
+
new_datum = []
|
116 |
+
new_datum.extend([a, b, c])
|
117 |
+
# new_datum.extend([sa, sb, sc])
|
118 |
+
# new_datum.extend([ca, cb, cc])
|
119 |
+
# new_datum.extend([ta, tb, tc])
|
120 |
+
|
121 |
+
full_data.append(new_datum)
|
122 |
+
|
123 |
+
return full_data
|
124 |
+
|
125 |
+
|
126 |
+
def get_fake_triangle_data(num_triangles, max_domain):
|
127 |
+
full_data = []
|
128 |
+
for i in range(num_triangles):
|
129 |
+
[a, b, c] = np.random.uniform(0, max_domain, 3)
|
130 |
+
[sa, sb, sc] = np.random.uniform(-1, 1, 3)
|
131 |
+
[ca, cb, cc] = np.random.uniform(-1, 1, 3)
|
132 |
+
[ta, tb, tc] = np.random.uniform(-5, 5, 3)
|
133 |
+
|
134 |
+
new_datum = []
|
135 |
+
new_datum.extend([a, b, c])
|
136 |
+
# new_datum.extend([va, vb, vc])
|
137 |
+
new_datum.extend([sa, sb, sc])
|
138 |
+
new_datum.extend([ca, cb, cc])
|
139 |
+
new_datum.extend([ta, tb, tc])
|
140 |
+
|
141 |
+
# new_datum.extend([va, vb, vc])
|
142 |
+
# new_datum.extend([a, 2*a])
|
143 |
+
# new_datum.extend([ta, tb, tc])
|
144 |
+
|
145 |
+
full_data.append(new_datum)
|
146 |
+
return full_data
|
147 |
+
|
148 |
+
|
149 |
+
def get_non_triangle_data(num_triangles, max_domain):
|
150 |
+
full_data = []
|
151 |
+
for i in range(num_triangles):
|
152 |
+
[x1, y1, x2, y2, x3, y3, x4, y4] = np.random.uniform(0, max_domain, 8)
|
153 |
+
|
154 |
+
a = np.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
|
155 |
+
b = np.sqrt((x3 - x2) ** 2 + (y3 - y2) ** 2)
|
156 |
+
c = np.sqrt((x4 - x3) ** 2 + (y4 - y3) ** 2)
|
157 |
+
d = np.sqrt((x1 - x4) ** 2 + (y1 - y4) ** 2)
|
158 |
+
e24 = np.sqrt((x4 - x2) ** 2 + (y4 - y2) ** 2)
|
159 |
+
e13 = np.sqrt((x3 - x1) ** 2 + (y3 - y1) ** 2)
|
160 |
+
|
161 |
+
va = np.arccos((b ** 2 + c ** 2 - e24 ** 2) / (2 * b * c))
|
162 |
+
vb = np.arccos((d ** 2 + c ** 2 - e13 ** 2) / (2 * d * c))
|
163 |
+
vc = np.arccos((d ** 2 + a ** 2 - e24 ** 2) / (2 * d * a))
|
164 |
+
|
165 |
+
sa, sb, sc = np.sin([va, vb, vc])
|
166 |
+
ca, cb, cc = np.cos([va, vb, vc])
|
167 |
+
ta, tb, tc = np.tan([va, vb, vc])
|
168 |
+
|
169 |
+
new_datum = []
|
170 |
+
new_datum.extend([a, b, c])
|
171 |
+
# new_datum.extend([va, vb, vc])
|
172 |
+
new_datum.extend([sa, sb, sc])
|
173 |
+
new_datum.extend([ca, cb, cc])
|
174 |
+
# new_datum.extend([ta, tb, tc])
|
175 |
+
full_data.append(new_datum)
|
176 |
+
return full_data
|
177 |
+
|
178 |
+
def get_var_names():
|
179 |
+
# var_names = ["a", "b", "c"]
|
180 |
+
var_names = []
|
181 |
+
var_names.extend(["sin(x)", "cos(x)", "tan(x)"])
|
182 |
+
# var_names.extend(["A", "B", "C"])
|
183 |
+
var_names.extend(["sin(A)", "sin(B)", "sin(C)"])
|
184 |
+
var_names.extend(["cos(A)", "cos(B)", "cos(C)"])
|
185 |
+
var_names.extend(["tan(A)", "tan(B)", "tan(C)"])
|
186 |
+
return var_names
|
187 |
+
#
|
188 |
+
# our_results = []
|
189 |
+
#
|
190 |
+
# settings.true_eqn = "0*x1"
|
191 |
+
# settings.num_features = num_smp_features
|
192 |
+
# settings.show_output = False
|
193 |
+
# settings.keep_logs = False
|
194 |
+
#
|
195 |
+
# model = SFL()
|
196 |
+
#
|
197 |
+
# for trial_round in range(num_trials):
|
198 |
+
# sampled_features = np.random.choice(range(len(full_data[0])), num_smp_features, replace=True)
|
199 |
+
#
|
200 |
+
# # sampled_features = [3,6]
|
201 |
+
# data = [[row[smp_i] for smp_i in sampled_features] for row in full_data]
|
202 |
+
# smp_var_names = [var_names[smp_i] for smp_i in sampled_features]
|
203 |
+
# print("Trial round {} of {}.".format(trial_round + 1, num_trials))
|
204 |
+
# print(" Using variables {}.".format(smp_var_names))
|
205 |
+
#
|
206 |
+
# settings.fixed_x = []
|
207 |
+
# settings.fixed_y = []
|
208 |
+
# for line in data:
|
209 |
+
# settings.fixed_x.append(line)
|
210 |
+
# settings.fixed_y.append(0)
|
211 |
+
#
|
212 |
+
# model.reset(var_names=smp_var_names)
|
213 |
+
#
|
214 |
+
# # train_X = DataUtils.generate_data(settings.train_N, n_vars=model.n_input_variables,
|
215 |
+
# # avoid_zero=settings.avoid_zero)
|
216 |
+
# # valid_X = DataUtils.generate_data(settings.train_N, n_vars=model.n_input_variables,
|
217 |
+
# # avoid_zero=settings.avoid_zero)
|
218 |
+
# # test_X = DataUtils.generate_data(settings.test_N, n_vars=model.n_input_variables,
|
219 |
+
# # min_x=settings.test_scope[0],
|
220 |
+
# # max_x=settings.test_scope[1])
|
221 |
+
# train_X = np.array(data)
|
222 |
+
# valid_X = np.array(data)
|
223 |
+
# test_X = np.array(data)
|
224 |
+
#
|
225 |
+
# train_X = train_X.reshape([-1, settings.num_dims_per_feature, settings.num_features])
|
226 |
+
# valid_X = valid_X.reshape([-1, settings.num_dims_per_feature, settings.num_features])
|
227 |
+
# test_X = test_X.reshape([-1, settings.num_dims_per_feature, settings.num_features])
|
228 |
+
#
|
229 |
+
# start_time = time.time()
|
230 |
+
# best_model, best_iter, best_err = model.repeat_train(train_X,
|
231 |
+
# settings.num_train_repeat_processes,
|
232 |
+
# test_x=test_X)
|
233 |
+
# running_time = time.time() - start_time
|
234 |
+
#
|
235 |
+
# print("best_model: {}".format(best_model))
|
236 |
+
# print("----------------------")
|
237 |
+
# print("Finished this experiment. Took {:.2f} minutes.\n".format(running_time / 60))
|
238 |
+
#
|
239 |
+
# our_results.append([best_err, best_model, smp_var_names])
|
240 |
+
# our_results = sorted(our_results, key=lambda entry: entry[0])
|
241 |
+
#
|
242 |
+
# output_file = open("images/triangle_output.txt", "w")
|
243 |
+
# for entry in our_results:
|
244 |
+
# output_file.write("{}\n{}\n{}\n\n".format(entry[0], entry[2], entry[1]))
|
245 |
+
# output_file.close()
|
246 |
+
#
|
247 |
+
# print("Final solution found at attempt {}:".format(best_iter))
|
248 |
+
# print("y = {}".format(best_model))
|
249 |
+
# print("Test error: {}".format(best_err))
|
250 |
+
# if best_err < 0.02:
|
251 |
+
# print("Attained error less than 0.02 - great!")
|
252 |
+
# print()
|
MLP_Model.py
ADDED
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import time
|
2 |
+
|
3 |
+
import numpy as np
|
4 |
+
from sklearn.neural_network import MLPRegressor
|
5 |
+
from sklearn.utils.validation import column_or_1d
|
6 |
+
|
7 |
+
import Settings as settings
|
8 |
+
from DataUtils import make_y_multi_safe
|
9 |
+
|
10 |
+
hidden_layer_sizes = (5, 5)
|
11 |
+
max_iter_mlp = 500000
|
12 |
+
|
13 |
+
|
14 |
+
class MLP_Model:
|
15 |
+
def __init__(self):
|
16 |
+
self.name = "MLP Model"
|
17 |
+
self.short_name = "MLP"
|
18 |
+
self.hidden_layer_sizes = hidden_layer_sizes
|
19 |
+
self.solver = 'adam'
|
20 |
+
self.max_iter = max_iter_mlp
|
21 |
+
self.warm_start = False
|
22 |
+
self.verbose = False
|
23 |
+
self.est_mlp = MLPRegressor(hidden_layer_sizes=self.hidden_layer_sizes,
|
24 |
+
solver=self.solver,
|
25 |
+
activation='relu',
|
26 |
+
max_iter=self.max_iter,
|
27 |
+
verbose=self.verbose,
|
28 |
+
tol=1e-7,
|
29 |
+
warm_start=self.warm_start,
|
30 |
+
n_iter_no_change=100)
|
31 |
+
|
32 |
+
def get_formula_string(self):
|
33 |
+
return "(neural black box)"
|
34 |
+
# current_inputs = ["X{}".format(i + 1) for i in range(settings.num_features)]
|
35 |
+
# # print(current_inputs)
|
36 |
+
# matrices = self.est_mlp.coefs_
|
37 |
+
# vectors = self.est_mlp.intercepts_
|
38 |
+
# for i in range(len(matrices)):
|
39 |
+
# current_outputs = []
|
40 |
+
#
|
41 |
+
# for j in range(matrices[i].shape[1]):
|
42 |
+
# current_term = [vectors[i][j]]
|
43 |
+
# for k in range(matrices[i].shape[0]):
|
44 |
+
# sys.stdout.flush()
|
45 |
+
# current_term.append(["*", matrices[i][k][j], current_inputs[k]])
|
46 |
+
#
|
47 |
+
# current_output = current_term[-1]
|
48 |
+
# for k in range(len(current_term), 1, -1):
|
49 |
+
# current_output = ["+", current_term[k - 2], current_output]
|
50 |
+
# current_outputs.append(current_output)
|
51 |
+
# current_inputs = [["max", 0, old_out] for old_out in current_outputs]
|
52 |
+
#
|
53 |
+
# # [-1] since we don't do relu activation on the last layer.
|
54 |
+
# return current_inputs[0][-1]
|
55 |
+
|
56 |
+
def get_formula(self):
|
57 |
+
return "(neural black box)"
|
58 |
+
# return self.get_formula_string()
|
59 |
+
|
60 |
+
def train(self, X, Y):
|
61 |
+
X = np.reshape(X, [X.shape[0], -1])
|
62 |
+
Y = np.reshape(Y, [-1, 1])
|
63 |
+
Y = column_or_1d(Y)
|
64 |
+
self.est_mlp.fit(X, Y)
|
65 |
+
return None
|
66 |
+
|
67 |
+
def predict(self, X):
|
68 |
+
return self.est_mlp.predict(X)
|
69 |
+
|
70 |
+
# Mean square error
|
71 |
+
def test(self, X, Y):
|
72 |
+
X = np.reshape(X, [X.shape[0], -1])
|
73 |
+
y_hat = np.reshape(self.est_mlp.predict(X), [1, -1])[0]
|
74 |
+
y_gold = np.reshape(Y, [1, -1])[0]
|
75 |
+
our_sum = 0
|
76 |
+
for i in range(len(y_gold)):
|
77 |
+
our_sum += (y_hat[i] - y_gold[i]) ** 2
|
78 |
+
|
79 |
+
return our_sum / len(y_gold)
|
80 |
+
|
81 |
+
def reset(self):
|
82 |
+
self.est_mlp = MLPRegressor(hidden_layer_sizes=self.hidden_layer_sizes,
|
83 |
+
solver=self.solver,
|
84 |
+
activation='relu',
|
85 |
+
max_iter=self.max_iter,
|
86 |
+
verbose=self.verbose,
|
87 |
+
tol=1e-7,
|
88 |
+
warm_start=self.warm_start,
|
89 |
+
n_iter_no_change=100)
|
90 |
+
|
91 |
+
def soft_reset(self):
|
92 |
+
self.est_mlp = MLPRegressor(hidden_layer_sizes=self.hidden_layer_sizes,
|
93 |
+
solver=self.solver,
|
94 |
+
activation='relu',
|
95 |
+
max_iter=self.max_iter,
|
96 |
+
verbose=self.verbose,
|
97 |
+
tol=1e-7,
|
98 |
+
warm_start=self.warm_start,
|
99 |
+
n_iter_no_change=100)
|
100 |
+
|
101 |
+
def get_simple_formula(self, digits=None):
|
102 |
+
full_formula = self.get_formula_string()
|
103 |
+
return full_formula
|
104 |
+
# return DataUtils.simplify_formula(full_formula, digits=digits)
|
105 |
+
|
106 |
+
def real_repeat_train(self, x, y=None,
|
107 |
+
num_repeats=settings.num_train_repeat_processes,
|
108 |
+
test_x=None, test_y=None,
|
109 |
+
verbose=True):
|
110 |
+
|
111 |
+
# we still reduce train set size if only 1 repeat
|
112 |
+
train_set_size = int(len(x) * settings.quick_train_fraction + 0.1)
|
113 |
+
|
114 |
+
x = np.array(x)
|
115 |
+
if y is not None:
|
116 |
+
y = np.array(y)
|
117 |
+
|
118 |
+
sample = np.random.choice(range(x.shape[0]), size=train_set_size, replace=False)
|
119 |
+
train_x = x[sample][:]
|
120 |
+
if y is not None:
|
121 |
+
train_y = y[sample]
|
122 |
+
else:
|
123 |
+
train_y = None
|
124 |
+
|
125 |
+
out_sample = [aaa for aaa in range(x.shape[0]) if aaa not in sample]
|
126 |
+
valid_x = x[out_sample][:]
|
127 |
+
if y is not None:
|
128 |
+
valid_y = y[out_sample]
|
129 |
+
# valid_y = self.make_y_multi_safe(valid_y)
|
130 |
+
else:
|
131 |
+
valid_y = None
|
132 |
+
|
133 |
+
best_formula = ""
|
134 |
+
best_iter = 0
|
135 |
+
best_validation = 999999
|
136 |
+
best_err = 999999
|
137 |
+
old_time = time.time()
|
138 |
+
|
139 |
+
if verbose:
|
140 |
+
print("Beginning {} repeat sessions of {} iterations each.".format(num_repeats,
|
141 |
+
settings.num_train_steps_in_repeat_mode))
|
142 |
+
print()
|
143 |
+
start_time = time.time()
|
144 |
+
old_time = start_time
|
145 |
+
|
146 |
+
for train_iter in range(1, 1 + num_repeats):
|
147 |
+
if verbose:
|
148 |
+
print("Repeated train session {} of {}.".format(train_iter, num_repeats))
|
149 |
+
|
150 |
+
self.soft_reset()
|
151 |
+
self.train(train_x, train_y)
|
152 |
+
|
153 |
+
valid_err = self.test(valid_x, valid_y)
|
154 |
+
|
155 |
+
current_time = time.time()
|
156 |
+
if verbose:
|
157 |
+
# print(self.get_simple_formula())
|
158 |
+
print("Attained validation error: {:.5f}".format(valid_err))
|
159 |
+
|
160 |
+
if valid_err < best_validation:
|
161 |
+
best_validation = valid_err
|
162 |
+
best_formula = self.get_simple_formula()
|
163 |
+
best_iter = train_iter
|
164 |
+
if test_x is not None:
|
165 |
+
safe_test_y = make_y_multi_safe(test_y)
|
166 |
+
best_err = self.test(test_x, safe_test_y)
|
167 |
+
else:
|
168 |
+
best_err = valid_err
|
169 |
+
if verbose:
|
170 |
+
print(">>> New best model!")
|
171 |
+
print(best_formula)
|
172 |
+
|
173 |
+
if verbose:
|
174 |
+
iters_per_minute = 60.0 / (current_time - old_time)
|
175 |
+
print("Took {:.2f} minutes.".format((current_time - old_time) / 60))
|
176 |
+
print("Est. {:.2f} minutes remaining.".format((num_repeats - train_iter) / iters_per_minute))
|
177 |
+
print()
|
178 |
+
old_time = current_time
|
179 |
+
|
180 |
+
if verbose:
|
181 |
+
print("Total time for repeat process: {:.2f} minutes.".format((time.time() - start_time) / 60))
|
182 |
+
|
183 |
+
return best_formula, best_iter, best_err
|
184 |
+
|
185 |
+
# Does not repeat train. sorry.
|
186 |
+
def repeat_train(self, x, y=None,
|
187 |
+
num_repeats=settings.num_train_repeat_processes,
|
188 |
+
test_x=None, test_y=None,
|
189 |
+
verbose=True):
|
190 |
+
|
191 |
+
# we still reduce train set size if only 1 repeat
|
192 |
+
train_set_size = int(len(x) * settings.quick_train_fraction + 0.1)
|
193 |
+
|
194 |
+
x = np.array(x)
|
195 |
+
if y is not None:
|
196 |
+
y = np.array(y)
|
197 |
+
|
198 |
+
sample = np.random.choice(range(x.shape[0]), size=train_set_size, replace=False)
|
199 |
+
train_x = x[sample][:]
|
200 |
+
if y is not None:
|
201 |
+
train_y = y[sample]
|
202 |
+
else:
|
203 |
+
train_y = None
|
204 |
+
|
205 |
+
out_sample = [aaa for aaa in range(x.shape[0]) if aaa not in sample]
|
206 |
+
valid_x = x[out_sample][:]
|
207 |
+
if y is not None:
|
208 |
+
valid_y = y[out_sample]
|
209 |
+
# valid_y = self.make_y_multi_safe(valid_y)
|
210 |
+
else:
|
211 |
+
valid_y = None
|
212 |
+
|
213 |
+
if verbose:
|
214 |
+
start_time = time.time()
|
215 |
+
old_time = start_time
|
216 |
+
|
217 |
+
self.soft_reset()
|
218 |
+
self.train(train_x, train_y)
|
219 |
+
|
220 |
+
current_time = time.time()
|
221 |
+
|
222 |
+
best_formula = self.get_simple_formula()
|
223 |
+
if test_x is not None:
|
224 |
+
safe_test_y = make_y_multi_safe(test_y)
|
225 |
+
best_err = self.test(test_x, safe_test_y)
|
226 |
+
else:
|
227 |
+
best_err = self.test(valid_x, valid_y)
|
228 |
+
if verbose:
|
229 |
+
print(">>> New best model!")
|
230 |
+
print(best_formula)
|
231 |
+
|
232 |
+
if verbose:
|
233 |
+
print("Took {:.2f} minutes.".format((current_time - old_time) / 60))
|
234 |
+
print()
|
235 |
+
|
236 |
+
if verbose:
|
237 |
+
print("Total time for repeat process: {:.2f} minutes.".format((time.time() - start_time) / 60))
|
238 |
+
|
239 |
+
return best_formula, 0, best_err
|
RegressorTest.py
ADDED
@@ -0,0 +1,115 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""""""""""""""""""""""""""""""""
|
2 |
+
This file is for running.
|
3 |
+
Do not modify this file.
|
4 |
+
|
5 |
+
For running: DiffEqnSolver.py
|
6 |
+
For modifying: settings.py
|
7 |
+
"""""""""""""""""""""""""""""""""
|
8 |
+
|
9 |
+
import os
|
10 |
+
import time
|
11 |
+
|
12 |
+
import numpy as np
|
13 |
+
from pysr import pysr, best
|
14 |
+
|
15 |
+
import DataUtils
|
16 |
+
import Settings as settings
|
17 |
+
from SymbolicFunctionLearner import SFL
|
18 |
+
|
19 |
+
# Dataset
|
20 |
+
X = 2 * np.random.randn(100, 5)
|
21 |
+
y = 2 * np.cos(X[:, 3]) + X[:, 0] ** 2 - 2
|
22 |
+
|
23 |
+
# Learn equations
|
24 |
+
equations = pysr(X, y, niterations=5,
|
25 |
+
binary_operators=["plus", "mult"],
|
26 |
+
unary_operators=[
|
27 |
+
"cos", "exp", "sin"])
|
28 |
+
# Pre-defined library of operators (see https://pysr.readthedocs.io/en/latest/docs/operators/)
|
29 |
+
# "inv(x) = 1/x"]) # Define your own operator! (Julia syntax)
|
30 |
+
|
31 |
+
... # (you can use ctl-c to exit early)
|
32 |
+
|
33 |
+
print(best(equations))
|
34 |
+
|
35 |
+
|
36 |
+
|
37 |
+
settings.mode = "sr"
|
38 |
+
|
39 |
+
if not os.path.exists('images'):
|
40 |
+
os.makedirs('images')
|
41 |
+
|
42 |
+
input_file = open("FeynmanEqns.txt", "r")
|
43 |
+
input_lines = input_file.readlines()
|
44 |
+
input_file.close()
|
45 |
+
for line in input_lines[1:]:
|
46 |
+
line_parts = line.strip().split(";")
|
47 |
+
eqn_name = line_parts[0].strip()
|
48 |
+
settings.n_tree_layers = int(line_parts[1].strip())
|
49 |
+
settings.num_features = int(line_parts[2].strip())
|
50 |
+
eqn_str = line_parts[3].strip()
|
51 |
+
print("True equation: {}".format(eqn_str))
|
52 |
+
settings.true_eqn = eqn_str
|
53 |
+
settings.initialize_ops = eval(line_parts[4].strip())
|
54 |
+
print(settings.initialize_ops)
|
55 |
+
|
56 |
+
# Set up data
|
57 |
+
fixed_x = DataUtils.generate_data(settings.train_N, n_vars=settings.num_features)
|
58 |
+
print(fixed_x.shape)
|
59 |
+
fixed_y = DataUtils.true_function(fixed_x)
|
60 |
+
print(len(fixed_y))
|
61 |
+
|
62 |
+
settings.fixed_x = []
|
63 |
+
settings.fixed_y = []
|
64 |
+
for i in range(settings.train_N):
|
65 |
+
settings.fixed_x.append(fixed_x[i, :].tolist()[0])
|
66 |
+
settings.fixed_y.append(fixed_y[i])
|
67 |
+
|
68 |
+
current_model = SFL()
|
69 |
+
|
70 |
+
print('\nBeginning experiment: {}'.format(current_model.name))
|
71 |
+
|
72 |
+
print("{} tree layers.".format(settings.n_tree_layers))
|
73 |
+
print("{} features of {} component(s) each.".format(settings.num_features, settings.num_dims_per_feature))
|
74 |
+
print("{} components in output.".format(settings.n_dims_in_output))
|
75 |
+
print("{} operators: {}.".format(len(current_model.function_set),
|
76 |
+
current_model.function_set))
|
77 |
+
|
78 |
+
train_errors = []
|
79 |
+
valid_errors = []
|
80 |
+
test_errors = []
|
81 |
+
true_eqns = []
|
82 |
+
|
83 |
+
# train_X = DataUtils.generate_data(settings.train_N, n_vars=current_model.n_input_variables,
|
84 |
+
# avoid_zero=settings.avoid_zero)
|
85 |
+
# valid_X = DataUtils.generate_data(settings.train_N, n_vars=current_model.n_input_variables,
|
86 |
+
# avoid_zero=settings.avoid_zero)
|
87 |
+
# test_X = DataUtils.generate_data(settings.test_N, n_vars=current_model.n_input_variables,
|
88 |
+
# min_x=settings.test_scope[0],
|
89 |
+
# max_x=settings.test_scope[1])
|
90 |
+
train_X = fixed_x
|
91 |
+
test_X = fixed_x
|
92 |
+
train_Y = fixed_y
|
93 |
+
test_Y = fixed_y
|
94 |
+
|
95 |
+
print("\n========================")
|
96 |
+
print("Starting Solver.")
|
97 |
+
print("==========================\n")
|
98 |
+
|
99 |
+
# Train the model from scratch several times, keeping the best one.
|
100 |
+
start_time = time.time()
|
101 |
+
best_model, best_iter, best_err = current_model.repeat_train(train_X, train_Y,
|
102 |
+
settings.num_train_repeat_processes,
|
103 |
+
test_x=test_X, test_y=test_Y)
|
104 |
+
running_time = time.time() - start_time
|
105 |
+
|
106 |
+
print("best_model: {}".format(best_model))
|
107 |
+
print("----------------------")
|
108 |
+
print("Finished regression. Took {:.2f} minutes.\n".format(running_time / 60))
|
109 |
+
|
110 |
+
print("Final solution found at attempt {}:".format(best_iter))
|
111 |
+
print("y = {}".format(best_model))
|
112 |
+
print("Test error: {}".format(best_err))
|
113 |
+
if best_err < 0.02:
|
114 |
+
print("Attained error less than 0.02 - great!")
|
115 |
+
print()
|
Settings.py
ADDED
@@ -0,0 +1,277 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""""""""""""""""""""""""""""""""
|
2 |
+
This file is for modifying.
|
3 |
+
Do not run this file.
|
4 |
+
|
5 |
+
For running: RegressorTest.py
|
6 |
+
For modifying: Settings.py
|
7 |
+
"""""""""""""""""""""""""""""""""
|
8 |
+
import numpy as np
|
9 |
+
import tensorflow as tf
|
10 |
+
|
11 |
+
"""""""""""""""""""""""""""
|
12 |
+
Settings that can change
|
13 |
+
"""""""""""""""""""""""""""
|
14 |
+
# Determines complexity of tree
|
15 |
+
n_tree_layers = 3
|
16 |
+
|
17 |
+
# Allowable operators for tree nodes
|
18 |
+
# function_set = ['id', 'mul', 'sqrt', 'sin', 'div', 'log']
|
19 |
+
function_set = ["id", "mul", "sin", "sqrt"]
|
20 |
+
|
21 |
+
num_features = 1
|
22 |
+
num_dims_per_feature = 1
|
23 |
+
n_dims_in_output = 1
|
24 |
+
|
25 |
+
train_scope = [0, 5]
|
26 |
+
test_scope = [y * 2 for y in train_scope]
|
27 |
+
|
28 |
+
num_train_repeat_processes = 5
|
29 |
+
num_train_steps_in_repeat_mode = 8000
|
30 |
+
|
31 |
+
"""""""""""""""""""""""""""
|
32 |
+
Display and log settings
|
33 |
+
"""""""""""""""""""""""""""
|
34 |
+
|
35 |
+
show_output = False
|
36 |
+
keep_logs = False
|
37 |
+
output_freq = 49000
|
38 |
+
plot_frequency = 500000
|
39 |
+
save_all_formulas = False
|
40 |
+
|
41 |
+
max_formula_output_length = 400
|
42 |
+
|
43 |
+
"""""""""""""""""""""""""""
|
44 |
+
Tree settings
|
45 |
+
"""""""""""""""""""""""""""
|
46 |
+
use_both_for_unary = True
|
47 |
+
non_const = False
|
48 |
+
use_leaf_sm = False
|
49 |
+
|
50 |
+
|
51 |
+
"""""""""""""""""""""""""""
|
52 |
+
Domain parameters
|
53 |
+
"""""""""""""""""""""""""""
|
54 |
+
|
55 |
+
fpe_example = 0
|
56 |
+
|
57 |
+
max_x = np.pi
|
58 |
+
|
59 |
+
train_scope2 = [0, 5]
|
60 |
+
test_scope2 = test_scope.copy() # [0, 5]
|
61 |
+
|
62 |
+
avoid_zero = False
|
63 |
+
# if fpe_example == 4:
|
64 |
+
# avoid_zero = True
|
65 |
+
|
66 |
+
|
67 |
+
"""""""""""""""""""""""""""
|
68 |
+
Define the ODE here
|
69 |
+
"""""""""""""""""""""""""""
|
70 |
+
# options: mode = "sr", "de", "lr"
|
71 |
+
mode = "de"
|
72 |
+
|
73 |
+
# This is the "g" function that defines the ODE problem.
|
74 |
+
def implicit_function(full_x, y, y_p, y_pp):
|
75 |
+
# Implicit function is 0 if we are doing symbolic regression
|
76 |
+
if mode == "sr":
|
77 |
+
return y * 0.0
|
78 |
+
|
79 |
+
y_p1 = y_p[0]
|
80 |
+
y_p2 = y_p[1]
|
81 |
+
y_p3 = y_p[2]
|
82 |
+
|
83 |
+
y_pp1 = y_pp[0]
|
84 |
+
y_pp2 = y_pp[1]
|
85 |
+
y_pp12 = y_pp[2]
|
86 |
+
|
87 |
+
x = tf.reshape(full_x[:, 0, 0], [-1, 1, 1])
|
88 |
+
t = tf.reshape(full_x[:, 0, -1], [-1, 1, 1])
|
89 |
+
if num_features > 1:
|
90 |
+
w = tf.reshape(full_x[:, 0, 1], [-1, 1, 1])
|
91 |
+
|
92 |
+
ret_val = None
|
93 |
+
|
94 |
+
""" Lane-Emden Equation """
|
95 |
+
# emden_m = 0
|
96 |
+
# ret_val = y_pp1 + 2.0 * tf.math.divide_no_nan(y_p1, x)
|
97 |
+
# ret_val += y ** emden_m
|
98 |
+
|
99 |
+
""" Bell curve integral """
|
100 |
+
# ret_val = tf.math.exp(-1.0 * tf.square(x)) - y_p1
|
101 |
+
|
102 |
+
""" One dimensional wave equation """
|
103 |
+
# c = 1.0
|
104 |
+
# ret_val = y_pp2 - c**2 * y_pp1
|
105 |
+
|
106 |
+
""" One dimensional heat equation """
|
107 |
+
# c = 1.0
|
108 |
+
# ret_val = y_p2 - c**2 * y_pp1 - tf.math.cos(x)
|
109 |
+
|
110 |
+
""" Inhomogeneous wave equation """
|
111 |
+
# ret_val = y_pp1 - y_pp2 - 2
|
112 |
+
|
113 |
+
""" Two dimensional Laplace equation """
|
114 |
+
# ret_val = y_pp2 + y_pp1
|
115 |
+
|
116 |
+
ret_val = y_p1 - 2 * x
|
117 |
+
|
118 |
+
|
119 |
+
|
120 |
+
""" FP Eqn """
|
121 |
+
if fpe_example == 1:
|
122 |
+
# Example 1
|
123 |
+
a = -1.0
|
124 |
+
a_p = 0.0
|
125 |
+
b = 1.0
|
126 |
+
b_p = 0.0
|
127 |
+
b_pp = 0.0
|
128 |
+
elif fpe_example == 2:
|
129 |
+
# Example 2
|
130 |
+
a = x
|
131 |
+
a_p = 1.0
|
132 |
+
b = tf.math.square(x) / 2
|
133 |
+
b_p = x
|
134 |
+
b_pp = 1.0
|
135 |
+
elif fpe_example == 3:
|
136 |
+
# Example 3
|
137 |
+
a = -1.0 - x
|
138 |
+
a_p = -1.0
|
139 |
+
b = tf.multiply(x ** 2, tf.math.exp(t))
|
140 |
+
b_p = 2 * x * tf.math.exp(t)
|
141 |
+
b_pp = 2 * tf.math.exp(t)
|
142 |
+
elif fpe_example == 4:
|
143 |
+
# Example 4
|
144 |
+
a = 4.0 * tf.math.divide_no_nan(y, x) - x / 3.0
|
145 |
+
a_p = 4.0 * (tf.math.divide_no_nan(y_p1, x) - tf.math.divide_no_nan(y, x ** 2)) - 1.0 / 3
|
146 |
+
b = y
|
147 |
+
b_p = y_p1
|
148 |
+
b_pp = y_pp1
|
149 |
+
elif fpe_example == 6:
|
150 |
+
# Example 5
|
151 |
+
a = 0.0
|
152 |
+
a_p = 0.0
|
153 |
+
b = 0.5
|
154 |
+
b_p = 0.0
|
155 |
+
b_pp = 0.0
|
156 |
+
|
157 |
+
if fpe_example > 0:
|
158 |
+
t1 = tf.multiply(y, b_pp - a_p)
|
159 |
+
t2 = tf.multiply(y_p1, 2 * b_p - a)
|
160 |
+
t3 = tf.multiply(y_pp1, b)
|
161 |
+
# print("a: {}".format(a.shape))
|
162 |
+
# print("a_p: {}".format(a_p.shape))
|
163 |
+
# print("b: {}".format(a.shape))
|
164 |
+
# print("b_p: {}".format(b_p.shape))
|
165 |
+
# print("b_pp: {}".format(b_pp.shape))
|
166 |
+
# print("t1: {}".format(t1.shape))
|
167 |
+
# print("t2: {}".format(t2.shape))
|
168 |
+
# print("t3: {}".format(t3.shape))
|
169 |
+
|
170 |
+
ret_val = y_p2 - (t1 + t2 + t3)
|
171 |
+
|
172 |
+
if fpe_example == 3:
|
173 |
+
ret_val = y_p2 - ((x + 1) * y_p1 + x ** 2 * tf.math.exp(t) * y_pp1)
|
174 |
+
|
175 |
+
if fpe_example == 4:
|
176 |
+
t1 = y * (y_pp1 * x ** 2 - 4 * y_p1 * x + 4 * y + x * x / 3.0)
|
177 |
+
t2 = y_p1 * (2 * x * y_p1 - 4 * x * y + x * x * x / 3.0)
|
178 |
+
t3 = x ** 2 * y_pp1 * y
|
179 |
+
ret_val = y_p2 * x * x - (t1 + t2 + t3)
|
180 |
+
|
181 |
+
if fpe_example == 5:
|
182 |
+
ret_val = y_p3 - (-2 * y + 3 * x * y_p1 - w * y_p2 + x ** 2 * y_pp1 + w ** 2 * y_pp2 + 2 * y_pp12)
|
183 |
+
|
184 |
+
return ret_val
|
185 |
+
|
186 |
+
"""""""""""""""""""""""""""
|
187 |
+
Initial values
|
188 |
+
"""""""""""""""""""""""""""
|
189 |
+
# initialize_ops are given in bottom-up order.
|
190 |
+
initialize_ops = np.zeros([2 ** n_tree_layers - 1])
|
191 |
+
# initialize_ops = ["mul", "mul", "id"]
|
192 |
+
# if fpe_example in [1, 2, 3, 5]:
|
193 |
+
# initialize_ops = ["id", "exp", "mul"]
|
194 |
+
# elif fpe_example in [4]:
|
195 |
+
# initialize_ops = ["mul", "exp", "mul"]
|
196 |
+
|
197 |
+
|
198 |
+
# initialize_ops = ["exp", "sin", "mul"]
|
199 |
+
#
|
200 |
+
min_x = 0
|
201 |
+
max_t = 5
|
202 |
+
min_t = 0
|
203 |
+
n_bc_points = 5
|
204 |
+
|
205 |
+
# Initial values for (x, y)
|
206 |
+
|
207 |
+
fixed_x = []
|
208 |
+
fixed_y = []
|
209 |
+
# for i in range(n_bc_points):
|
210 |
+
# t_i = i * (max_t - min_t)/n_bc_points + min_t
|
211 |
+
# fixed_x.append([0, t_i])
|
212 |
+
# # fixed_y.append(0)
|
213 |
+
# fixed_y.append(1 - np.exp(-1 * t_i))
|
214 |
+
# fixed_x.append([np.pi, t_i])
|
215 |
+
# fixed_y.append(np.exp(-1 * t_i) - 1)
|
216 |
+
# fixed_y.append(0)
|
217 |
+
|
218 |
+
|
219 |
+
|
220 |
+
# Initial values for (x, y')
|
221 |
+
fixed_x_p1 = []
|
222 |
+
fixed_y_p1 = []
|
223 |
+
fixed_x_p2 = []
|
224 |
+
fixed_y_p2 = []
|
225 |
+
|
226 |
+
if mode == "de":
|
227 |
+
if fpe_example != 5:
|
228 |
+
for i in range(n_bc_points):
|
229 |
+
x_i = i * (max_x - min_x) / (n_bc_points - 1) + min_x
|
230 |
+
fixed_x.append([x_i, 0])
|
231 |
+
if fpe_example in [1, 2, 5]:
|
232 |
+
fixed_y.append(x_i)
|
233 |
+
elif fpe_example == 3:
|
234 |
+
fixed_y.append(x_i + 1)
|
235 |
+
elif fpe_example == 4:
|
236 |
+
fixed_y.append(x_i ** 2)
|
237 |
+
# fixed_y.append(0)
|
238 |
+
# fixed_y.append(x_i + np.cos(x_i))
|
239 |
+
# fixed_x_p2.append([x_i, 0])
|
240 |
+
# fixed_y_p2.append(np.sin(x_i))
|
241 |
+
|
242 |
+
if fpe_example == 5:
|
243 |
+
for i in range(n_bc_points):
|
244 |
+
x_i = i * (max_x - min_x) / (n_bc_points - 1) + min_x
|
245 |
+
for j in range(n_bc_points):
|
246 |
+
w_j = j * (max_x - min_x) / (n_bc_points - 1) + min_x
|
247 |
+
|
248 |
+
fixed_x.append([x_i, w_j, 0])
|
249 |
+
fixed_y.append(x_i)
|
250 |
+
|
251 |
+
# print("IVP (x, y):\n{}".format([(fixed_x[i], fixed_y[i]) for i in range(len(fixed_x))]))
|
252 |
+
# print("IVP (x, y_p1):\n{}".format([(fixed_x_p1[i], fixed_y_p1[i]) for i in range(len(fixed_x_p1))]))
|
253 |
+
# print("IVP (x, y_p2):\n{}".format([(fixed_x_p2[i], fixed_y_p2[i]) for i in range(len(fixed_x_p2))]))
|
254 |
+
|
255 |
+
# Weight to give IVP error
|
256 |
+
ivp_lambda = 10
|
257 |
+
|
258 |
+
|
259 |
+
"""""""""""""""""""""""""""
|
260 |
+
Training hyperparameters
|
261 |
+
"""""""""""""""""""""""""""
|
262 |
+
quick_train_fraction = 0.7
|
263 |
+
|
264 |
+
# Probably don't need to change any of the ones below
|
265 |
+
max_training_batch_size = 1000
|
266 |
+
t1_fraction = 5/20
|
267 |
+
t2_fraction = 15 / 20
|
268 |
+
|
269 |
+
train_N = 5000
|
270 |
+
test_N = 1000
|
271 |
+
|
272 |
+
eps = 1e-4
|
273 |
+
big_eps = 1e-3
|
274 |
+
d_eps = 2.0e-2
|
275 |
+
learn_rate = 0.001
|
276 |
+
w_matrix_stddev = 0.1
|
277 |
+
init_weight_value = 5
|
SymbolicFunctionLearner.py
ADDED
@@ -0,0 +1,1449 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""""""""""""""""""""""""""""""""
|
2 |
+
Do not run or modify this file.
|
3 |
+
|
4 |
+
For running: DiffEqnSolver.py
|
5 |
+
For modifying: Settings.py
|
6 |
+
"""""""""""""""""""""""""""""""""
|
7 |
+
|
8 |
+
import datetime
|
9 |
+
import time
|
10 |
+
|
11 |
+
import numpy as np
|
12 |
+
import tensorflow as tf
|
13 |
+
|
14 |
+
import DataUtils
|
15 |
+
import Settings
|
16 |
+
from DataUtils import choices_to_init_weight_matrix
|
17 |
+
from DataUtils import tf_diff_sqrt, tf_diff_log, our_tanh, spike
|
18 |
+
from Settings import implicit_function, d_eps
|
19 |
+
|
20 |
+
tf.compat.v1.disable_eager_execution()
|
21 |
+
|
22 |
+
|
23 |
+
def new_weight_matrix(n_rows, n_cols, mean=0.0, name=None):
|
24 |
+
initial = tf.random.normal(shape=[n_rows, n_cols], mean=mean, stddev=Settings.w_matrix_stddev)
|
25 |
+
if name is not None:
|
26 |
+
return tf.Variable(initial, name=name)
|
27 |
+
return tf.Variable(initial)
|
28 |
+
|
29 |
+
|
30 |
+
def new_bias(n_cols, name=None):
|
31 |
+
initial = tf.zeros(shape=[1, n_cols])
|
32 |
+
if name is not None:
|
33 |
+
return tf.Variable(initial, name=name)
|
34 |
+
return tf.Variable(initial)
|
35 |
+
|
36 |
+
|
37 |
+
def operate_on_tensors(tensor_A, tensor_B, fn_set, use_both_for_unary=True):
|
38 |
+
# print('op on tensors. input shapes: {}, {}'.format(tensor_A.shape, tensor_B.shape))
|
39 |
+
if use_both_for_unary:
|
40 |
+
w2 = 1.0
|
41 |
+
else:
|
42 |
+
w2 = 0.0
|
43 |
+
|
44 |
+
answer_vector = []
|
45 |
+
for operator_i in fn_set:
|
46 |
+
if operator_i == 'id':
|
47 |
+
answer_vector.extend([tensor_A[:, :, 0] + w2 * tensor_B[:, :, 0]])
|
48 |
+
# print("id vector shape: {}".format(answer_vector[-1].shape))
|
49 |
+
elif operator_i == 'add':
|
50 |
+
answer_vector.extend([tensor_A[:, :, 0] + tensor_B[:, :, 0]])
|
51 |
+
elif operator_i == 'sin':
|
52 |
+
answer_vector.extend([tf.sin(tensor_A[:, :, 0] + w2 * tensor_B[:, :, 0])])
|
53 |
+
elif operator_i == 'cos':
|
54 |
+
answer_vector.extend([tf.cos(tensor_A[:, :, 0] + w2 * tensor_B[:, :, 0])])
|
55 |
+
elif operator_i == 'sqrt':
|
56 |
+
answer_vector.extend([tf_diff_sqrt(tensor_A[:, :, 0] + w2 * tensor_B[:, :, 0])])
|
57 |
+
elif operator_i == 'mul':
|
58 |
+
answer_vector.extend([tf.multiply(tensor_A[:, :, 0], tensor_B[:, :, 0])])
|
59 |
+
elif operator_i == 'div':
|
60 |
+
answer_vector.extend([tf.math.divide_no_nan(tensor_A[:, :, 0], tensor_B[:, :, 0])])
|
61 |
+
elif operator_i == 'log':
|
62 |
+
answer_vector.extend([tf_diff_log(tensor_A[:, :, 0] + w2 * tensor_B[:, :, 0])])
|
63 |
+
elif operator_i == 'exp':
|
64 |
+
answer_vector.extend([tf.exp(our_tanh(tensor_A[:, :, 0] + w2 * tensor_B[:, :, 0], factor=np.log(50000)))])
|
65 |
+
else:
|
66 |
+
answer_vector.extend([None])
|
67 |
+
|
68 |
+
return tf.stack(answer_vector, axis=-1)
|
69 |
+
|
70 |
+
|
71 |
+
def sm_no_const_selector(nonflat_input, flat_input, initial_weights):
|
72 |
+
# print("sm_no_const_selector---")
|
73 |
+
# print("initial_weights: {}".format(initial_weights.shape))
|
74 |
+
# print("nonflat_input: {}".format(nonflat_input.shape))
|
75 |
+
pre_sm_weights = new_weight_matrix(int(nonflat_input.shape[-1]), 1)
|
76 |
+
post_sm_weights = tf.math.softmax(pre_sm_weights + initial_weights, axis=0)
|
77 |
+
# print("post_sm_weights: {}".format(post_sm_weights.shape))
|
78 |
+
sm_result = tf.matmul(nonflat_input, post_sm_weights)
|
79 |
+
# print("sm_result: {}".format(sm_result.shape))
|
80 |
+
|
81 |
+
flat_weights = tf.multiply(post_sm_weights,
|
82 |
+
tf.cast(tf.greater(post_sm_weights,
|
83 |
+
tf.reduce_max(post_sm_weights) - 0.01), tf.float32))
|
84 |
+
flat_weights = tf.divide(flat_weights, tf.reduce_sum(flat_weights))
|
85 |
+
flat_result = tf.matmul(flat_input, flat_weights)
|
86 |
+
|
87 |
+
return sm_result, flat_result, pre_sm_weights+initial_weights, post_sm_weights, flat_weights
|
88 |
+
|
89 |
+
|
90 |
+
def collect_op_inputs_str(weight_w, weight_b, input_strs):
|
91 |
+
num_inputs = weight_w.shape[0]
|
92 |
+
# print("num_inputs: {}. input_strs length: {}".format(num_inputs, len(input_strs)))
|
93 |
+
# print(weight_w)
|
94 |
+
# print(input_strs)
|
95 |
+
temp_answer = ''
|
96 |
+
has_one = False
|
97 |
+
has_more_than_one = False
|
98 |
+
for row in range(num_inputs):
|
99 |
+
if np.abs(weight_w[row][0]) > Settings.big_eps and input_strs[row] != '0':
|
100 |
+
if has_one:
|
101 |
+
temp_answer += ' + '
|
102 |
+
has_more_than_one = True
|
103 |
+
if np.abs(weight_w[row][0] - 1) < Settings.big_eps:
|
104 |
+
temp_answer += '{}'.format(input_strs[row])
|
105 |
+
else:
|
106 |
+
temp_answer += '{:.4f}*({})'.format(weight_w[row][0], input_strs[row])
|
107 |
+
has_one = True
|
108 |
+
# print('weight_b[-1]: {}'.format(weight_b))
|
109 |
+
if np.abs(weight_b[-1][0]) > Settings.big_eps:
|
110 |
+
if has_one:
|
111 |
+
temp_answer += ' + '
|
112 |
+
has_more_than_one = True
|
113 |
+
temp_answer += '{:.4f}'.format(weight_b[-1][0])
|
114 |
+
if len(temp_answer) == 0:
|
115 |
+
temp_answer = '0'
|
116 |
+
if has_more_than_one:
|
117 |
+
return '(' + temp_answer + ')'
|
118 |
+
return temp_answer
|
119 |
+
|
120 |
+
def collect_minimal_op_inputs_str(weight_w, input_strs):
|
121 |
+
num_inputs = weight_w.shape[0]
|
122 |
+
temp_answer = ''
|
123 |
+
has_one = False
|
124 |
+
has_more_than_one = False
|
125 |
+
for row in range(num_inputs):
|
126 |
+
if has_one:
|
127 |
+
temp_answer += ' + '
|
128 |
+
has_more_than_one = True
|
129 |
+
temp_answer += '{}'.format(input_strs[row])
|
130 |
+
has_one = True
|
131 |
+
|
132 |
+
if len(temp_answer) == 0:
|
133 |
+
temp_answer = '0'
|
134 |
+
if has_more_than_one:
|
135 |
+
return '(' + temp_answer + ')'
|
136 |
+
return temp_answer
|
137 |
+
|
138 |
+
def operation_to_str_best(weight_w, weight_b, weight_sm, input_strs1, input_strs2, fn_set,
|
139 |
+
digits=None, unary_both=True, minimal=False):
|
140 |
+
if input_strs2 is None:
|
141 |
+
temp_answer = collect_op_inputs_str(weight_w, weight_b, input_strs1)
|
142 |
+
return [temp_answer]
|
143 |
+
|
144 |
+
answer = ['0' for _ in fn_set]
|
145 |
+
|
146 |
+
temp_answer1 = input_strs1
|
147 |
+
temp_answer2 = input_strs2
|
148 |
+
|
149 |
+
# Set up temp answer. Don't change this value!
|
150 |
+
if unary_both:
|
151 |
+
if temp_answer1 == '0' and temp_answer2 == '0':
|
152 |
+
temp_answer = '0'
|
153 |
+
elif temp_answer1 == '0':
|
154 |
+
temp_answer = str(temp_answer2)
|
155 |
+
elif temp_answer2 == '0':
|
156 |
+
temp_answer = str(temp_answer1)
|
157 |
+
else:
|
158 |
+
temp_answer = '({} + {})'.format(temp_answer1, temp_answer2)
|
159 |
+
else:
|
160 |
+
temp_answer = str(temp_answer1)
|
161 |
+
|
162 |
+
if 'id' in fn_set:
|
163 |
+
fn_index = fn_set.index('id')
|
164 |
+
answer[fn_index] = temp_answer
|
165 |
+
|
166 |
+
if 'sin' in fn_set:
|
167 |
+
fn_index = fn_set.index('sin')
|
168 |
+
if temp_answer == '0':
|
169 |
+
answer[fn_index] = '0'
|
170 |
+
else:
|
171 |
+
answer[fn_index] = 'sin({})'.format(temp_answer)
|
172 |
+
|
173 |
+
if 'cos' in fn_set:
|
174 |
+
fn_index = fn_set.index('cos')
|
175 |
+
answer[fn_index] = 'cos({})'.format(temp_answer)
|
176 |
+
|
177 |
+
if 'sqrt' in fn_set:
|
178 |
+
fn_index = fn_set.index('sqrt')
|
179 |
+
answer[fn_index] = '(abs({}))^(0.5)'.format(temp_answer)
|
180 |
+
|
181 |
+
if 'log' in fn_set:
|
182 |
+
fn_index = fn_set.index('log')
|
183 |
+
if temp_answer == '0':
|
184 |
+
answer[fn_index] = 'log(0.0001)'
|
185 |
+
else:
|
186 |
+
answer[fn_index] = 'log({})'.format(temp_answer)
|
187 |
+
|
188 |
+
if 'mul' in fn_set:
|
189 |
+
fn_index = fn_set.index('mul')
|
190 |
+
if temp_answer1 == '0' or temp_answer2 == '0':
|
191 |
+
prod_answer = '0'
|
192 |
+
else:
|
193 |
+
prod_answer = '({} * {})'.format(temp_answer1, temp_answer2)
|
194 |
+
answer[fn_index] = prod_answer
|
195 |
+
|
196 |
+
if 'add' in fn_set:
|
197 |
+
fn_index = fn_set.index('add')
|
198 |
+
if temp_answer1 == '0' and temp_answer2 == '0':
|
199 |
+
sum_answer = '0'
|
200 |
+
elif temp_answer1 == '0':
|
201 |
+
sum_answer = str(temp_answer2)
|
202 |
+
elif temp_answer2 == '0':
|
203 |
+
sum_answer = str(temp_answer1)
|
204 |
+
else:
|
205 |
+
sum_answer = '({} + {})'.format(temp_answer1, temp_answer2)
|
206 |
+
answer[fn_index] = sum_answer
|
207 |
+
|
208 |
+
if 'sub' in fn_set:
|
209 |
+
fn_index = fn_set.index('sub')
|
210 |
+
temp_answer1 = input_strs1
|
211 |
+
temp_answer2 = input_strs2
|
212 |
+
|
213 |
+
if temp_answer1 == '0' and temp_answer2 == '0':
|
214 |
+
diff_answer = '0'
|
215 |
+
elif temp_answer1 == '0':
|
216 |
+
diff_answer = "-{}".format(temp_answer2)
|
217 |
+
elif temp_answer2 == '0':
|
218 |
+
diff_answer = temp_answer1
|
219 |
+
else:
|
220 |
+
diff_answer = '({} - {})'.format(temp_answer1, temp_answer2)
|
221 |
+
answer[fn_index] = diff_answer
|
222 |
+
|
223 |
+
if 'max' in fn_set:
|
224 |
+
fn_index = fn_set.index('max')
|
225 |
+
answer[fn_index] = 'max({}, {})'.format(temp_answer1, temp_answer2)
|
226 |
+
|
227 |
+
if 'min' in fn_set:
|
228 |
+
fn_index = fn_set.index('min')
|
229 |
+
answer[fn_index] = 'min({}, {})'.format(temp_answer1, temp_answer2)
|
230 |
+
|
231 |
+
if 'div' in fn_set:
|
232 |
+
fn_index = fn_set.index('div')
|
233 |
+
if temp_answer2 == '0':
|
234 |
+
temp_answer2 = '0.001'
|
235 |
+
if temp_answer1 == '0':
|
236 |
+
div_answer = '0'
|
237 |
+
else:
|
238 |
+
div_answer = '({} / ({}))'.format(temp_answer1, temp_answer2)
|
239 |
+
answer[fn_index] = div_answer
|
240 |
+
|
241 |
+
if 'exp' in fn_set:
|
242 |
+
fn_index = fn_set.index('exp')
|
243 |
+
answer[fn_index] = 'exp({})'.format(temp_answer)
|
244 |
+
|
245 |
+
new_answer = [collect_op_inputs_str(weight_sm, np.zeros([1, 1]), answer)]
|
246 |
+
# print('New answer: {}'.format(new_answer))
|
247 |
+
# print('weight w, weight b: {}, {}'.format(weight_w, weight_b))
|
248 |
+
if minimal:
|
249 |
+
ret_val = collect_minimal_op_inputs_str(weight_w, new_answer)
|
250 |
+
else:
|
251 |
+
ret_val = collect_op_inputs_str(weight_w, weight_b, new_answer)
|
252 |
+
return ret_val
|
253 |
+
|
254 |
+
|
255 |
+
def flattened_sm_result(input_x, sm_applied_weights, our_w, our_b):
|
256 |
+
# print('Create operator node. Input shapes: {}, {}'.format(input_1.shape, input_2.shape))
|
257 |
+
new_sm_weights = tf.multiply(sm_applied_weights,
|
258 |
+
tf.cast(tf.greater(sm_applied_weights,
|
259 |
+
tf.reduce_max(sm_applied_weights) - 0.01), tf.float32))
|
260 |
+
new_sm_weights = tf.divide(new_sm_weights, tf.reduce_sum(new_sm_weights))
|
261 |
+
|
262 |
+
sm_result = tf.matmul(input_x, new_sm_weights)
|
263 |
+
final_result = tf.multiply(sm_result, our_w) + our_b
|
264 |
+
# print(' Final result shape: {}'.format(final_result.shape))
|
265 |
+
|
266 |
+
return final_result, new_sm_weights
|
267 |
+
|
268 |
+
|
269 |
+
class SFL:
|
270 |
+
def __init__(self, var_names=None):
|
271 |
+
|
272 |
+
self.name = "Symbolic Function Learner"
|
273 |
+
self.short_name = "SFL"
|
274 |
+
|
275 |
+
# mode: in ["sr", "de", "lr"]
|
276 |
+
self.mode = Settings.mode
|
277 |
+
|
278 |
+
# main hyperparameters of the symbolic expression
|
279 |
+
self.n_tree_layers = Settings.n_tree_layers
|
280 |
+
self.function_set = Settings.function_set.copy()
|
281 |
+
self.n_input_variables = Settings.num_features
|
282 |
+
self.n_dims_per_variable = Settings.num_dims_per_feature
|
283 |
+
self.n_dims_in_output = Settings.n_dims_in_output
|
284 |
+
assert self.n_dims_in_output in [1, self.n_dims_per_variable]
|
285 |
+
|
286 |
+
# use_both_for_unary: decide how to handle two input children
|
287 |
+
# for a unary operator.
|
288 |
+
# True: add the two inputs.
|
289 |
+
# False: keep first input; discard second input.
|
290 |
+
self.use_both_for_unary = Settings.use_both_for_unary
|
291 |
+
|
292 |
+
# Use a softmax on leaf layer?
|
293 |
+
self.sm_leaf_layer = Settings.use_leaf_sm
|
294 |
+
|
295 |
+
# data_x,y: Input (x, y) values over which we are training.
|
296 |
+
# For symbolic regression, it's the same as fixed_x,y.
|
297 |
+
# For differential equations, it's random values.
|
298 |
+
self.data_x = tf.compat.v1.placeholder("float", [None, self.n_dims_per_variable,
|
299 |
+
self.n_input_variables], name="data_x")
|
300 |
+
self.data_y = tf.compat.v1.placeholder("float", [None, self.n_dims_per_variable, 1], name="data_y")
|
301 |
+
|
302 |
+
# Fixed_x,y: these are the set of points that must be satisfied
|
303 |
+
# by the function that is learned. These are used to compute
|
304 |
+
# the residual error in the cost function.
|
305 |
+
self.fixed_x = tf.compat.v1.placeholder("float", [None, self.n_dims_per_variable,
|
306 |
+
self.n_input_variables], name="data_x")
|
307 |
+
self.fixed_y = tf.compat.v1.placeholder("float", [None, self.n_dims_per_variable, 1], name="data_y")
|
308 |
+
|
309 |
+
# To initialize operators in the SFL with a warm start before training
|
310 |
+
self.init_op_weights = tf.compat.v1.placeholder("float", [len(self.function_set), 2 ** self.n_tree_layers - 1],
|
311 |
+
name="init_op_weights")
|
312 |
+
self.init_op_weight_matrix = np.zeros(shape=[len(self.function_set), 2 ** self.n_tree_layers - 1])
|
313 |
+
|
314 |
+
# To initialize variable choices in the SFL with a warm start before training
|
315 |
+
num_var_input_choices = self.n_input_variables
|
316 |
+
self.init_var_weights = tf.compat.v1.placeholder("float", [num_var_input_choices, 2 ** self.n_tree_layers],
|
317 |
+
name="init_var_weights")
|
318 |
+
self.init_var_weight_matrix = np.zeros(shape=[num_var_input_choices, 2 ** self.n_tree_layers])
|
319 |
+
|
320 |
+
# variables can have default or custom names
|
321 |
+
if self.n_input_variables == 1 and var_names is None:
|
322 |
+
self.var_names = ['x']
|
323 |
+
elif var_names is None:
|
324 |
+
self.var_names = ['x{}'.format(i + 1) for i in range(self.n_input_variables)]
|
325 |
+
else:
|
326 |
+
self.var_names = var_names
|
327 |
+
|
328 |
+
self.learn_rate = Settings.learn_rate
|
329 |
+
|
330 |
+
self.y_gold = self.data_y
|
331 |
+
self.g_error = tf.Variable(0.0)
|
332 |
+
self.g_error_not_flat = tf.Variable(0.0)
|
333 |
+
self.mse = tf.Variable(0.0)
|
334 |
+
self.mse_not_flat = tf.Variable(0.0)
|
335 |
+
self.spike_error = tf.Variable(0.0)
|
336 |
+
self.ivp_error = tf.Variable(0.0)
|
337 |
+
self.ivp_error_not_flat = tf.Variable(0.0)
|
338 |
+
self.total_error = tf.Variable(0.0)
|
339 |
+
|
340 |
+
if self.mode == "de":
|
341 |
+
self.ivp_lambda = Settings.ivp_lambda
|
342 |
+
else:
|
343 |
+
self.ivp_lambda = 0
|
344 |
+
|
345 |
+
self.train_accuracy_log = []
|
346 |
+
self.valid_accuracy_log = []
|
347 |
+
self.test_accuracy_log = []
|
348 |
+
|
349 |
+
self.seen_eqns = []
|
350 |
+
self.seen_minimal_eqns = []
|
351 |
+
self.log_iters = []
|
352 |
+
|
353 |
+
self.best_accuracy_so_far = 9999999
|
354 |
+
self.best_formula_so_far = ""
|
355 |
+
self.best_iter = 0
|
356 |
+
|
357 |
+
self.y_hat = None
|
358 |
+
self.y_hat_p1 = None
|
359 |
+
self.y_hat_pp1 = None
|
360 |
+
self.y_hat_p2 = None
|
361 |
+
self.y_hat_pp2 = None
|
362 |
+
self.y_hat_p3 = None
|
363 |
+
self.y_hat_pp3 = None
|
364 |
+
self.y_hat_pp12 = None
|
365 |
+
self.implicit_g = None
|
366 |
+
self.y_hat_not_flat = None
|
367 |
+
self.y_hat_p_not_flat = None
|
368 |
+
self.y_hat_pp_not_flat = None
|
369 |
+
self.implicit_g_not_flat = None
|
370 |
+
|
371 |
+
self.W_matrices = []
|
372 |
+
self.b_matrices = []
|
373 |
+
self.non_sm_weights = []
|
374 |
+
self.leaf_sm_weights = []
|
375 |
+
self.sm_W_matrices = []
|
376 |
+
self.sm_applied_W_matrices = []
|
377 |
+
self.flattened_W_matrices = []
|
378 |
+
|
379 |
+
self.use_both_for_unary = Settings.use_both_for_unary
|
380 |
+
|
381 |
+
self.init = None
|
382 |
+
self.sess = None
|
383 |
+
|
384 |
+
self.build_sfl()
|
385 |
+
self.reset(var_names)
|
386 |
+
|
387 |
+
def build_sfl(self):
|
388 |
+
self.data_x = tf.compat.v1.placeholder("float", [None, self.n_dims_per_variable,
|
389 |
+
self.n_input_variables], name="data_x")
|
390 |
+
self.data_y = tf.compat.v1.placeholder("float", [None, self.n_dims_per_variable, 1], name="data_y")
|
391 |
+
self.fixed_x = tf.compat.v1.placeholder("float", [None, self.n_dims_per_variable,
|
392 |
+
self.n_input_variables], name="fixed_x")
|
393 |
+
self.fixed_y = tf.compat.v1.placeholder("float", [None, self.n_dims_per_variable, 1], name="fixed_y")
|
394 |
+
|
395 |
+
# To initialize operators in the SFL with a warm start before training
|
396 |
+
self.init_op_weights = tf.compat.v1.placeholder("float", [len(self.function_set), 2 ** self.n_tree_layers - 1],
|
397 |
+
name="init_op_weights")
|
398 |
+
# To initialize variable choices in the SFL with a warm start before training
|
399 |
+
# Right now, only one variable is supported.
|
400 |
+
num_var_input_choices = self.n_input_variables
|
401 |
+
|
402 |
+
self.init_var_weights = tf.compat.v1.placeholder("float", [num_var_input_choices, 2 ** self.n_tree_layers],
|
403 |
+
name="init_var_weights")
|
404 |
+
|
405 |
+
self.g_error = tf.Variable(0.0)
|
406 |
+
self.g_error_not_flat = tf.Variable(0.0)
|
407 |
+
self.mse = tf.Variable(0.0)
|
408 |
+
self.mse_not_flat = tf.Variable(0.0)
|
409 |
+
self.spike_error = tf.Variable(0.0)
|
410 |
+
self.ivp_error = tf.Variable(0.0)
|
411 |
+
self.ivp_error_not_flat = tf.Variable(0.0)
|
412 |
+
self.total_error = tf.Variable(0.0)
|
413 |
+
|
414 |
+
self.W_matrices = []
|
415 |
+
self.b_matrices = []
|
416 |
+
self.non_sm_weights = []
|
417 |
+
self.leaf_sm_weights = []
|
418 |
+
self.sm_W_matrices = []
|
419 |
+
self.sm_applied_W_matrices = []
|
420 |
+
self.flattened_W_matrices = []
|
421 |
+
|
422 |
+
previous_output = []
|
423 |
+
previous_flat_output = []
|
424 |
+
weight_layer = []
|
425 |
+
bias_layer = []
|
426 |
+
|
427 |
+
if Settings.show_output:
|
428 |
+
print("Setting up {} model.".format(self.name))
|
429 |
+
print(" {} tree layers.".format(self.n_tree_layers))
|
430 |
+
print(" {} features of {} component(s) each.".format(self.n_input_variables, self.n_dims_per_variable))
|
431 |
+
print(" {} component(s) in output.".format(self.n_dims_in_output))
|
432 |
+
print(" {} operators: {}.".format(len(self.function_set),
|
433 |
+
self.function_set))
|
434 |
+
|
435 |
+
# Set up leaf layer
|
436 |
+
for i in range(2 ** (Settings.n_tree_layers - 1)):
|
437 |
+
if self.sm_leaf_layer:
|
438 |
+
num_leaf_weights = 1
|
439 |
+
else:
|
440 |
+
num_leaf_weights = self.n_input_variables
|
441 |
+
|
442 |
+
new_weights1 = new_weight_matrix(num_leaf_weights, 1, mean=0.0)
|
443 |
+
new_b1 = new_bias(1)
|
444 |
+
|
445 |
+
new_weights2 = new_weight_matrix(num_leaf_weights, 1, mean=0.0)
|
446 |
+
new_b2 = new_bias(1)
|
447 |
+
|
448 |
+
# print("self.data_x.shape: {}".format(self.data_x.shape))
|
449 |
+
|
450 |
+
if self.sm_leaf_layer:
|
451 |
+
new_sm_weights1 = new_weight_matrix(self.n_input_variables, 1, mean=0.0)
|
452 |
+
new_sm_weights2 = new_weight_matrix(self.n_input_variables, 1, mean=0.0)
|
453 |
+
|
454 |
+
input_1 = tf.matmul(self.data_x, tf.math.softmax(new_sm_weights1, axis=0))
|
455 |
+
input_2 = tf.matmul(self.data_x, tf.math.softmax(new_sm_weights2, axis=0))
|
456 |
+
|
457 |
+
# todo: ugh
|
458 |
+
# new_weights1 = tf.constant([[1.0]])
|
459 |
+
# new_weights2 = tf.constant([[1.0]])
|
460 |
+
else:
|
461 |
+
input_1 = self.data_x
|
462 |
+
input_2 = self.data_x
|
463 |
+
|
464 |
+
# print("input_1.shape: {}".format(input_1.shape))
|
465 |
+
result_1 = tf.matmul(input_1, new_weights1) + new_b1
|
466 |
+
result_2 = tf.matmul(input_2, new_weights2) + new_b2
|
467 |
+
|
468 |
+
# print("result_1.shape: {}".format(result_1.shape))
|
469 |
+
weight_layer.extend([new_weights1, new_weights2])
|
470 |
+
bias_layer.extend([new_b1, new_b2])
|
471 |
+
if self.sm_leaf_layer:
|
472 |
+
self.leaf_sm_weights.extend([tf.math.softmax(new_sm_weights1, axis=0),
|
473 |
+
tf.math.softmax(new_sm_weights2, axis=0)])
|
474 |
+
self.non_sm_weights.extend([new_weights1, new_weights2, new_b1, new_b2])
|
475 |
+
# self.non_sm_weights.extend([new_weights1, new_weights2])
|
476 |
+
|
477 |
+
previous_output.extend([result_1, result_2])
|
478 |
+
previous_flat_output.extend([result_1, result_2])
|
479 |
+
|
480 |
+
self.W_matrices.append(weight_layer)
|
481 |
+
self.b_matrices.append(bias_layer)
|
482 |
+
self.sm_W_matrices.append([])
|
483 |
+
self.sm_applied_W_matrices.append([])
|
484 |
+
self.flattened_W_matrices.append([])
|
485 |
+
current_node = 0
|
486 |
+
|
487 |
+
# Set up parent layers, one at a time going up
|
488 |
+
for j in range(Settings.n_tree_layers):
|
489 |
+
sm_weight_layer = []
|
490 |
+
sm_applied_weight_layer = []
|
491 |
+
flattened_weight_layer = []
|
492 |
+
weight_layer = []
|
493 |
+
bias_layer = []
|
494 |
+
new_output = []
|
495 |
+
new_flat_output = []
|
496 |
+
result_layer = []
|
497 |
+
flattened_result_layer = []
|
498 |
+
|
499 |
+
for i in range(2 ** (Settings.n_tree_layers - j - 1)):
|
500 |
+
current_input_1 = previous_output[2 * i]
|
501 |
+
current_input_2 = previous_output[2 * i + 1]
|
502 |
+
nonflatten_input = operate_on_tensors(current_input_1, current_input_2, self.function_set,
|
503 |
+
use_both_for_unary=self.use_both_for_unary)
|
504 |
+
|
505 |
+
current_flat_input_1 = previous_flat_output[2 * i]
|
506 |
+
current_flat_input_2 = previous_flat_output[2 * i + 1]
|
507 |
+
flatten_input = operate_on_tensors(current_flat_input_1, current_flat_input_2,
|
508 |
+
self.function_set,
|
509 |
+
use_both_for_unary=self.use_both_for_unary)
|
510 |
+
|
511 |
+
init_op_weights = tf.reshape(self.init_op_weights[:, current_node], [-1, 1])
|
512 |
+
sm_r, flat_r, pre_sm_w, post_sm_w, flat_w = sm_no_const_selector(nonflatten_input,
|
513 |
+
flatten_input,
|
514 |
+
init_op_weights)
|
515 |
+
new_w = new_weight_matrix(1, 1, mean=1.0)
|
516 |
+
new_b = new_bias(1)
|
517 |
+
# self.non_sm_weights.extend([new_b])
|
518 |
+
|
519 |
+
sm_r = tf.math.multiply(sm_r, new_w) + new_b
|
520 |
+
flat_r = tf.multiply(flat_r, new_w) + new_b
|
521 |
+
|
522 |
+
sm_weight_layer.extend([pre_sm_w])
|
523 |
+
sm_applied_weight_layer.extend([post_sm_w])
|
524 |
+
flattened_weight_layer.extend([flat_w])
|
525 |
+
|
526 |
+
new_output.extend([sm_r])
|
527 |
+
new_flat_output.extend([flat_r])
|
528 |
+
|
529 |
+
weight_layer.extend([new_w])
|
530 |
+
bias_layer.extend([new_b])
|
531 |
+
""" self.non_sm_weights.extend([new_w, new_b])"""
|
532 |
+
|
533 |
+
result_layer.extend([sm_r])
|
534 |
+
flattened_result_layer.extend([flat_r])
|
535 |
+
|
536 |
+
current_node += 1
|
537 |
+
|
538 |
+
self.sm_W_matrices.extend([sm_weight_layer])
|
539 |
+
self.sm_applied_W_matrices.extend([sm_applied_weight_layer])
|
540 |
+
self.flattened_W_matrices.extend([flattened_weight_layer])
|
541 |
+
self.W_matrices.extend([weight_layer])
|
542 |
+
self.b_matrices.extend([bias_layer])
|
543 |
+
|
544 |
+
previous_output = new_output
|
545 |
+
previous_flat_output = new_flat_output
|
546 |
+
|
547 |
+
if self.mode == "lr":
|
548 |
+
self.y_hat_not_flat = spike(previous_output[-1])
|
549 |
+
self.y_hat = spike(previous_flat_output[-1])
|
550 |
+
else:
|
551 |
+
self.y_hat_not_flat = our_tanh(previous_output[-1], factor=10000)
|
552 |
+
self.y_hat = our_tanh(previous_flat_output[-1], factor=10000)
|
553 |
+
|
554 |
+
def reset(self, var_names=None):
|
555 |
+
tf.compat.v1.reset_default_graph()
|
556 |
+
self.build_sfl()
|
557 |
+
|
558 |
+
if var_names is not None:
|
559 |
+
self.var_names = var_names
|
560 |
+
|
561 |
+
self.log_iters = []
|
562 |
+
self.train_accuracy_log = []
|
563 |
+
self.valid_accuracy_log = []
|
564 |
+
self.test_accuracy_log = []
|
565 |
+
self.seen_eqns = []
|
566 |
+
self.seen_minimal_eqns = []
|
567 |
+
|
568 |
+
self.setup_derivative_values()
|
569 |
+
self.setup_err_values(non_const=Settings.non_const)
|
570 |
+
|
571 |
+
# TODO: really need to sort out the whole fixed_x, fixed_y thing
|
572 |
+
if self.mode == "de":
|
573 |
+
self.ivp_error_not_flat, self.ivp_error = self.setup_ivp_values(self.fixed_x, self.fixed_y)
|
574 |
+
|
575 |
+
if self.mode == "de":
|
576 |
+
self.total_error = self.total_error + self.g_error
|
577 |
+
if Settings.non_const:
|
578 |
+
self.total_error = self.total_error + self.spike_error
|
579 |
+
|
580 |
+
self.total_error = self.total_error + self.mse + self.ivp_lambda * self.ivp_error
|
581 |
+
|
582 |
+
sum_of_squares = tf.reduce_sum([tf.reduce_sum(tf.square(reg_w)) for reg_w in self.non_sm_weights])
|
583 |
+
sum_of_squares_minus_max = sum_of_squares - tf.reduce_sum([tf.reduce_max(tf.square(reg_w))
|
584 |
+
for reg_w in self.non_sm_weights])
|
585 |
+
|
586 |
+
self.regularization_penalty = tf.reduce_mean([tf.reduce_sum(tf.abs(reg_w))
|
587 |
+
for reg_w in self.non_sm_weights])
|
588 |
+
|
589 |
+
# self.regularization_penalty += sum_of_squares_minus_max
|
590 |
+
|
591 |
+
self.loss_function1 = self.mse_not_flat + self.g_error_not_flat + self.spike_error + self.ivp_lambda * self.ivp_error_not_flat
|
592 |
+
|
593 |
+
self.loss_function2 = self.mse + self.g_error + self.spike_error + self.ivp_lambda * self.ivp_error
|
594 |
+
self.loss_function2 += self.regularization_penalty * 0.05 # 0.1
|
595 |
+
|
596 |
+
self.loss_function3 = self.mse + self.g_error + self.spike_error + self.ivp_lambda * self.ivp_error
|
597 |
+
self.loss_function3 += self.regularization_penalty * 0.9 # 1.0
|
598 |
+
|
599 |
+
self.opt = tf.compat.v1.train.AdamOptimizer(self.learn_rate)
|
600 |
+
|
601 |
+
self.train_step_1 = self.opt.minimize(self.loss_function1)
|
602 |
+
self.train_step_2 = self.opt.minimize(self.loss_function2)
|
603 |
+
self.train_step_3 = self.opt.minimize(self.loss_function3)
|
604 |
+
|
605 |
+
self.init = tf.compat.v1.global_variables_initializer()
|
606 |
+
self.sess = tf.compat.v1.Session()
|
607 |
+
self.sess.run(self.init)
|
608 |
+
|
609 |
+
self.best_accuracy_so_far = 9999999
|
610 |
+
self.best_formula_so_far = ""
|
611 |
+
self.best_iter = 0
|
612 |
+
|
613 |
+
def setup_err_values(self, non_const=False):
|
614 |
+
if self.mode == "de":
|
615 |
+
self.g_error = tf.reduce_mean(tf.math.square(self.implicit_g))
|
616 |
+
self.g_not_flat = tf.reduce_mean(tf.math.square(self.implicit_g_not_flat))
|
617 |
+
else:
|
618 |
+
self.g_error = tf.Variable(0.0)
|
619 |
+
self.g_error_not_flat = tf.Variable(0.0)
|
620 |
+
self.mse = tf.reduce_mean(tf.math.squared_difference(self.y_hat, self.data_y))
|
621 |
+
self.mse_not_flat = tf.reduce_mean(tf.math.squared_difference(self.y_hat_not_flat, self.data_y))
|
622 |
+
|
623 |
+
if non_const:
|
624 |
+
self.spike_error = tf.reduce_mean(spike(self.y_hat_p1))
|
625 |
+
# tf.reduce_sum(spike(self.y_hat_p1) + spike(self.y_hat_p2) + spike(self.y_hat_p3))
|
626 |
+
|
627 |
+
def setup_ivp_values(self, fixed_x_ph, fixed_y_ph):
|
628 |
+
y_hat_err_not_flat = tf.Variable(0.0)
|
629 |
+
y_hat_err = tf.Variable(0.0)
|
630 |
+
|
631 |
+
if fixed_x_ph is not None:
|
632 |
+
y_hat_err_not_flat = tf.reduce_mean(tf.math.squared_difference(fixed_y_ph,
|
633 |
+
self.eval_formula(fixed_x_ph, flat=False)))
|
634 |
+
y_hat_err = tf.reduce_mean(tf.math.squared_difference(fixed_y_ph, self.eval_formula(fixed_x_ph)))
|
635 |
+
|
636 |
+
eye = tf.eye(self.n_input_variables)
|
637 |
+
u1 = eye[:, 0]
|
638 |
+
if Settings.fixed_x_p1 is not None and len(Settings.fixed_x_p1) > 0:
|
639 |
+
fixed_x_p1 = tf.constant(np.reshape(Settings.fixed_x_p1,
|
640 |
+
[-1, Settings.num_dims_per_feature, Settings.num_features]),
|
641 |
+
dtype="float32")
|
642 |
+
fixed_y_p1 = tf.constant(np.reshape(Settings.fixed_y_p1,
|
643 |
+
[-1, Settings.n_dims_in_output, 1]),
|
644 |
+
dtype="float32")
|
645 |
+
|
646 |
+
y_p1_fixed_hat = self.eval_formula(fixed_x_p1 + d_eps * u1 / 2)
|
647 |
+
y_p1_fixed_hat -= self.eval_formula(fixed_x_p1 - d_eps * u1 / 2)
|
648 |
+
y_p1_fixed_hat = y_p1_fixed_hat / d_eps
|
649 |
+
|
650 |
+
y_hat_err_not_flat += tf.reduce_mean(tf.math.squared_difference(fixed_y_p1, y_p1_fixed_hat))
|
651 |
+
y_hat_err += tf.reduce_mean(tf.math.squared_difference(fixed_y_p1, y_p1_fixed_hat))
|
652 |
+
|
653 |
+
if self.n_input_variables > 1:
|
654 |
+
u2 = eye[:, 1]
|
655 |
+
if Settings.fixed_x_p2 is not None and len(Settings.fixed_x_p2) > 0:
|
656 |
+
fixed_x_p2 = tf.constant(np.reshape(Settings.fixed_x_p2,
|
657 |
+
[-1, Settings.num_dims_per_feature, Settings.num_features]),
|
658 |
+
dtype="float32")
|
659 |
+
fixed_y_p2 = tf.constant(np.reshape(Settings.fixed_y_p2,
|
660 |
+
[-1, Settings.n_dims_in_output, 1]),
|
661 |
+
dtype="float32")
|
662 |
+
|
663 |
+
y_p2_fixed_hat = self.eval_formula(fixed_x_p2 + d_eps * u2 / 2)
|
664 |
+
y_p2_fixed_hat -= self.eval_formula(fixed_x_p2 - d_eps * u2 / 2)
|
665 |
+
y_p2_fixed_hat = y_p2_fixed_hat / d_eps
|
666 |
+
|
667 |
+
y_hat_err_not_flat += tf.reduce_mean(tf.math.squared_difference(fixed_y_p2, y_p2_fixed_hat))
|
668 |
+
y_hat_err += tf.reduce_mean(tf.math.squared_difference(fixed_y_p2, y_p2_fixed_hat))
|
669 |
+
|
670 |
+
return y_hat_err_not_flat, y_hat_err
|
671 |
+
|
672 |
+
def get_formula_string(self, digits=None):
|
673 |
+
eval_dict = {self.init_op_weights: self.init_op_weight_matrix,
|
674 |
+
self.init_var_weights: self.init_var_weight_matrix}
|
675 |
+
|
676 |
+
inputs = []
|
677 |
+
for i in range(len(self.W_matrices[0])):
|
678 |
+
w_matrix = self.W_matrices[0][i].eval(session=self.sess)
|
679 |
+
b_vector = self.b_matrices[0][i].eval(session=self.sess)
|
680 |
+
if self.sm_leaf_layer:
|
681 |
+
sm_vector = self.leaf_sm_weights[i].eval(session=self.sess)
|
682 |
+
print("sm_vector: {}".format(sm_vector))
|
683 |
+
|
684 |
+
new_answer = [collect_op_inputs_str(sm_vector, np.zeros([1, 1]), self.var_names)]
|
685 |
+
new_input = collect_op_inputs_str(w_matrix, b_vector, new_answer)
|
686 |
+
else:
|
687 |
+
new_input = collect_op_inputs_str(w_matrix, b_vector, self.var_names)
|
688 |
+
|
689 |
+
inputs.extend([new_input])
|
690 |
+
for layer_i in range(1, len(self.W_matrices)):
|
691 |
+
sm_applied_this_layer = self.flattened_W_matrices[layer_i]
|
692 |
+
w_this_layer = self.W_matrices[layer_i]
|
693 |
+
b_this_layer = self.b_matrices[layer_i]
|
694 |
+
new_inputs = []
|
695 |
+
for iii in range(0, len(w_this_layer)):
|
696 |
+
new_inputs.extend([operation_to_str_best(w_this_layer[iii].eval(self.sess),
|
697 |
+
b_this_layer[iii].eval(self.sess),
|
698 |
+
sm_applied_this_layer[iii].eval(session=self.sess,
|
699 |
+
feed_dict=eval_dict),
|
700 |
+
inputs[2 * iii],
|
701 |
+
inputs[2 * iii + 1],
|
702 |
+
self.function_set,
|
703 |
+
unary_both=self.use_both_for_unary)])
|
704 |
+
inputs = new_inputs
|
705 |
+
|
706 |
+
if isinstance(inputs[0], list):
|
707 |
+
return inputs[0][0]
|
708 |
+
return inputs[0]
|
709 |
+
|
710 |
+
def get_minimal_formula_string(self):
|
711 |
+
eval_dict = {self.init_op_weights: self.init_op_weight_matrix,
|
712 |
+
self.init_var_weights: self.init_var_weight_matrix}
|
713 |
+
|
714 |
+
inputs = []
|
715 |
+
for i in range(len(self.W_matrices[0])):
|
716 |
+
# w_matrix = self.W_matrices[0][i].eval(self.sess)
|
717 |
+
# inputs.extend([collect_minimal_op_inputs_str(w_matrix, self.var_names)])
|
718 |
+
inputs.append("A{}".format(i+1))
|
719 |
+
|
720 |
+
for layer_i in range(1, len(self.W_matrices)):
|
721 |
+
sm_applied_this_layer = self.flattened_W_matrices[layer_i]
|
722 |
+
w_this_layer = self.W_matrices[layer_i]
|
723 |
+
|
724 |
+
new_inputs = []
|
725 |
+
for iii in range(0, len(sm_applied_this_layer)):
|
726 |
+
new_inputs.extend([operation_to_str_best(w_this_layer[iii].eval(self.sess),
|
727 |
+
None,
|
728 |
+
sm_applied_this_layer[iii].eval(session=self.sess,
|
729 |
+
feed_dict=eval_dict),
|
730 |
+
inputs[2 * iii],
|
731 |
+
inputs[2 * iii + 1],
|
732 |
+
self.function_set,
|
733 |
+
unary_both=self.use_both_for_unary,
|
734 |
+
minimal=True)])
|
735 |
+
inputs = new_inputs
|
736 |
+
|
737 |
+
if isinstance(inputs[0], list):
|
738 |
+
return inputs[0][0]
|
739 |
+
return inputs[0]
|
740 |
+
|
741 |
+
def eval_formula(self, input_x, flat=True):
|
742 |
+
inputs = []
|
743 |
+
for i in range(len(self.W_matrices[0])):
|
744 |
+
w_matrix = self.W_matrices[0][i]
|
745 |
+
b_vector = self.b_matrices[0][i]
|
746 |
+
|
747 |
+
if self.sm_leaf_layer:
|
748 |
+
post_sm_weights = self.leaf_sm_weights[i]
|
749 |
+
sm_result = tf.matmul(input_x, post_sm_weights)
|
750 |
+
result = tf.multiply(sm_result, w_matrix) + b_vector
|
751 |
+
else:
|
752 |
+
result = tf.matmul(input_x, w_matrix) + b_vector
|
753 |
+
|
754 |
+
inputs.extend([result])
|
755 |
+
|
756 |
+
for layer_i in range(1, len(self.W_matrices)):
|
757 |
+
sm_flat_this_layer = self.flattened_W_matrices[layer_i]
|
758 |
+
sm_applied_this_layer = self.sm_applied_W_matrices[layer_i]
|
759 |
+
w_this_layer = self.W_matrices[layer_i]
|
760 |
+
b_this_layer = self.b_matrices[layer_i]
|
761 |
+
new_inputs = []
|
762 |
+
|
763 |
+
for iii in range(0, len(w_this_layer)):
|
764 |
+
post_sm_weights = sm_applied_this_layer[iii]
|
765 |
+
flat_sm_weights = sm_flat_this_layer[iii]
|
766 |
+
|
767 |
+
op_result = operate_on_tensors(inputs[2 * iii],
|
768 |
+
inputs[2 * iii + 1],
|
769 |
+
self.function_set,
|
770 |
+
use_both_for_unary=self.use_both_for_unary)
|
771 |
+
|
772 |
+
if flat:
|
773 |
+
# result, flat_sm_weights = flattened_sm_result(op_result,
|
774 |
+
# post_sm_weights,
|
775 |
+
# w_this_layer[iii],
|
776 |
+
# b_this_layer[iii])
|
777 |
+
sm_result = tf.matmul(op_result, flat_sm_weights)
|
778 |
+
else:
|
779 |
+
sm_result = tf.matmul(op_result, post_sm_weights)
|
780 |
+
result = tf.multiply(sm_result, w_this_layer[iii]) + b_this_layer[iii]
|
781 |
+
new_inputs.extend([result])
|
782 |
+
|
783 |
+
inputs = new_inputs
|
784 |
+
|
785 |
+
if self.mode == "lr":
|
786 |
+
return spike(inputs[0])
|
787 |
+
return inputs[0]
|
788 |
+
|
789 |
+
def setup_derivative_values(self):
|
790 |
+
|
791 |
+
d2_eps = 1e-2
|
792 |
+
eye = tf.eye(self.n_input_variables)
|
793 |
+
u1 = eye[:, 0]
|
794 |
+
if self.n_input_variables > 1:
|
795 |
+
u2 = eye[:, 1]
|
796 |
+
if self.n_input_variables > 2:
|
797 |
+
u3 = eye[:, 2]
|
798 |
+
# u = []
|
799 |
+
# for i in range(self.n_input_variables):
|
800 |
+
# u_i = eye[:, i]
|
801 |
+
|
802 |
+
|
803 |
+
# dy / dx1
|
804 |
+
|
805 |
+
self.y_hat_p1 = self.eval_formula(self.data_x + d_eps * u1 / 2)
|
806 |
+
self.y_hat_p1 -= self.eval_formula(self.data_x - d_eps * u1 / 2)
|
807 |
+
self.y_hat_p1 = self.y_hat_p1 / d_eps
|
808 |
+
|
809 |
+
# d^2y / dx1^2
|
810 |
+
|
811 |
+
self.y_hat_pp1 = self.eval_formula(self.data_x + d2_eps * u1)
|
812 |
+
self.y_hat_pp1 -= (2 * self.eval_formula(self.data_x))
|
813 |
+
self.y_hat_pp1 += self.eval_formula(self.data_x - d2_eps * u1)
|
814 |
+
self.y_hat_pp1 /= (d2_eps ** 2)
|
815 |
+
|
816 |
+
if self.n_input_variables > 1:
|
817 |
+
# dy / dx2
|
818 |
+
|
819 |
+
self.y_hat_p2 = self.eval_formula(self.data_x + d_eps * u2 / 2)
|
820 |
+
self.y_hat_p2 -= self.eval_formula(self.data_x - d_eps * u2 / 2)
|
821 |
+
self.y_hat_p2 = self.y_hat_p2 / d_eps
|
822 |
+
|
823 |
+
# d^2y / dx2^2
|
824 |
+
|
825 |
+
self.y_hat_pp2 = self.eval_formula(self.data_x + d2_eps * u2)
|
826 |
+
self.y_hat_pp2 -= (2 * self.eval_formula(self.data_x))
|
827 |
+
self.y_hat_pp2 += self.eval_formula(self.data_x - d2_eps * u2)
|
828 |
+
self.y_hat_pp2 /= (d2_eps ** 2)
|
829 |
+
|
830 |
+
# d^2y / dx1 dx2
|
831 |
+
self.y_hat_pp12 = self.eval_formula(self.data_x + d2_eps * (u1 + u2))
|
832 |
+
self.y_hat_pp12 -= self.eval_formula(self.data_x - d2_eps * (u1 - u2))
|
833 |
+
self.y_hat_pp12 -= self.eval_formula(self.data_x - d2_eps * (u2 - u1))
|
834 |
+
self.y_hat_pp12 -= self.eval_formula(self.data_x + d2_eps * (-u1 - u2))
|
835 |
+
self.y_hat_pp12 /= (4 * d2_eps ** 2)
|
836 |
+
else:
|
837 |
+
self.y_hat_p2 = None
|
838 |
+
self.y_hat_pp2 = None
|
839 |
+
self.y_hat_pp12 = None
|
840 |
+
|
841 |
+
if self.n_input_variables > 2:
|
842 |
+
# dy / dx2
|
843 |
+
|
844 |
+
self.y_hat_p3 = self.eval_formula(self.data_x + d_eps * u3 / 2)
|
845 |
+
self.y_hat_p3 -= self.eval_formula(self.data_x - d_eps * u3 / 2)
|
846 |
+
self.y_hat_p3 = self.y_hat_p3 / d_eps
|
847 |
+
else:
|
848 |
+
self.y_hat_p3 = None
|
849 |
+
|
850 |
+
|
851 |
+
self.y_hat_p_not_flat = self.eval_formula(self.data_x + d_eps * u1 / 2, flat=False)
|
852 |
+
self.y_hat_p_not_flat -= self.eval_formula(self.data_x - d_eps * u1 / 2, flat=False)
|
853 |
+
self.y_hat_p_not_flat = self.y_hat_p_not_flat / d_eps
|
854 |
+
|
855 |
+
self.y_hat_pp_not_flat = self.eval_formula(self.data_x + d_eps * u1, flat=False)
|
856 |
+
self.y_hat_pp_not_flat -= 2 * self.eval_formula(self.data_x, flat=False)
|
857 |
+
self.y_hat_pp_not_flat += self.eval_formula(self.data_x - d_eps * u1, flat=False)
|
858 |
+
self.y_hat_pp_not_flat = self.y_hat_pp_not_flat / d_eps ** 2
|
859 |
+
|
860 |
+
self.implicit_g = our_tanh(implicit_function(self.data_x, self.y_hat,
|
861 |
+
[self.y_hat_p1, self.y_hat_p2, self.y_hat_p3],
|
862 |
+
[self.y_hat_pp1, self.y_hat_pp2, self.y_hat_pp12]))
|
863 |
+
self.implicit_g_not_flat = our_tanh(implicit_function(self.data_x, self.y_hat_not_flat,
|
864 |
+
[self.y_hat_p_not_flat, self.y_hat_p2, self.y_hat_p3],
|
865 |
+
[self.y_hat_pp_not_flat, self.y_hat_pp2,
|
866 |
+
self.y_hat_pp12]))
|
867 |
+
|
868 |
+
""" Like reset, but does not erase records of training history.
|
869 |
+
It only restarts training from a new random initialization. """
|
870 |
+
def soft_reset(self):
|
871 |
+
self.init = tf.compat.v1.global_variables_initializer()
|
872 |
+
self.saver = tf.compat.v1.train.Saver()
|
873 |
+
self.sess = tf.compat.v1.Session()
|
874 |
+
self.sess.run(self.init)
|
875 |
+
|
876 |
+
self.best_accuracy_so_far = 9999999
|
877 |
+
self.best_formula_so_far = ""
|
878 |
+
self.best_iter = 0
|
879 |
+
|
880 |
+
# Not needed, but don't touch
|
881 |
+
def set_init_op_weight_matrix(self, init_op_weight_matrix):
|
882 |
+
self.init_op_weight_matrix = init_op_weight_matrix
|
883 |
+
|
884 |
+
# Not needed, but don't touch
|
885 |
+
def set_init_var_weight_matrix(self, init_var_weight_matrix):
|
886 |
+
self.init_var_weight_matrix = init_var_weight_matrix
|
887 |
+
|
888 |
+
# Not 100% tested
|
889 |
+
def make_y_multi_safe(self, old_y):
|
890 |
+
if isinstance(old_y, list):
|
891 |
+
new_y = np.array(old_y)
|
892 |
+
new_y.reshape([-1, self.n_dims_in_output, 1])
|
893 |
+
else:
|
894 |
+
new_y = old_y.copy()
|
895 |
+
if len(new_y.shape) == 1:
|
896 |
+
assert (self.n_dims_in_output == 1)
|
897 |
+
new_y = [[[y_value] for _ in range(self.n_dims_per_variable)] for y_value in new_y]
|
898 |
+
new_y = np.array(new_y)
|
899 |
+
elif len(new_y.shape) == 2:
|
900 |
+
assert (self.n_dims_in_output == 1)
|
901 |
+
new_y = [[y_value for _ in range(self.n_dims_per_variable)] for y_value in new_y]
|
902 |
+
new_y = np.array(new_y)
|
903 |
+
elif new_y.shape[1] < self.n_dims_per_variable:
|
904 |
+
assert (self.n_dims_in_output == 1)
|
905 |
+
new_y = [[y_value[0] for _ in range(self.n_dims_per_variable)] for y_value in new_y]
|
906 |
+
new_y = np.array(new_y)
|
907 |
+
return new_y
|
908 |
+
|
909 |
+
def get_simple_formula(self, digits=None):
|
910 |
+
full_formula = self.get_formula_string()
|
911 |
+
return DataUtils.simplify_formula(full_formula, digits=digits)
|
912 |
+
|
913 |
+
# todo: want total or mean square error?
|
914 |
+
def test(self, x, y=None):
|
915 |
+
test_dict = {self.data_x: x,
|
916 |
+
self.init_op_weights: self.init_op_weight_matrix,
|
917 |
+
self.init_var_weights: self.init_var_weight_matrix}
|
918 |
+
if y is not None:
|
919 |
+
test_dict[self.data_y] = y
|
920 |
+
return self.sess.run(self.total_error, feed_dict=test_dict)
|
921 |
+
|
922 |
+
# Runs train process a number of times on a limited number of train steps.
|
923 |
+
# Returns the best formula found during that experience.
|
924 |
+
# If init_ops is given, it will start off with ops initialized accordingly.
|
925 |
+
# If it is None, then ops will be initialized randomly.
|
926 |
+
# If it is 0, then ops will have no initialization.
|
927 |
+
# Same with init_vars.
|
928 |
+
def train(self, x, y=None, init_op_weight_matrix=None, init_var_weight_matrix=None,
|
929 |
+
test_x=None, test_y=None):
|
930 |
+
n_rounds = Settings.num_train_steps_in_repeat_mode
|
931 |
+
|
932 |
+
batch_size = min(Settings.max_training_batch_size, int(len(x) / 2))
|
933 |
+
|
934 |
+
train_set_size = len(x)
|
935 |
+
train_x = np.array(x, dtype=np.float32)
|
936 |
+
|
937 |
+
if self.mode in ["de"]:
|
938 |
+
y = [0 for _ in range(x.shape[0])]
|
939 |
+
if test_x is not None:
|
940 |
+
test_y = [0 for _ in range(test_x.shape[0])]
|
941 |
+
# elif self.mode == ["sr", "lr"]:
|
942 |
+
# y = DataUtils.true_function(x)
|
943 |
+
# if test_x is not None:
|
944 |
+
# test_y = DataUtils.true_function(test_x)
|
945 |
+
|
946 |
+
train_y = self.make_y_multi_safe(y)
|
947 |
+
|
948 |
+
if test_y is not None:
|
949 |
+
test_y = self.make_y_multi_safe(test_y)
|
950 |
+
|
951 |
+
if init_op_weight_matrix is not None:
|
952 |
+
self.set_init_op_weight_matrix(init_op_weight_matrix)
|
953 |
+
|
954 |
+
if init_var_weight_matrix is not None:
|
955 |
+
self.set_init_var_weight_matrix(init_var_weight_matrix)
|
956 |
+
|
957 |
+
target_y = self.y_hat
|
958 |
+
show_gt = False
|
959 |
+
|
960 |
+
if Settings.show_output:
|
961 |
+
print("Starting actual training!")
|
962 |
+
start_time = time.time()
|
963 |
+
old_time = time.time()
|
964 |
+
time_spent_training = 0
|
965 |
+
time_getting_formulas = 0
|
966 |
+
time_getting_scores = 0
|
967 |
+
time_plotting = 0
|
968 |
+
other_time = 0
|
969 |
+
|
970 |
+
for i in range(1, n_rounds + 1):
|
971 |
+
mini_start_time = time.time()
|
972 |
+
train_batch_x, train_batch_y, valid_batch_x, valid_batch_y = DataUtils.get_samples(train_set_size,
|
973 |
+
batch_size,
|
974 |
+
train_x, train_y)
|
975 |
+
other_time += time.time() - mini_start_time
|
976 |
+
|
977 |
+
training_dict = {self.data_x: train_batch_x,
|
978 |
+
self.data_y: train_batch_y,
|
979 |
+
self.init_op_weights: self.init_op_weight_matrix,
|
980 |
+
self.init_var_weights: self.init_var_weight_matrix}
|
981 |
+
|
982 |
+
valid_batch_dict = {self.data_x: valid_batch_x,
|
983 |
+
self.data_y: valid_batch_y,
|
984 |
+
self.init_op_weights: self.init_op_weight_matrix,
|
985 |
+
self.init_var_weights: self.init_var_weight_matrix}
|
986 |
+
|
987 |
+
test_dict = {self.data_x: test_x, self.data_y: test_y,
|
988 |
+
self.init_op_weights: self.init_op_weight_matrix,
|
989 |
+
self.init_var_weights: self.init_var_weight_matrix}
|
990 |
+
|
991 |
+
""" Actual training happens here """
|
992 |
+
mini_start_time = time.time()
|
993 |
+
if i < n_rounds * Settings.t1_fraction:
|
994 |
+
self.sess.run(self.train_step_1, feed_dict=training_dict)
|
995 |
+
elif i < n_rounds * Settings.t2_fraction:
|
996 |
+
self.sess.run(self.train_step_2, feed_dict=training_dict)
|
997 |
+
else:
|
998 |
+
self.sess.run(self.train_step_3, feed_dict=training_dict)
|
999 |
+
|
1000 |
+
time_spent_training += (time.time() - mini_start_time)
|
1001 |
+
|
1002 |
+
""" Save formulas, accuracy, etc. """
|
1003 |
+
if (i % Settings.plot_frequency == 0 or i % Settings.output_freq == 0) and Settings.keep_logs:
|
1004 |
+
|
1005 |
+
# Save current formula to make list of all formulas seen
|
1006 |
+
current_formula = "(Formula not saved)"
|
1007 |
+
if Settings.save_all_formulas:
|
1008 |
+
mini_start_time = time.time()
|
1009 |
+
current_formula = self.get_simple_formula(digits=4)
|
1010 |
+
time_getting_formulas += (time.time() - mini_start_time)
|
1011 |
+
|
1012 |
+
if current_formula not in self.seen_eqns:
|
1013 |
+
self.seen_eqns.append(current_formula)
|
1014 |
+
|
1015 |
+
# Get results from validation set.
|
1016 |
+
mini_start_time = time.time()
|
1017 |
+
[valid_acc, y_pr_v] = self.sess.run([self.total_error, target_y], feed_dict=valid_batch_dict)
|
1018 |
+
# Get results from test set.
|
1019 |
+
if test_x is not None:
|
1020 |
+
[test_acc, y_pr_test] = self.sess.run([self.total_error, target_y], feed_dict=test_dict)
|
1021 |
+
|
1022 |
+
y_gold_v = valid_batch_y.reshape([-1, self.n_dims_per_variable, 1])[0].tolist()
|
1023 |
+
y_hat_v = y_pr_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1024 |
+
|
1025 |
+
time_getting_scores += (time.time() - mini_start_time)
|
1026 |
+
|
1027 |
+
mini_start_time = time.time()
|
1028 |
+
[valid_acc, g_pr_v] = self.sess.run([self.total_error, self.implicit_g], feed_dict=valid_batch_dict)
|
1029 |
+
g_hat_val = g_pr_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1030 |
+
g_hat_1d_val = [y_value[0][0] for y_value in g_hat_val]
|
1031 |
+
g_tru_1d_val = [y_value[0][0] for y_value in valid_batch_y]
|
1032 |
+
g_hat_1d_test = None
|
1033 |
+
g_tru_1d_test = None
|
1034 |
+
|
1035 |
+
[yp_v, ypp_v] = self.sess.run([self.y_hat_p1, self.y_hat_pp1], feed_dict=valid_batch_dict)
|
1036 |
+
y_p1_v = yp_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1037 |
+
y_pp1_v = ypp_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1038 |
+
|
1039 |
+
[yp2_v, ypp2_v] = self.sess.run([self.y_hat_p2, self.y_hat_pp2], feed_dict=valid_batch_dict)
|
1040 |
+
y_p2_v = yp2_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1041 |
+
y_pp2_v = ypp2_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1042 |
+
|
1043 |
+
time_getting_scores += (time.time() - mini_start_time)
|
1044 |
+
|
1045 |
+
if test_x is not None:
|
1046 |
+
mini_start_time = time.time()
|
1047 |
+
[test_acc, g_pr_test] = self.sess.run([self.total_error, self.implicit_g], feed_dict=test_dict)
|
1048 |
+
|
1049 |
+
g_hat_test = g_pr_test.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1050 |
+
g_hat_1d_test = [g_value[0][0] for g_value in g_hat_test]
|
1051 |
+
g_tru_1d_test = [g_value[0][0] for g_value in test_y]
|
1052 |
+
time_getting_scores += (time.time() - mini_start_time)
|
1053 |
+
|
1054 |
+
# Update best formula seen based on validation error.
|
1055 |
+
if Settings.save_all_formulas:
|
1056 |
+
if valid_acc < self.best_accuracy_so_far:
|
1057 |
+
self.best_accuracy_so_far = valid_acc
|
1058 |
+
self.best_formula_so_far = current_formula
|
1059 |
+
self.best_iter = i
|
1060 |
+
|
1061 |
+
# We only can make plots using y values if y is 1d.
|
1062 |
+
if self.n_dims_in_output == 1:
|
1063 |
+
|
1064 |
+
mini_start_time = time.time()
|
1065 |
+
|
1066 |
+
y_hat_1d_val = [y_value[0][0] for y_value in y_hat_v]
|
1067 |
+
y_tru_1d_val = [y_value[0][0] for y_value in valid_batch_y]
|
1068 |
+
y_hat_1d_test = None
|
1069 |
+
y_tru_1d_test = None
|
1070 |
+
|
1071 |
+
if test_x is not None:
|
1072 |
+
y_hat_test = y_pr_test.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1073 |
+
y_hat_1d_test = [y_value[0][0] for y_value in y_hat_test]
|
1074 |
+
y_tru_1d_test = [y_value[0][0] for y_value in test_y]
|
1075 |
+
other_time += (time.time() - mini_start_time)
|
1076 |
+
|
1077 |
+
if self.mode in ["sr", "lr"]:
|
1078 |
+
# Plot predicted y value against actual y value.
|
1079 |
+
mini_start_time = time.time()
|
1080 |
+
DataUtils.plot_predicted_vs_actual(y_hat_1d_val, y_tru_1d_val,
|
1081 |
+
y_hat_1d_test, y_tru_1d_test,
|
1082 |
+
self.name,
|
1083 |
+
set_name="Iteration {}".format(i))
|
1084 |
+
time_plotting += (time.time() - mini_start_time)
|
1085 |
+
|
1086 |
+
# DataUtils.plot_2d_curve(x_1d_val, y_tru_1d_val, y_hat_1d_val, None, None, None)
|
1087 |
+
|
1088 |
+
# If x is also 1d, we can plot the function itself.
|
1089 |
+
if self.n_input_variables == 1:
|
1090 |
+
|
1091 |
+
# Plot the actual function we learned.
|
1092 |
+
mini_start_time = time.time()
|
1093 |
+
x_1d_val = [x_value[0][0] for x_value in valid_batch_x]
|
1094 |
+
x_1d_test = None
|
1095 |
+
if test_x is not None:
|
1096 |
+
x_1d_test = [x_value[0][0] for x_value in test_x]
|
1097 |
+
other_time += (time.time() - mini_start_time)
|
1098 |
+
|
1099 |
+
mini_start_time = time.time()
|
1100 |
+
DataUtils.plot_1d_curve(x_1d_val, y_tru_1d_val, y_hat_1d_val,
|
1101 |
+
x_1d_test, y_tru_1d_test, y_hat_1d_test,
|
1102 |
+
file_suffix="_y",
|
1103 |
+
title="Learned function: Iteration {}".format(i),
|
1104 |
+
show_ground_truth=show_gt)
|
1105 |
+
time_plotting += (time.time() - mini_start_time)
|
1106 |
+
|
1107 |
+
# Plot the g output values, in implicit case
|
1108 |
+
if test_x is not None:
|
1109 |
+
mini_start_time = time.time()
|
1110 |
+
DataUtils.plot_1d_curve(x_1d_val, g_tru_1d_val, g_hat_1d_val,
|
1111 |
+
x_1d_test, g_tru_1d_test, g_hat_1d_test,
|
1112 |
+
file_suffix="_g",
|
1113 |
+
title="Output of g: Iteration {}".format(i))
|
1114 |
+
|
1115 |
+
time_plotting += time.time() - mini_start_time
|
1116 |
+
|
1117 |
+
elif self.n_input_variables == 2:
|
1118 |
+
# Plot the actual function we learned.
|
1119 |
+
mini_start_time = time.time()
|
1120 |
+
|
1121 |
+
plot2d_x1 = np.arange(Settings.test_scope[0], Settings.test_scope[1], 0.1)
|
1122 |
+
plot2d_x2 = np.arange(Settings.test_scope[0], Settings.test_scope[1], 0.1)
|
1123 |
+
|
1124 |
+
plot2d_x1_m, plot2d_x2_m = np.meshgrid(plot2d_x1, plot2d_x2)
|
1125 |
+
plot2d_x1 = np.reshape(plot2d_x1_m, [-1, 1, 1])
|
1126 |
+
plot2d_x2 = np.reshape(plot2d_x2_m, [-1, 1, 1])
|
1127 |
+
plot2d_x1x2 = np.concatenate([plot2d_x1, plot2d_x2], axis=-1)
|
1128 |
+
[plot2d_y, plot2d_g] = self.sess.run([target_y, self.implicit_g],
|
1129 |
+
feed_dict={self.data_x: plot2d_x1x2,
|
1130 |
+
self.init_op_weights: self.init_op_weight_matrix,
|
1131 |
+
self.init_var_weights: self.init_var_weight_matrix})
|
1132 |
+
|
1133 |
+
if self.mode == "sr":
|
1134 |
+
plot2d_g = DataUtils.true_function(plot2d_x1x2)
|
1135 |
+
|
1136 |
+
plot2d_y_m = np.reshape(plot2d_y, plot2d_x1_m.shape)
|
1137 |
+
plot2d_g_m = np.reshape(plot2d_g, plot2d_x1_m.shape)
|
1138 |
+
|
1139 |
+
DataUtils.plot_2d_curve(plot2d_x1_m, plot2d_x2_m, plot2d_y_m, plot2d_g_m)
|
1140 |
+
|
1141 |
+
time_plotting += (time.time() - mini_start_time)
|
1142 |
+
|
1143 |
+
if Settings.keep_logs:
|
1144 |
+
mini_start_time = time.time()
|
1145 |
+
self.train_accuracy_log.append(self.test(train_x, train_y))
|
1146 |
+
# self.valid_accuracy_log.append(valid_acc)
|
1147 |
+
self.valid_accuracy_log.append(self.test(valid_batch_x, valid_batch_y))
|
1148 |
+
|
1149 |
+
# self.log_iters.append(i)
|
1150 |
+
if len(self.log_iters) == 0:
|
1151 |
+
self.log_iters.append(i)
|
1152 |
+
else:
|
1153 |
+
self.log_iters.append(self.log_iters[-1] + Settings.plot_frequency)
|
1154 |
+
|
1155 |
+
accuracies_to_plot = [self.train_accuracy_log,
|
1156 |
+
self.valid_accuracy_log]
|
1157 |
+
accuracy_type_names = ["Training Error", "Validation Error"]
|
1158 |
+
if test_x is not None:
|
1159 |
+
self.test_accuracy_log.append(test_acc)
|
1160 |
+
accuracies_to_plot.append(self.test_accuracy_log)
|
1161 |
+
accuracy_type_names.append("Test Error")
|
1162 |
+
|
1163 |
+
time_getting_scores += (time.time() - mini_start_time)
|
1164 |
+
|
1165 |
+
mini_start_time = time.time()
|
1166 |
+
DataUtils.plot_accuracy_over_time(self.log_iters, accuracies_to_plot, accuracy_type_names)
|
1167 |
+
|
1168 |
+
time_plotting += time.time() - mini_start_time
|
1169 |
+
|
1170 |
+
if i % Settings.output_freq == 0 and Settings.show_output:
|
1171 |
+
|
1172 |
+
if not Settings.keep_logs:
|
1173 |
+
# Get results from validation set.
|
1174 |
+
mini_start_time = time.time()
|
1175 |
+
[valid_acc, y_pr_v] = self.sess.run([self.total_error, target_y], feed_dict=valid_batch_dict)
|
1176 |
+
# Get results from test set.
|
1177 |
+
if test_x is not None:
|
1178 |
+
[test_acc, y_pr_test] = self.sess.run([self.total_error, target_y], feed_dict=test_dict)
|
1179 |
+
y_hat_v = y_pr_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1180 |
+
|
1181 |
+
g_pr_v = self.sess.run(self.implicit_g, feed_dict=valid_batch_dict)
|
1182 |
+
g_hat_val = g_pr_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1183 |
+
|
1184 |
+
[yp1_v, ypp1_v] = self.sess.run([self.y_hat_p1, self.y_hat_pp1],
|
1185 |
+
feed_dict={self.data_x: valid_batch_x,
|
1186 |
+
self.init_op_weights: self.init_op_weight_matrix,
|
1187 |
+
self.init_var_weights: self.init_var_weight_matrix})
|
1188 |
+
y_p1_v = yp1_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1189 |
+
y_pp1_v = ypp1_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1190 |
+
|
1191 |
+
[yp2_v, ypp2_v] = self.sess.run([self.y_hat_p2, self.y_hat_pp2],
|
1192 |
+
feed_dict={self.data_x: valid_batch_x,
|
1193 |
+
self.init_op_weights: self.init_op_weight_matrix,
|
1194 |
+
self.init_var_weights: self.init_var_weight_matrix})
|
1195 |
+
y_p2_v = yp2_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1196 |
+
y_pp2_v = ypp2_v.reshape([-1, self.n_dims_per_variable, 1]).tolist()
|
1197 |
+
|
1198 |
+
time_getting_scores += (time.time() - mini_start_time)
|
1199 |
+
|
1200 |
+
print()
|
1201 |
+
|
1202 |
+
print('Iteration {}:'.format(i))
|
1203 |
+
|
1204 |
+
mini_start_time = time.time()
|
1205 |
+
formula_as_string = self.get_formula_string(digits=4)
|
1206 |
+
dotdot = ""
|
1207 |
+
if len(formula_as_string) > Settings.max_formula_output_length:
|
1208 |
+
dotdot = " ..."
|
1209 |
+
print("# Current Model: {}{}".format(formula_as_string[:Settings.max_formula_output_length], dotdot))
|
1210 |
+
|
1211 |
+
simple_formula = self.get_simple_formula(digits=4)
|
1212 |
+
dotdot = ""
|
1213 |
+
if len(simple_formula) > Settings.max_formula_output_length:
|
1214 |
+
dotdot = " ..."
|
1215 |
+
print("# AKA: {}{}".format(simple_formula[:Settings.max_formula_output_length], dotdot))
|
1216 |
+
|
1217 |
+
minimal_eqn = self.get_minimal_formula_string()
|
1218 |
+
print("# Simple: {}".format(minimal_eqn))
|
1219 |
+
if minimal_eqn not in self.seen_minimal_eqns:
|
1220 |
+
self.seen_minimal_eqns.append(minimal_eqn)
|
1221 |
+
|
1222 |
+
if "**" in simple_formula:
|
1223 |
+
print("(Has a power)")
|
1224 |
+
|
1225 |
+
time_getting_formulas += (time.time() - mini_start_time)
|
1226 |
+
|
1227 |
+
print(
|
1228 |
+
" Length: {} ({})".format(len(formula_as_string), len("{}".format(simple_formula))))
|
1229 |
+
|
1230 |
+
print(" Train batch size: {}".format(train_batch_x.shape))
|
1231 |
+
print(" Valid batch size: {}".format(valid_batch_x.shape))
|
1232 |
+
print(" # Mnml eqns seen: {}".format(len(self.seen_minimal_eqns)))
|
1233 |
+
|
1234 |
+
iters_per_min = Settings.output_freq * 60 / (time.time() - old_time)
|
1235 |
+
print(' Iters per minute: {:.2f}'.format(iters_per_min))
|
1236 |
+
total_time = time.time() - start_time
|
1237 |
+
print(' Time so far: {:.2f} minutes'.format(total_time / 60.0))
|
1238 |
+
print(' ({:.1%} training, {:.1%} scoring, {:.1%} formulas)'.format(
|
1239 |
+
time_spent_training / total_time,
|
1240 |
+
time_getting_scores / total_time,
|
1241 |
+
time_getting_formulas / total_time))
|
1242 |
+
print(' ({:.1%} plotting, {:.1%} other)'.format(time_plotting / total_time,
|
1243 |
+
other_time / total_time))
|
1244 |
+
print(' Est. time left: {:.2f} minutes'.format((n_rounds - i) / iters_per_min))
|
1245 |
+
|
1246 |
+
print('Error values:')
|
1247 |
+
mini_start_time = time.time()
|
1248 |
+
|
1249 |
+
curr_errs = self.sess.run([self.g_error, self.ivp_error,
|
1250 |
+
self.spike_error, self.total_error],
|
1251 |
+
feed_dict=valid_batch_dict)
|
1252 |
+
if self.mode == "de":
|
1253 |
+
print(' g-err Valid: {}'.format(curr_errs[0]))
|
1254 |
+
print(' IVP Valid: {}'.format(curr_errs[1]))
|
1255 |
+
if Settings.non_const:
|
1256 |
+
print(' Spike err: {}'.format(curr_errs[2]))
|
1257 |
+
print(' Tot. Val.: {}'.format(curr_errs[3]))
|
1258 |
+
if np.abs(curr_errs[3] - (curr_errs[0] + self.ivp_lambda * curr_errs[1] + curr_errs[2])) > 1e-4:
|
1259 |
+
print("Something is wrong.")
|
1260 |
+
|
1261 |
+
|
1262 |
+
# Hope we don't get nans, but break out if we do.
|
1263 |
+
nans = np.isnan(curr_errs[0])
|
1264 |
+
if nans:
|
1265 |
+
break
|
1266 |
+
|
1267 |
+
if test_x is not None:
|
1268 |
+
print(' Tot. Test: {}'.format(test_acc))
|
1269 |
+
|
1270 |
+
time_getting_scores += (time.time() - mini_start_time)
|
1271 |
+
|
1272 |
+
print('Performance on sample validation data:')
|
1273 |
+
|
1274 |
+
print_str = ""
|
1275 |
+
for feature_i in range(Settings.num_features):
|
1276 |
+
print_str += "{}\t\t".format(self.var_names[feature_i])
|
1277 |
+
print_str += "|\t"
|
1278 |
+
# print_str += "y_tru\t"
|
1279 |
+
if self.mode == "de":
|
1280 |
+
print_str += "g_hat\t"
|
1281 |
+
elif self.mode == "sr" or self.mode == "lr":
|
1282 |
+
print_str += "y_tru\t"
|
1283 |
+
print_str += "y_hat\t"
|
1284 |
+
|
1285 |
+
if self.mode == "de":
|
1286 |
+
print_str += "y_p1\t"
|
1287 |
+
print_str += "y_pp1\t"
|
1288 |
+
print_str += "y_p2\t"
|
1289 |
+
print_str += "y_pp2\t"
|
1290 |
+
print(print_str)
|
1291 |
+
line_len = len(print_str) + 16
|
1292 |
+
print("=" * line_len)
|
1293 |
+
|
1294 |
+
var_range = range(self.n_input_variables)
|
1295 |
+
num_pts_to_show = 5
|
1296 |
+
if self.mode in ["sr", "lr"]:
|
1297 |
+
y_tru_v = valid_batch_y[:num_pts_to_show, :, :]
|
1298 |
+
# y_tru_v = DataUtils.predict_from_formula(Settings.true_eqn, valid_batch_x[:num_pts_to_show, :, :])
|
1299 |
+
|
1300 |
+
for datapoint_i in range(min(valid_batch_x.shape[0], num_pts_to_show)):
|
1301 |
+
comps_to_show = range(self.n_dims_per_variable)
|
1302 |
+
if self.n_dims_per_variable > 9:
|
1303 |
+
comps_to_show = [0, 1, 2, -1]
|
1304 |
+
for component_j in comps_to_show:
|
1305 |
+
if component_j == -1:
|
1306 |
+
print(" ... ")
|
1307 |
+
print_str = ""
|
1308 |
+
for var_k in var_range:
|
1309 |
+
x_ijk = valid_batch_x[datapoint_i, component_j, var_k]
|
1310 |
+
print_str += "{:.3f}\t".format(x_ijk)
|
1311 |
+
print_str += "|\t"
|
1312 |
+
# print_str += "{:.3f}\t".format(valid_batch_y[datapoint_i, component_j, 0])
|
1313 |
+
|
1314 |
+
if self.mode == "de":
|
1315 |
+
print_str += "{:.3f}\t".format(g_hat_val[datapoint_i][component_j][0])
|
1316 |
+
elif self.mode in ["sr", "lr"]:
|
1317 |
+
print_str += "{:.3f}\t".format(y_tru_v[datapoint_i][0][0])
|
1318 |
+
|
1319 |
+
print_str += "{:.3f}\t".format(y_hat_v[datapoint_i][component_j][0])
|
1320 |
+
|
1321 |
+
if self.mode == "de":
|
1322 |
+
print_str += "{:.3f}\t".format(y_p1_v[datapoint_i][component_j][0])
|
1323 |
+
print_str += "{:.3f}\t".format(y_pp1_v[datapoint_i][component_j][0])
|
1324 |
+
print_str += "{:.3f}\t".format(y_p2_v[datapoint_i][component_j][0])
|
1325 |
+
print_str += "{:.3f}\t".format(y_pp2_v[datapoint_i][component_j][0])
|
1326 |
+
|
1327 |
+
print(print_str)
|
1328 |
+
print("-" * line_len)
|
1329 |
+
print()
|
1330 |
+
|
1331 |
+
old_time = time.time()
|
1332 |
+
|
1333 |
+
if Settings.show_output:
|
1334 |
+
print('Finished training at {:%H:%M:%S}.\n'.format(datetime.datetime.now()))
|
1335 |
+
end_time = time.time()
|
1336 |
+
total_time = end_time - start_time
|
1337 |
+
|
1338 |
+
print('Took {:.2f} seconds to finish.'.format(total_time))
|
1339 |
+
print(' ({:.1%} training, {:.1%} scoring, {:.1%} formulas)'.format(time_spent_training / total_time,
|
1340 |
+
time_getting_scores / total_time,
|
1341 |
+
time_getting_formulas / total_time))
|
1342 |
+
print(' ({:.1%} plotting, {:.1%} other)'.format(time_plotting / total_time,
|
1343 |
+
other_time / total_time))
|
1344 |
+
print('Average of {:.2f} training steps per minute.'.format(
|
1345 |
+
60 * n_rounds / total_time))
|
1346 |
+
print('Average of {:.2f} minutes per 10000 training steps.'.format(
|
1347 |
+
10000 * total_time / (60 * n_rounds)))
|
1348 |
+
|
1349 |
+
print()
|
1350 |
+
if Settings.save_all_formulas:
|
1351 |
+
print("Best formula had accuracy {:.3f} and was seen at iteration {}:".format(
|
1352 |
+
self.best_accuracy_so_far,
|
1353 |
+
self.best_iter))
|
1354 |
+
print("{}".format(self.best_formula_so_far)[:1000])
|
1355 |
+
else:
|
1356 |
+
final_acc = self.sess.run(self.total_error, feed_dict={self.data_x: train_x,
|
1357 |
+
self.data_y: train_y,
|
1358 |
+
self.init_op_weights: self.init_op_weight_matrix,
|
1359 |
+
self.init_var_weights: self.init_var_weight_matrix})
|
1360 |
+
print("Final formula had accuracy {:.3f}:".format(final_acc))
|
1361 |
+
|
1362 |
+
print("{}".format(self.get_simple_formula(digits=4))[:1000])
|
1363 |
+
print()
|
1364 |
+
|
1365 |
+
return self.get_simple_formula(digits=4)
|
1366 |
+
|
1367 |
+
def repeat_train(self, x, y=None,
|
1368 |
+
num_repeats=Settings.num_train_repeat_processes,
|
1369 |
+
test_x=None, test_y=None,
|
1370 |
+
verbose=True):
|
1371 |
+
|
1372 |
+
# we still reduce train set size if only 1 repeat
|
1373 |
+
train_set_size = int(len(x) * Settings.quick_train_fraction + 0.1)
|
1374 |
+
|
1375 |
+
x = np.array(x)
|
1376 |
+
|
1377 |
+
if y is not None:
|
1378 |
+
y = np.array(y)
|
1379 |
+
|
1380 |
+
sample = np.random.choice(range(x.shape[0]), size=train_set_size, replace=False)
|
1381 |
+
train_x = x[sample][:]
|
1382 |
+
if y is not None:
|
1383 |
+
train_y = y[sample]
|
1384 |
+
|
1385 |
+
out_sample = [aaa for aaa in range(x.shape[0]) if aaa not in sample]
|
1386 |
+
valid_x = x[out_sample][:]
|
1387 |
+
if y is not None:
|
1388 |
+
valid_y = y[out_sample]
|
1389 |
+
valid_y = self.make_y_multi_safe(valid_y)
|
1390 |
+
|
1391 |
+
best_formula = ""
|
1392 |
+
best_iter = 0
|
1393 |
+
best_validation = 999999
|
1394 |
+
best_err = 999999
|
1395 |
+
old_time = time.time()
|
1396 |
+
|
1397 |
+
if verbose:
|
1398 |
+
print("Beginning {} repeat sessions of {} iterations each.".format(num_repeats,
|
1399 |
+
Settings.num_train_steps_in_repeat_mode))
|
1400 |
+
print()
|
1401 |
+
start_time = time.time()
|
1402 |
+
old_time = start_time
|
1403 |
+
|
1404 |
+
for train_iter in range(1, 1 + num_repeats):
|
1405 |
+
if verbose:
|
1406 |
+
print("Repeated train session {} of {}.".format(train_iter, num_repeats))
|
1407 |
+
|
1408 |
+
|
1409 |
+
self.soft_reset()
|
1410 |
+
|
1411 |
+
self.set_init_op_weight_matrix(choices_to_init_weight_matrix(Settings.initialize_ops,
|
1412 |
+
self.function_set))
|
1413 |
+
self.set_init_var_weight_matrix(choices_to_init_weight_matrix(np.zeros([2 ** self.n_tree_layers]),
|
1414 |
+
self.var_names))
|
1415 |
+
|
1416 |
+
|
1417 |
+
self.train(train_x, train_y, test_x=test_x, test_y=test_y)
|
1418 |
+
|
1419 |
+
valid_err = self.test(valid_x, valid_y)
|
1420 |
+
|
1421 |
+
current_time = time.time()
|
1422 |
+
if verbose:
|
1423 |
+
# print(self.get_simple_formula())
|
1424 |
+
print("Attained validation error: {:.5f}".format(valid_err))
|
1425 |
+
|
1426 |
+
if valid_err < best_validation:
|
1427 |
+
best_validation = valid_err
|
1428 |
+
best_formula = self.get_simple_formula()
|
1429 |
+
best_iter = train_iter
|
1430 |
+
if test_x is not None:
|
1431 |
+
safe_test_y = self.make_y_multi_safe(test_y)
|
1432 |
+
best_err = self.test(test_x, safe_test_y)
|
1433 |
+
else:
|
1434 |
+
best_err = valid_err
|
1435 |
+
if verbose:
|
1436 |
+
print(">>> New best model!")
|
1437 |
+
print(best_formula)
|
1438 |
+
|
1439 |
+
if verbose:
|
1440 |
+
iters_per_minute = 60.0 / (current_time - old_time)
|
1441 |
+
print("Took {:.2f} minutes.".format((current_time - old_time) / 60))
|
1442 |
+
print("Est. {:.2f} minutes remaining.".format((num_repeats - train_iter) / iters_per_minute))
|
1443 |
+
print()
|
1444 |
+
old_time = current_time
|
1445 |
+
|
1446 |
+
if verbose:
|
1447 |
+
print("Total time for repeat process: {:.2f} minutes.".format((time.time() - start_time) / 60))
|
1448 |
+
|
1449 |
+
return best_formula, best_iter, best_err
|
gp_model.py
ADDED
@@ -0,0 +1,163 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import time
|
2 |
+
|
3 |
+
import numpy as np
|
4 |
+
from gplearn.genetic import SymbolicRegressor
|
5 |
+
from sklearn.utils.validation import column_or_1d
|
6 |
+
|
7 |
+
import Settings as settings
|
8 |
+
from DataUtils import make_y_multi_safe
|
9 |
+
|
10 |
+
pop_size = 5000
|
11 |
+
generations = 20
|
12 |
+
p_crossover = 0.7
|
13 |
+
warm_start = False
|
14 |
+
|
15 |
+
|
16 |
+
class Genetic_Model:
|
17 |
+
def __init__(self):
|
18 |
+
self.name = "Genetic Model"
|
19 |
+
self.short_name = "GP"
|
20 |
+
self.function_set = settings.function_set.copy()
|
21 |
+
if "id" in self.function_set:
|
22 |
+
self.function_set.remove("id")
|
23 |
+
|
24 |
+
self.est_gp = SymbolicRegressor(population_size=pop_size,
|
25 |
+
generations=generations, stopping_criteria=0.01, # 20 gen
|
26 |
+
p_crossover=p_crossover, p_subtree_mutation=0.1,
|
27 |
+
p_hoist_mutation=0.05, p_point_mutation=0.1,
|
28 |
+
warm_start=warm_start,
|
29 |
+
max_samples=0.9, verbose=False,
|
30 |
+
parsimony_coefficient=0.01,
|
31 |
+
function_set=self.function_set)
|
32 |
+
|
33 |
+
def reset(self):
|
34 |
+
del self.est_gp
|
35 |
+
self.est_gp = SymbolicRegressor(population_size=pop_size,
|
36 |
+
generations=generations, stopping_criteria=0.01, # 20 gen
|
37 |
+
p_crossover=p_crossover, p_subtree_mutation=0.1,
|
38 |
+
p_hoist_mutation=0.05, p_point_mutation=0.1,
|
39 |
+
warm_start=warm_start,
|
40 |
+
max_samples=0.9, verbose=False,
|
41 |
+
parsimony_coefficient=0.01,
|
42 |
+
function_set=self.function_set)
|
43 |
+
|
44 |
+
def soft_reset(self):
|
45 |
+
del self.est_gp
|
46 |
+
self.est_gp = SymbolicRegressor(population_size=pop_size,
|
47 |
+
generations=generations, stopping_criteria=0.01, # 20 gen
|
48 |
+
p_crossover=p_crossover, p_subtree_mutation=0.1,
|
49 |
+
p_hoist_mutation=0.05, p_point_mutation=0.1,
|
50 |
+
warm_start=warm_start,
|
51 |
+
max_samples=0.9, verbose=False,
|
52 |
+
parsimony_coefficient=0.01,
|
53 |
+
function_set=self.function_set)
|
54 |
+
|
55 |
+
def predict(self, X):
|
56 |
+
return self.est_gp.predict(X)
|
57 |
+
|
58 |
+
def get_formula(self):
|
59 |
+
return self.est_gp._program
|
60 |
+
|
61 |
+
def get_simple_formula(self, digits=None):
|
62 |
+
return self.get_formula()
|
63 |
+
|
64 |
+
def get_big_formula(self):
|
65 |
+
formula_string = str(self.get_formula())
|
66 |
+
nested_list_string = formula_string.replace("sqrt(", "[\'sqrt\', ")
|
67 |
+
nested_list_string = nested_list_string.replace("add(", "[\'+\', ")
|
68 |
+
nested_list_string = nested_list_string.replace("mul(", "[\'*\', ")
|
69 |
+
nested_list_string = nested_list_string.replace("sub(", "[\'-\', ")
|
70 |
+
nested_list_string = nested_list_string.replace("sin(", "[\'sin\', ")
|
71 |
+
nested_list_string = nested_list_string.replace(")", "]")
|
72 |
+
nested_list_string = nested_list_string.replace("X", "Y")
|
73 |
+
|
74 |
+
retval = ""
|
75 |
+
currently_digits = False
|
76 |
+
current_number = ""
|
77 |
+
for current_char in nested_list_string:
|
78 |
+
if current_char == 'Y':
|
79 |
+
retval += "\'x"
|
80 |
+
currently_digits = True
|
81 |
+
current_number = ""
|
82 |
+
elif currently_digits:
|
83 |
+
if current_char.isdigit():
|
84 |
+
# retval += "{}".format(current_char)
|
85 |
+
current_number += "{}".format(current_char)
|
86 |
+
else:
|
87 |
+
currently_digits = False
|
88 |
+
retval += "{}".format(int(current_number) + 1)
|
89 |
+
retval += "\'{}".format(current_char)
|
90 |
+
else:
|
91 |
+
retval += "{}".format(current_char)
|
92 |
+
|
93 |
+
if "Y" in retval:
|
94 |
+
print("ERROR: formula still contains a Y...")
|
95 |
+
print(" formula string: {}\n nested list string: {}".format(formula_string, nested_list_string))
|
96 |
+
|
97 |
+
return eval(retval)
|
98 |
+
|
99 |
+
def train(self, X, Y):
|
100 |
+
X = np.reshape(X, [X.shape[0], -1])
|
101 |
+
Y = np.reshape(Y, [-1, 1])
|
102 |
+
Y = column_or_1d(Y)
|
103 |
+
self.est_gp.fit(X, Y)
|
104 |
+
return None
|
105 |
+
|
106 |
+
# Does not repeat train. Sorry.
|
107 |
+
def repeat_train(self, x, y, test_x=None, test_y=None,
|
108 |
+
num_repeats=settings.num_train_repeat_processes,
|
109 |
+
num_steps_to_train=settings.num_train_steps_in_repeat_mode,
|
110 |
+
verbose=True):
|
111 |
+
train_set_size = int(len(x) * settings.quick_train_fraction + 0.1)
|
112 |
+
x = np.array(x)
|
113 |
+
y = np.reshape(np.array(y), [-1, ])
|
114 |
+
sample = np.random.choice(range(x.shape[0]), size=train_set_size, replace=False)
|
115 |
+
out_sample = [yyy for yyy in range(x.shape[0]) if yyy not in sample]
|
116 |
+
|
117 |
+
train_x = x[sample][:]
|
118 |
+
train_y = y[sample][:]
|
119 |
+
valid_x = x[out_sample][:]
|
120 |
+
valid_y = y[out_sample][:]
|
121 |
+
|
122 |
+
old_time = time.time()
|
123 |
+
|
124 |
+
if verbose:
|
125 |
+
print("Beginning {} repeat sessions of {} iterations each.".format(num_repeats,
|
126 |
+
settings.num_train_steps_in_repeat_mode))
|
127 |
+
print()
|
128 |
+
start_time = time.time()
|
129 |
+
old_time = start_time
|
130 |
+
|
131 |
+
self.soft_reset()
|
132 |
+
self.train(train_x, train_y)
|
133 |
+
|
134 |
+
current_time = time.time()
|
135 |
+
if verbose:
|
136 |
+
# print(self.get_simple_formula())
|
137 |
+
print("Attained validation error: {:.5f}".format(valid_err))
|
138 |
+
|
139 |
+
best_formula = self.get_simple_formula()
|
140 |
+
if test_x is not None:
|
141 |
+
safe_test_y = make_y_multi_safe(test_y)
|
142 |
+
best_err = self.test(test_x, safe_test_y)
|
143 |
+
else:
|
144 |
+
best_err = self.test(valid_x, valid_y)
|
145 |
+
|
146 |
+
if verbose:
|
147 |
+
iters_per_minute = 60.0 / (current_time - old_time)
|
148 |
+
print("Took {:.2f} minutes.".format((current_time - old_time) / 60))
|
149 |
+
print("Est. {:.2f} minutes remaining.".format((num_repeats - train_iter) / iters_per_minute))
|
150 |
+
print()
|
151 |
+
|
152 |
+
return best_formula, 0, best_err
|
153 |
+
|
154 |
+
# Mean square error
|
155 |
+
def test(self, x, y):
|
156 |
+
x = np.reshape(x, [x.shape[0], -1])
|
157 |
+
y_hat = np.reshape(self.est_gp.predict(x), [1, -1])[0]
|
158 |
+
y_gold = np.reshape(y, [1, -1])[0]
|
159 |
+
our_sum = 0
|
160 |
+
for i in range(len(y_gold)):
|
161 |
+
our_sum += (y_hat[i] - y_gold[i]) ** 2
|
162 |
+
|
163 |
+
return our_sum / len(y_gold)
|