mg-pso-gui 0.1.4__tar.gz → 0.1.6__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/PKG-INFO +1 -1
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mg_pso_gui.egg-info/PKG-INFO +1 -1
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mg_pso_gui.egg-info/SOURCES.txt +10 -1
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/HomePage.py +1 -1
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/util/PSORunner.py +4 -15
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/__init__.py +10 -0
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/pso/__init__.py +16 -0
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/pso/csip_access.py +70 -0
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/pso/pso.py +326 -0
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/utils/__init__.py +10 -0
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/utils/plot/__init__.py +4 -0
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/utils/plot/cost_steps.py +111 -0
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/utils/trace_writer.py +82 -0
- mg-pso-gui-0.1.6/mgpsogui/util/recosu/utils/utils.py +137 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/setup.py +1 -1
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mg_pso_gui.egg-info/dependency_links.txt +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mg_pso_gui.egg-info/entry_points.txt +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mg_pso_gui.egg-info/requires.txt +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mg_pso_gui.egg-info/top_level.txt +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/__init__.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/OptionManager.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/PlatformTab/PlatformTab.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/PlatformTab/__init__.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/RunTab/RunTab.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/RunTab/__init__.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/BoundsEditorWindow.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/BoundsList.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/CalibrationParametersView.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/FunctionsList.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/ListParametersView.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/SetupTab.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/StaticParameterView.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/StepView.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/SetupTab/__init__.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/VisualizeTab/VisualizeTab.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/VisualizeTab/__init__.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/__init__.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/IGOW 4 Logo.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/collapse.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/down.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/expand.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/play.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/refresh.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/refresh_hd.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/stop.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/trash.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/gui/images/up.png +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/mgpsogui.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/start.yaml +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/util/CTkToolTip/__init__.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/util/CTkToolTip/ctk_tooltip.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/util/GraphGenerator.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/mgpsogui/util/__init__.py +0 -0
- {mg-pso-gui-0.1.4 → mg-pso-gui-0.1.6}/setup.cfg +0 -0
@@ -40,4 +40,13 @@ mgpsogui/util/GraphGenerator.py
|
|
40
40
|
mgpsogui/util/PSORunner.py
|
41
41
|
mgpsogui/util/__init__.py
|
42
42
|
mgpsogui/util/CTkToolTip/__init__.py
|
43
|
-
mgpsogui/util/CTkToolTip/ctk_tooltip.py
|
43
|
+
mgpsogui/util/CTkToolTip/ctk_tooltip.py
|
44
|
+
mgpsogui/util/recosu/__init__.py
|
45
|
+
mgpsogui/util/recosu/pso/__init__.py
|
46
|
+
mgpsogui/util/recosu/pso/csip_access.py
|
47
|
+
mgpsogui/util/recosu/pso/pso.py
|
48
|
+
mgpsogui/util/recosu/utils/__init__.py
|
49
|
+
mgpsogui/util/recosu/utils/trace_writer.py
|
50
|
+
mgpsogui/util/recosu/utils/utils.py
|
51
|
+
mgpsogui/util/recosu/utils/plot/__init__.py
|
52
|
+
mgpsogui/util/recosu/utils/plot/cost_steps.py
|
@@ -523,7 +523,7 @@ class App(customtkinter.CTk):
|
|
523
523
|
self.footer_progress_bar.start()
|
524
524
|
self.progress_message_left.configure(text="")
|
525
525
|
self.progress_message_middle.configure(text="Starting new round...")
|
526
|
-
self.footer_progress_label.configure(text="
|
526
|
+
self.footer_progress_label.configure(text="Next round...")
|
527
527
|
self.progress_message_right.configure(text="")
|
528
528
|
|
529
529
|
|
@@ -7,19 +7,8 @@ import threading
|
|
7
7
|
import time
|
8
8
|
import os
|
9
9
|
|
10
|
-
from cosu.pso import global_best
|
11
|
-
|
12
|
-
class Unbuffered:
|
13
|
-
def __init__(self, stream):
|
14
|
-
self.stream = stream
|
15
|
-
def write(self, data):
|
16
|
-
self.stream.write(data)
|
17
|
-
self.stream.flush()
|
18
|
-
def writelines(self, datas):
|
19
|
-
self.stream.writelines(datas)
|
20
|
-
self.stream.flush()
|
21
|
-
def __getattr__(self, attr):
|
22
|
-
return getattr(self.stream, attr)
|
10
|
+
#from cosu.pso import global_best
|
11
|
+
from .recosu.pso import global_best
|
23
12
|
|
24
13
|
def enqueue_output(out, queue):
|
25
14
|
for line in iter(out.readline, b''):
|
@@ -52,8 +41,8 @@ def run_process(stdout_queue, stderr_queue, results_queue, data, folder):
|
|
52
41
|
read_stdout, write_stdout = os.pipe()
|
53
42
|
read_stderr, write_stderr = os.pipe()
|
54
43
|
|
55
|
-
sys.stdout =
|
56
|
-
sys.stderr =
|
44
|
+
sys.stdout = os.fdopen(write_stdout, 'w')
|
45
|
+
sys.stderr = os.fdopen(write_stderr, 'w')
|
57
46
|
|
58
47
|
stdour_thread = threading.Thread(target=enqueue_output, args=(os.fdopen(read_stdout, 'r'), stdout_queue))
|
59
48
|
stderr_thread = threading.Thread(target=enqueue_output, args=(os.fdopen(read_stderr, 'r'), stderr_queue))
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
"""
|
4
|
+
LUCA/PSO toolkit
|
5
|
+
=========================================
|
6
|
+
This is ...
|
7
|
+
|
8
|
+
"""
|
9
|
+
|
10
|
+
__author__ = """Olaf David"""
|
11
|
+
__email__ = "odavid@colostate.edu"
|
12
|
+
__version__ = "1.0"
|
13
|
+
|
14
|
+
from .pso import global_best
|
15
|
+
|
16
|
+
__all__ = ["global_best"]
|
@@ -0,0 +1,70 @@
|
|
1
|
+
from cosu import utils
|
2
|
+
from csip import Client
|
3
|
+
from typing import List, Dict, Tuple
|
4
|
+
import queue, os
|
5
|
+
|
6
|
+
|
7
|
+
def csip_worker(reqq: queue.Queue, thread_no: int, stop, full_trace,
|
8
|
+
url, files, arg_params, conf: Dict, metainfo: Dict) -> None:
|
9
|
+
async_call = conf.get('async_call', True) # default is async
|
10
|
+
save_resp = conf.get('save_response_to', None) # save response, set it to a folder if responses should be saved.
|
11
|
+
|
12
|
+
while not stop():
|
13
|
+
try:
|
14
|
+
(rnd, step, iteration, particle, x, step_param_names, calib_params, objfunc, resq) = reqq.get(True, 0.5)
|
15
|
+
# print(thread_no, particle)
|
16
|
+
|
17
|
+
c = Client(metainfo=metainfo)
|
18
|
+
|
19
|
+
# static params (from args)
|
20
|
+
for param in arg_params:
|
21
|
+
c.add_data(param['name'], param['value'])
|
22
|
+
|
23
|
+
# particle params (generated from steps)
|
24
|
+
# for i, value in enumerate(x):
|
25
|
+
for idx, value in enumerate(x[particle, :]):
|
26
|
+
c.add_data(step_param_names[idx], value)
|
27
|
+
|
28
|
+
# other, previously calibrated params (other steps)
|
29
|
+
for name, value in calib_params.items():
|
30
|
+
c.add_data(name, value)
|
31
|
+
|
32
|
+
# objective function info
|
33
|
+
for of in objfunc:
|
34
|
+
c.add_cosu(of['name'], of['of'], of['data'])
|
35
|
+
# c.add_data(of['name'], (of['data'][0], of['data'][1]))
|
36
|
+
|
37
|
+
print('.', end='', flush=True)
|
38
|
+
|
39
|
+
try:
|
40
|
+
# print(c)
|
41
|
+
if async_call:
|
42
|
+
res = c.execute_async(url, files=files, conf=conf)
|
43
|
+
else:
|
44
|
+
res = c.execute(url, files=files, conf=conf)
|
45
|
+
|
46
|
+
if res.is_failed():
|
47
|
+
print(res)
|
48
|
+
|
49
|
+
if save_resp:
|
50
|
+
res.save_to(os.path.join(save_resp, 'r{}s{}i{}p{}.json'.format(rnd, step, iteration, particle)))
|
51
|
+
|
52
|
+
# print(res)
|
53
|
+
print(u'\u2714', end='', flush=True)
|
54
|
+
cost = utils.calc_cost(res, objfunc)
|
55
|
+
|
56
|
+
if full_trace is not None:
|
57
|
+
all_params = {}
|
58
|
+
# for i, value in enumerate(x):
|
59
|
+
for idx, value in enumerate(x[particle, :]):
|
60
|
+
all_params[step_param_names[idx]] = value
|
61
|
+
for name, value in calib_params.items():
|
62
|
+
all_params[name] = value
|
63
|
+
full_trace.append((all_params, cost))
|
64
|
+
|
65
|
+
resq.put((particle, cost))
|
66
|
+
except:
|
67
|
+
print(res)
|
68
|
+
reqq.task_done()
|
69
|
+
except queue.Empty:
|
70
|
+
continue
|
@@ -0,0 +1,326 @@
|
|
1
|
+
#
|
2
|
+
# $Id:$
|
3
|
+
#
|
4
|
+
# This file is part of the Cloud Services Integration Platform (CSIP),
|
5
|
+
# a Model-as-a-Service framework, API, and application suite.
|
6
|
+
#
|
7
|
+
# 2012-2020, OMSLab, Colorado State University.
|
8
|
+
#
|
9
|
+
# OMSLab licenses this file to you under the MIT license.
|
10
|
+
# See the LICENSE file in the project root for more information.
|
11
|
+
#
|
12
|
+
|
13
|
+
from ..utils import utils
|
14
|
+
from .csip_access import csip_worker
|
15
|
+
from pyswarms.single.global_best import GlobalBestPSO
|
16
|
+
from os import path
|
17
|
+
from threading import Thread
|
18
|
+
from typing import Dict, List, Set, Tuple
|
19
|
+
import numpy as np
|
20
|
+
import copy
|
21
|
+
import datetime
|
22
|
+
import queue
|
23
|
+
import json
|
24
|
+
import os
|
25
|
+
from multiprocessing import Queue
|
26
|
+
|
27
|
+
|
28
|
+
def eval_cost(x, iteration, step_param_names, step_objfunc, calib_params, req_queue, files, url, param, conf: Dict, rnd,
|
29
|
+
step):
|
30
|
+
particles = len(x[:, 0])
|
31
|
+
|
32
|
+
pfail_count = conf.get('particles_fail', 1) # Number of particles allowed to fail.
|
33
|
+
pfail_retry = conf.get('particles_retry', 3) # retry number of times if more than allowed fail
|
34
|
+
|
35
|
+
while pfail_retry > 0:
|
36
|
+
cost = np.ones(particles)
|
37
|
+
res_queue = queue.Queue()
|
38
|
+
|
39
|
+
print(' ', end='', flush=True)
|
40
|
+
|
41
|
+
# submit for processing
|
42
|
+
# for i_particle, v in enumerate(x[:, 0]):
|
43
|
+
for particle in range(particles):
|
44
|
+
req_queue.put((rnd, step, iteration, particle, x, step_param_names, calib_params, step_objfunc, res_queue))
|
45
|
+
# req_queue.put((i_particle, x[i_particle,:], step_param_names, calib_params, step_objfunc, res_queue))
|
46
|
+
|
47
|
+
# wait for the cost value to come back
|
48
|
+
# for i, v in enumerate(x[:, 0]):
|
49
|
+
for idx in range(particles):
|
50
|
+
(particle, p_cost) = res_queue.get()
|
51
|
+
cost[particle] = p_cost
|
52
|
+
res_queue.task_done()
|
53
|
+
|
54
|
+
res_queue.join()
|
55
|
+
|
56
|
+
# replace the 'nan' cost values (failed/missing runs) with the mean of the
|
57
|
+
# rest of the cost values, hence ignore it
|
58
|
+
|
59
|
+
# print("cost ", cost)
|
60
|
+
nan_idx = np.where(np.isnan(cost))
|
61
|
+
failed_particles = len(nan_idx[0])
|
62
|
+
|
63
|
+
# leave the loop if fails acceptable
|
64
|
+
if failed_particles <= pfail_count:
|
65
|
+
break
|
66
|
+
print("Re-running particles, since ", failed_particles, ' out of ', particles, ' particles failed.')
|
67
|
+
pfail_retry -= 1
|
68
|
+
|
69
|
+
if pfail_retry == 0:
|
70
|
+
print('Particle evaluation failed ', conf.get('particles_retry', 3), ' times. PSO stopped.')
|
71
|
+
return None
|
72
|
+
|
73
|
+
# print("mean ", mean)
|
74
|
+
# assign the mean value to all failed runs.
|
75
|
+
mean = np.nanmean(cost)
|
76
|
+
cost[nan_idx[0]] = mean
|
77
|
+
|
78
|
+
print(flush=True)
|
79
|
+
return cost
|
80
|
+
|
81
|
+
|
82
|
+
def global_best(steps: Dict, rounds: Tuple, args: Dict, n_particles: int, iters: int, options: Dict,
|
83
|
+
oh_strategy: Dict = None, n_threads: int = 4, rtol: float = 0.001, ftol: float = -np.inf,
|
84
|
+
ftol_iter: int = 1, full_trace: List = None, rtol_iter: int = 1,
|
85
|
+
conf: Dict = None, metainfo: Dict = None, cost_target: float = -np.inf, result_queue: Queue = None) -> Tuple:
|
86
|
+
"""Performs a stepwise particle swarm optimization PSO using a global best approach.
|
87
|
+
|
88
|
+
Parameters
|
89
|
+
----------
|
90
|
+
steps : Dict
|
91
|
+
step definitions
|
92
|
+
rounds : tuple
|
93
|
+
round definition, (min,max) or max
|
94
|
+
args : Dict
|
95
|
+
static service args
|
96
|
+
n_particles : int
|
97
|
+
number of particles
|
98
|
+
iters : int
|
99
|
+
number of iterations
|
100
|
+
options : Dict
|
101
|
+
PSO options (see pyswarms)
|
102
|
+
oh_strategy : Dict
|
103
|
+
PSO Option handling strategy (see pyswarms)
|
104
|
+
n_threads : int
|
105
|
+
size of thread pool (default: 4)
|
106
|
+
rtol : float
|
107
|
+
percentage of change of sum(best_cost) between rounds for
|
108
|
+
convergence. (Default is 0.001 0.1%)
|
109
|
+
ftol : float
|
110
|
+
PSO tolerance (default: -np.inf)
|
111
|
+
ftol_iter : float
|
112
|
+
number of iterations over which the relative error in
|
113
|
+
objective_func is acceptable for convergence. (default: 1)
|
114
|
+
full_trace : List
|
115
|
+
trace of all runs, list of tuples
|
116
|
+
first is dictionary of parameter names to parameter values
|
117
|
+
second is the cost value (default: None)
|
118
|
+
rtol_iter : int
|
119
|
+
the number of subsequent rounds with sum(best_cost) < rtol
|
120
|
+
(default: 1)
|
121
|
+
conf : Dict
|
122
|
+
configuration settings (default: {} )
|
123
|
+
metainfo : Dict
|
124
|
+
additional metainfo for the csip client (default: {} )
|
125
|
+
cost_target: float
|
126
|
+
the cost target (default: -np.inf)
|
127
|
+
Returns
|
128
|
+
-------
|
129
|
+
Tuple
|
130
|
+
optimizer: List, step_trace: Dict
|
131
|
+
|
132
|
+
"""
|
133
|
+
|
134
|
+
utils.check_url(args['url'])
|
135
|
+
|
136
|
+
step_file = conf.get('step_trace', None)
|
137
|
+
|
138
|
+
min_rounds = 1
|
139
|
+
if type(rounds) == tuple:
|
140
|
+
min_rounds = rounds[0]
|
141
|
+
max_rounds = rounds[1]
|
142
|
+
else:
|
143
|
+
max_rounds = rounds
|
144
|
+
|
145
|
+
if min_rounds < 1:
|
146
|
+
raise Exception('min rounds >= 1 expected, was "{}"'.format(min_rounds))
|
147
|
+
|
148
|
+
if max_rounds > 20:
|
149
|
+
raise Exception('max rounds <= 20 expected, was "{}"'.format(max_rounds))
|
150
|
+
|
151
|
+
if n_threads < 1:
|
152
|
+
raise Exception('n_threads >= 1, was "{}"'.format(n_threads))
|
153
|
+
|
154
|
+
if rtol_iter < 1:
|
155
|
+
raise Exception('rtol_iter >= 1, was "{}"'.format(rtol_iter))
|
156
|
+
|
157
|
+
if full_trace is not None and not isinstance(full_trace, list):
|
158
|
+
raise Exception('full_trace must be of type, was "{}"'.format(type(full_trace)))
|
159
|
+
|
160
|
+
best_cost = np.ones(len(steps)) * np.inf
|
161
|
+
optimizer = np.empty(len(steps), dtype=object)
|
162
|
+
|
163
|
+
# trace of steps info
|
164
|
+
step_trace = {}
|
165
|
+
|
166
|
+
step_trace['dir'] = os.getcwd()
|
167
|
+
step_trace['start'] = str(datetime.datetime.now())
|
168
|
+
step_trace['min_rounds'] = min_rounds
|
169
|
+
step_trace['max_rounds'] = max_rounds
|
170
|
+
step_trace['iters'] = iters
|
171
|
+
step_trace['ftol'] = ftol
|
172
|
+
step_trace['ftol_iter'] = ftol_iter
|
173
|
+
step_trace['rtol'] = rtol
|
174
|
+
step_trace['rtol_iter'] = rtol_iter
|
175
|
+
step_trace['n_threads'] = n_threads
|
176
|
+
step_trace['n_particles'] = n_particles
|
177
|
+
step_trace['n_steps'] = len(steps)
|
178
|
+
step_trace['steps'] = copy.deepcopy(steps)
|
179
|
+
step_trace['args'] = args
|
180
|
+
|
181
|
+
if step_file is not None:
|
182
|
+
with open(step_file, "w") as fo:
|
183
|
+
json.dump(step_trace, fo)
|
184
|
+
|
185
|
+
# best round cost
|
186
|
+
best_round_cost = np.inf
|
187
|
+
|
188
|
+
# request queue for worker
|
189
|
+
req_queue = queue.Queue()
|
190
|
+
|
191
|
+
conf = conf or {}
|
192
|
+
done = False
|
193
|
+
thread_pool = []
|
194
|
+
for thread_no in range(n_threads):
|
195
|
+
worker = Thread(target=csip_worker, args=(req_queue, thread_no, lambda: done,
|
196
|
+
full_trace, args['url'], args.get('files', None), args['param'],
|
197
|
+
conf, metainfo)
|
198
|
+
)
|
199
|
+
thread_pool.append(worker)
|
200
|
+
worker.start()
|
201
|
+
|
202
|
+
r_below = 0
|
203
|
+
early_exit = False
|
204
|
+
start_time = datetime.datetime.now()
|
205
|
+
for r in range(max_rounds):
|
206
|
+
no_improvement = np.full(len(steps), True)
|
207
|
+
for s, step in enumerate(steps):
|
208
|
+
|
209
|
+
# check if forced exit.
|
210
|
+
if path.exists("stop"):
|
211
|
+
print('\n>>>>> stop file found, exit now.')
|
212
|
+
early_exit = True
|
213
|
+
break
|
214
|
+
|
215
|
+
param_names, bounds, objfunc = utils.get_step_info(steps, s)
|
216
|
+
# maybe clone args?
|
217
|
+
args['step_param_names'] = param_names
|
218
|
+
args['step_objfunc'] = objfunc
|
219
|
+
# get calibrated parameter from all other steps
|
220
|
+
args['calib_params'] = utils.get_calibrated_params(steps, s)
|
221
|
+
|
222
|
+
args['req_queue'] = req_queue
|
223
|
+
args['conf'] = conf
|
224
|
+
|
225
|
+
# create optimizer in the first round.
|
226
|
+
if optimizer[s] is None:
|
227
|
+
optimizer[s] = GlobalBestPSO(step.get('n_particles', n_particles),
|
228
|
+
len(param_names),
|
229
|
+
oh_strategy=step.get('oh_strategy', oh_strategy),
|
230
|
+
options=step.get('options', options),
|
231
|
+
bounds=bounds,
|
232
|
+
ftol=step.get('ftol', ftol),
|
233
|
+
ftol_iter=step.get('ftol_iter', ftol_iter),
|
234
|
+
cost_target=step.get('cost_target', cost_target))
|
235
|
+
print('\n>>>>> R{}/S{} particle params: {} calibrated params: {}\n'.format(r + 1, s + 1, param_names,
|
236
|
+
args['calib_params']))
|
237
|
+
|
238
|
+
|
239
|
+
if result_queue is not None:
|
240
|
+
result_queue.put((r + 1, s + 1, param_names, args['calib_params']))
|
241
|
+
|
242
|
+
args['rnd'] = r + 1
|
243
|
+
args['step'] = s + 1
|
244
|
+
|
245
|
+
# perform optimization
|
246
|
+
cost, pos = optimizer[s].optimize(eval_cost, iters=step.get('iters', iters), **args)
|
247
|
+
if cost is None:
|
248
|
+
early_exit = True
|
249
|
+
break
|
250
|
+
|
251
|
+
# capture the best cost
|
252
|
+
# if cost < best_cost[s] and np.abs(cost - best_cost[s]) > rtol:
|
253
|
+
if cost < best_cost[s]:
|
254
|
+
best_cost[s] = cost
|
255
|
+
no_improvement[s] = False
|
256
|
+
utils.annotate_step(best_cost[s], pos, steps, s)
|
257
|
+
|
258
|
+
print('\n Step summary, best particle values: {} '.format(pos))
|
259
|
+
|
260
|
+
key = "r{}s{}".format(r + 1, s + 1)
|
261
|
+
step_trace[key] = {}
|
262
|
+
step_trace[key]['time'] = str(datetime.datetime.now())
|
263
|
+
step_trace[key]['best_costs'] = best_cost
|
264
|
+
step_trace[key]['steps'] = copy.deepcopy(steps)
|
265
|
+
|
266
|
+
if step_file is not None:
|
267
|
+
with open(step_file, "w") as fo:
|
268
|
+
json.dump(step_trace, fo)
|
269
|
+
|
270
|
+
# print(json.dumps(steps, sort_keys=False, indent=2))
|
271
|
+
|
272
|
+
if early_exit:
|
273
|
+
step_trace['exit'] = '1'
|
274
|
+
break
|
275
|
+
|
276
|
+
round_cost = np.sum(best_cost)
|
277
|
+
|
278
|
+
# if no improvement in all steps, break out of rounds prematurely
|
279
|
+
# but start checking only after min_rounds
|
280
|
+
# if (r + 1 >= min_rounds) and all(no_improvement):
|
281
|
+
rel_round_tol = 1 - round_cost / best_round_cost
|
282
|
+
|
283
|
+
print('\n Round summary - round_cost:{}, step_costs: {}, step improvement:{}'
|
284
|
+
.format(round_cost, best_cost, np.invert(no_improvement)))
|
285
|
+
print('\n Progress - best_round_cost:{}, rel_round_tol:{}, rtol:{}'
|
286
|
+
.format(best_round_cost, rel_round_tol, rtol))
|
287
|
+
|
288
|
+
key = "r{}".format(r + 1)
|
289
|
+
step_trace[key] = {}
|
290
|
+
step_trace[key]['time'] = str(datetime.datetime.now())
|
291
|
+
step_trace[key]['round_cost'] = round_cost
|
292
|
+
step_trace[key]['best_costs'] = best_cost
|
293
|
+
step_trace[key]['improvements'] = no_improvement
|
294
|
+
if step_file is not None:
|
295
|
+
with open(step_file, "w") as fo:
|
296
|
+
json.dump(step_trace, fo)
|
297
|
+
|
298
|
+
if (r + 1 >= min_rounds) and 0 <= rel_round_tol < rtol:
|
299
|
+
r_below += 1
|
300
|
+
if r_below >= rtol_iter:
|
301
|
+
break
|
302
|
+
else:
|
303
|
+
# reset
|
304
|
+
r_below = 0
|
305
|
+
|
306
|
+
if round_cost < best_round_cost:
|
307
|
+
best_round_cost = round_cost
|
308
|
+
|
309
|
+
end_time = datetime.datetime.now()
|
310
|
+
elapsed = str(end_time - start_time)
|
311
|
+
|
312
|
+
print('Done in {} after {} out of {} rounds'.format(elapsed, r + 1, max_rounds))
|
313
|
+
|
314
|
+
done = True
|
315
|
+
for worker in thread_pool:
|
316
|
+
worker.join()
|
317
|
+
|
318
|
+
step_trace['rounds'] = r + 1
|
319
|
+
step_trace['end'] = str(datetime.datetime.now())
|
320
|
+
step_trace['time'] = elapsed
|
321
|
+
|
322
|
+
if step_file is not None:
|
323
|
+
with open(step_file, "w") as fo:
|
324
|
+
json.dump(step_trace, fo)
|
325
|
+
|
326
|
+
return optimizer, step_trace
|
@@ -0,0 +1,111 @@
|
|
1
|
+
import matplotlib.pyplot as plt
|
2
|
+
import numpy as np
|
3
|
+
|
4
|
+
|
5
|
+
def rs_key(round, step):
|
6
|
+
return "r{}s{}".format(round, step)
|
7
|
+
|
8
|
+
|
9
|
+
def get_values(name, s, steps):
|
10
|
+
nrounds = steps['rounds']
|
11
|
+
vals = []
|
12
|
+
for r in range(1, nrounds + 1):
|
13
|
+
info = steps[rs_key(r, s)]
|
14
|
+
params = info[s - 1]['param']
|
15
|
+
for i, p in enumerate(params):
|
16
|
+
if name == p['name']:
|
17
|
+
vals.append(p['value'])
|
18
|
+
return vals
|
19
|
+
|
20
|
+
|
21
|
+
col = ['tab:blue', 'tab:orange', 'tab:green', 'tab:red', 'tab:purple', 'tab:brown']
|
22
|
+
|
23
|
+
|
24
|
+
def plot_cost_steps(optimizer, step_trace):
|
25
|
+
nsteps = step_trace['steps']
|
26
|
+
nrounds = step_trace['rounds']
|
27
|
+
iter = step_trace['iters']
|
28
|
+
particles = step_trace['particles']
|
29
|
+
time = step_trace['time']
|
30
|
+
|
31
|
+
total_iter = iter * nrounds
|
32
|
+
|
33
|
+
x = np.arange(1, total_iter + 1)
|
34
|
+
f = plt.figure(figsize=(15, 15))
|
35
|
+
|
36
|
+
# top plot
|
37
|
+
plt.subplot(nsteps + 1, 1, 1)
|
38
|
+
|
39
|
+
for i in range(nsteps):
|
40
|
+
plt.plot(x, optimizer[i].cost_history, label='step ' + str(i + 1), color=col[i + 2])
|
41
|
+
|
42
|
+
mini = optimizer[0].cost_history
|
43
|
+
for i in range(1, nsteps):
|
44
|
+
mini = np.maximum(mini, optimizer[i].cost_history)
|
45
|
+
|
46
|
+
for i, it in enumerate(range(iter, total_iter + 1, iter)):
|
47
|
+
plt.axvline(x=it, color='lightgray', linestyle='--')
|
48
|
+
plt.text(it, mini[0], ' R' + str(i + 1), color='lightgray')
|
49
|
+
|
50
|
+
plt.xlim(0, iter * nrounds)
|
51
|
+
plt.title('cost function\n (rounds:{} iter:{} particles:{} time:{})'.format(nrounds, iter, particles, time))
|
52
|
+
plt.legend()
|
53
|
+
|
54
|
+
px = np.arange(iter, (nrounds + 1) * iter, iter)
|
55
|
+
for s in range(1, nsteps + 1):
|
56
|
+
|
57
|
+
a = plt.subplot(nsteps + 1, 1, s + 1)
|
58
|
+
|
59
|
+
plt.subplots_adjust(hspace=0.3)
|
60
|
+
plt.xlim(0, iter * nrounds)
|
61
|
+
if s == nsteps:
|
62
|
+
plt.xlabel('Iterations')
|
63
|
+
|
64
|
+
# rounds marks
|
65
|
+
for i, it in enumerate(range(iter, total_iter + 1, iter)):
|
66
|
+
plt.axvline(x=it, color='lightgray', linestyle='--')
|
67
|
+
|
68
|
+
info = step_trace[rs_key(1, s)][s - 1]
|
69
|
+
# print(info['param'])
|
70
|
+
i = 0
|
71
|
+
params = info['param']
|
72
|
+
|
73
|
+
p = params[i]
|
74
|
+
# print('ax ', plt)
|
75
|
+
vals = get_values(p['name'], s, step_trace)
|
76
|
+
title = "{}:{:.5f}".format(p['name'], vals[-1])
|
77
|
+
a.plot(px, vals, 'v', label=p['name'], color=col[i])
|
78
|
+
a.axhline(p['bounds'][0], linestyle='--', color='lightgray')
|
79
|
+
a.axhline(p['bounds'][1], linestyle='--', color='lightgray')
|
80
|
+
a.set_ylabel(p['name'], color=col[i])
|
81
|
+
a.tick_params(axis='y', labelcolor=col[i])
|
82
|
+
for x, y in zip(px, vals):
|
83
|
+
label = "{:.5f}".format(y)
|
84
|
+
a.annotate(label, (x, y), textcoords="offset points", xytext=(0, 10),
|
85
|
+
color="gray", ha='center')
|
86
|
+
a.legend(loc="center left")
|
87
|
+
|
88
|
+
if (len(params) > 1):
|
89
|
+
sp = a.twinx()
|
90
|
+
i = i + 1
|
91
|
+
p = params[i]
|
92
|
+
vals = get_values(p['name'], s, step_trace)
|
93
|
+
title = "{} {}:{:.5f}".format(title, p['name'], vals[-1])
|
94
|
+
# title = title + ', ' + p['name'] + ':' + str(vals[-1])
|
95
|
+
sp.plot(px, vals, 'v', label=p['name'], color=col[i])
|
96
|
+
sp.axhline(p['bounds'][0], linestyle='--', color='lightgray')
|
97
|
+
sp.axhline(p['bounds'][1], linestyle='--', color='lightgray')
|
98
|
+
sp.set_ylabel(p['name'], color=col[i])
|
99
|
+
sp.tick_params(axis='y', labelcolor=col[i])
|
100
|
+
for x, y in zip(px, vals):
|
101
|
+
label = "{:.5f}".format(y)
|
102
|
+
sp.annotate(label, (x, y), textcoords="offset points", xytext=(0, 10),
|
103
|
+
color="gray", ha='center')
|
104
|
+
sp.legend(loc="right")
|
105
|
+
|
106
|
+
# print(s, params)
|
107
|
+
plt.legend()
|
108
|
+
plt.title("step {} ({})".format(s, title))
|
109
|
+
|
110
|
+
plt.xlabel('Iterations')
|
111
|
+
plt.show()
|
@@ -0,0 +1,82 @@
|
|
1
|
+
from typing import Dict, List, Set, Tuple
|
2
|
+
|
3
|
+
TraceList = List[Tuple[Dict[str, float], float]]
|
4
|
+
|
5
|
+
|
6
|
+
def write_run_trace(file: str, run_trace: TraceList, num_steps: int = 0, num_runs_per_step: int = 0) -> None:
|
7
|
+
if num_steps <= 0 or num_runs_per_step <= 0:
|
8
|
+
write_raw_run_trace(file, run_trace)
|
9
|
+
return
|
10
|
+
|
11
|
+
variable_list: List[str] = create_variable_list(run_trace)
|
12
|
+
split_trace_list: List[List[TraceList]] = split_trace(
|
13
|
+
run_trace, num_steps, num_runs_per_step)
|
14
|
+
try:
|
15
|
+
with open(file, "w") as writer:
|
16
|
+
writer.write("@T,trace\n")
|
17
|
+
writer.write("@H,cost,round,step,{}\n".format(",".join(variable_list)))
|
18
|
+
round: int = 1
|
19
|
+
for round_list in split_trace_list:
|
20
|
+
step: int = 1
|
21
|
+
for step_list in round_list:
|
22
|
+
for param, cost in step_list:
|
23
|
+
record = ",{},{},{}".format(cost, round, step)
|
24
|
+
for name in variable_list:
|
25
|
+
if name in param:
|
26
|
+
record += ",{}".format(param[name])
|
27
|
+
else:
|
28
|
+
record += ","
|
29
|
+
writer.write(record + "\n")
|
30
|
+
step = step + 1
|
31
|
+
round = round + 1
|
32
|
+
except IOError:
|
33
|
+
print("Failed to write to file {}".format(file))
|
34
|
+
|
35
|
+
|
36
|
+
def write_raw_run_trace(file: str, run_trace: TraceList) -> None:
|
37
|
+
variable_list: List[str] = create_variable_list(run_trace)
|
38
|
+
try:
|
39
|
+
with open(file, "w") as writer:
|
40
|
+
writer.write("@T,trace\n")
|
41
|
+
writer.write("@H,cost,{}\n".format(",".join(variable_list)))
|
42
|
+
for param, cost in run_trace:
|
43
|
+
record = ",{}".format(cost)
|
44
|
+
for name in variable_list:
|
45
|
+
if name in param:
|
46
|
+
record += ",{}".format(param[name])
|
47
|
+
else:
|
48
|
+
record += ","
|
49
|
+
writer.write(record + "\n")
|
50
|
+
except IOError:
|
51
|
+
print("Failed to write to file {}".format(file))
|
52
|
+
|
53
|
+
|
54
|
+
def create_variable_list(run_trace: TraceList) -> List[str]:
|
55
|
+
variable_set: Set[str] = set()
|
56
|
+
for param, cost in run_trace:
|
57
|
+
for name in param.keys():
|
58
|
+
variable_set.add(name)
|
59
|
+
return list(variable_set)
|
60
|
+
|
61
|
+
|
62
|
+
def split_trace(run_trace: TraceList, num_steps: int, num_runs_per_step: int) -> List[List[TraceList]]:
|
63
|
+
step = 1
|
64
|
+
i = 0
|
65
|
+
split_trace_list: List[List[TraceList]] = list()
|
66
|
+
step_list: List[TraceList]
|
67
|
+
while i < len(run_trace):
|
68
|
+
if step == 1:
|
69
|
+
step_list = list()
|
70
|
+
split_trace_list.append(step_list)
|
71
|
+
|
72
|
+
endpoint = i + num_runs_per_step
|
73
|
+
if endpoint > len(run_trace):
|
74
|
+
endpoint = len(run_trace)
|
75
|
+
|
76
|
+
step_list.append(run_trace[i:endpoint])
|
77
|
+
i = endpoint
|
78
|
+
step = step + 1
|
79
|
+
if step > num_steps:
|
80
|
+
step = 1
|
81
|
+
|
82
|
+
return split_trace_list
|
@@ -0,0 +1,137 @@
|
|
1
|
+
#
|
2
|
+
# $Id:$
|
3
|
+
#
|
4
|
+
# This file is part of the Cloud Services Integration Platform (CSIP),
|
5
|
+
# a Model-as-a-Service framework, API, and application suite.
|
6
|
+
#
|
7
|
+
# 2012-2020, OMSLab, Colorado State University.
|
8
|
+
#
|
9
|
+
# OMSLab licenses this file to you under the MIT license.
|
10
|
+
# See the LICENSE file in the project root for more information.
|
11
|
+
#
|
12
|
+
from builtins import Exception
|
13
|
+
|
14
|
+
import requests
|
15
|
+
import numpy as np
|
16
|
+
import json
|
17
|
+
from csip import Client
|
18
|
+
|
19
|
+
NORM = {
|
20
|
+
'kge': lambda x: 1 - x,
|
21
|
+
'nslog': lambda x: 1 - x,
|
22
|
+
'nslog1p': lambda x: 1 - x,
|
23
|
+
'ns': lambda x: 1 - x,
|
24
|
+
'rmse': lambda x: x,
|
25
|
+
'trmse': lambda x: x,
|
26
|
+
'pbias': lambda x: abs(x)
|
27
|
+
}
|
28
|
+
|
29
|
+
|
30
|
+
def calc_cost(response: "Client", objfunc) -> float:
|
31
|
+
"""aggregated objective function value -> cost"""
|
32
|
+
|
33
|
+
# this will eliminate the failed run as a candidate solution.
|
34
|
+
if not response.is_finished():
|
35
|
+
return np.nan
|
36
|
+
|
37
|
+
cost = 0.0
|
38
|
+
for o in objfunc:
|
39
|
+
cosu_val = response.get_cosu_value(o['name'])
|
40
|
+
if cosu_val is None:
|
41
|
+
raise Exception('No cosu of value for ' + o['name'])
|
42
|
+
cosu_of = o['of']
|
43
|
+
n = NORM.get(cosu_of, lambda x: x) # default
|
44
|
+
cost += n(cosu_val) * o.get('weight', 1.0)
|
45
|
+
|
46
|
+
# of_val = response.get_data_value(of['name'])
|
47
|
+
# for name in NORM:
|
48
|
+
# if str(c['name']).startswith(name):
|
49
|
+
# cost += NORM[name](cosu_val) * c.get('weight', 1.0)
|
50
|
+
return cost
|
51
|
+
|
52
|
+
def save(file, dict):
|
53
|
+
""" Save dict to json file. """
|
54
|
+
with open(file, 'w') as json_file:
|
55
|
+
json.dump(dict, json_file, sort_keys=False, indent=2)
|
56
|
+
|
57
|
+
|
58
|
+
def load(file):
|
59
|
+
""" Load dict from json file. """
|
60
|
+
with open(file) as json_file:
|
61
|
+
data = json.load(json_file)
|
62
|
+
return data
|
63
|
+
|
64
|
+
|
65
|
+
def get_step_info(steps, index):
|
66
|
+
"""Extract all relevant info from the step dict"""
|
67
|
+
|
68
|
+
step = steps[index]
|
69
|
+
l = len(step['param'])
|
70
|
+
param_min = np.ones(l)
|
71
|
+
param_max = np.ones(l)
|
72
|
+
param_names = []
|
73
|
+
|
74
|
+
# extract names and bounds
|
75
|
+
for i, p in enumerate(step['param']):
|
76
|
+
param_names.append(p['name'])
|
77
|
+
if len(p['bounds']) != 2:
|
78
|
+
raise Exception('Invalid bounds tuple: (min, max): "{}"'.format(p['bounds']))
|
79
|
+
if not p['bounds'][0] < p['bounds'][1]:
|
80
|
+
raise Exception('Invalid bounds values: "{}"'.format(p['bounds']))
|
81
|
+
param_min[i] = p['bounds'][0]
|
82
|
+
param_max[i] = p['bounds'][1]
|
83
|
+
|
84
|
+
# check if OF is supported
|
85
|
+
for o in step['objfunc']:
|
86
|
+
# if not o['name'] in NORM:
|
87
|
+
# raise Exception('OF not supported: "{}"'.format(o['name']))
|
88
|
+
found = False
|
89
|
+
for name in NORM:
|
90
|
+
if str(o['name']).startswith(name):
|
91
|
+
found = True
|
92
|
+
if not found:
|
93
|
+
raise Exception('OF not supported: "{}"'.format(o['name']))
|
94
|
+
if len(o['data']) != 2:
|
95
|
+
raise Exception('OF missing data: (sim, obs): "{}"'.format(o['name']))
|
96
|
+
|
97
|
+
return param_names, (param_min, param_max), step['objfunc']
|
98
|
+
|
99
|
+
|
100
|
+
def get_calibrated_params(steps, index):
|
101
|
+
"""Get all previously calibrated parameter from any other step"""
|
102
|
+
|
103
|
+
step = steps[index]
|
104
|
+
cp = {}
|
105
|
+
for s in steps:
|
106
|
+
# skip the own step
|
107
|
+
if s is step:
|
108
|
+
continue
|
109
|
+
for p in s['param']:
|
110
|
+
# if 'value' in p.keys():
|
111
|
+
if 'value' in p:
|
112
|
+
cp[p['name']] = p['value']
|
113
|
+
|
114
|
+
# if the step parameter are in any other step, take them out since
|
115
|
+
# we rather want to calibrate them
|
116
|
+
for p in step['param']:
|
117
|
+
if p['name'] in cp:
|
118
|
+
cp.pop(p['name'])
|
119
|
+
|
120
|
+
return cp
|
121
|
+
|
122
|
+
|
123
|
+
def annotate_step(best_cost, pos, steps, index):
|
124
|
+
"""Annotate the step with the best value"""
|
125
|
+
|
126
|
+
step = steps[index]
|
127
|
+
step['cost'] = best_cost
|
128
|
+
for i, p in enumerate(step['param']):
|
129
|
+
p['value'] = pos[i]
|
130
|
+
|
131
|
+
|
132
|
+
def check_url(url):
|
133
|
+
"""Check is the Url is valid."""
|
134
|
+
|
135
|
+
r = requests.head(url)
|
136
|
+
if r.status_code != 200:
|
137
|
+
raise Exception('Error code {} from Url: "{}"'.format(r.status_code, url))
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|