6
6
# All rights reserved. Please see the files COPYRIGHT.md and LICENSE.md for
7
7
# full copyright and license information.
8
8
###############################################################################
9
+
9
10
from pyomo .repn .standard_repn import generate_standard_repn
10
11
from mpisppy import MPI
11
12
from mpisppy .utils .lshaped_cuts import LShapedCutGenerator
13
+ from mpisppy .cylinders .spwindow import Field
12
14
13
15
import numpy as np
14
16
import pyomo .environ as pyo
@@ -18,7 +20,10 @@ class CrossScenarioCutSpoke(spoke.Spoke):
18
20
def __init__ (self , spbase_object , fullcomm , strata_comm , cylinder_comm , options = None ):
19
21
super ().__init__ (spbase_object , fullcomm , strata_comm , cylinder_comm , options = options )
20
22
21
- def make_windows (self ):
23
+ def register_send_fields (self ) -> None :
24
+
25
+ super ().register_send_fields ()
26
+
22
27
nscen = len (self .opt .all_scenario_names )
23
28
if nscen == 0 :
24
29
raise RuntimeError (f"(rank: { self .cylinder_rank } ), no local_scenarios" )
@@ -29,24 +34,20 @@ def make_windows(self):
29
34
for s in self .opt .local_scenarios .values ():
30
35
vbuflen += len (s ._mpisppy_data .nonant_indices )
31
36
local_scen_count = len (self .opt .local_scenario_names )
32
- self .nonant_per_scen = int (vbuflen / local_scen_count )
37
+ (self .nonant_per_scen , remainder ) = divmod (vbuflen , local_scen_count )
38
+ assert (remainder == 0 )
33
39
34
40
## the _locals will also have the kill signal
35
41
self .all_nonant_len = vbuflen
36
42
self .all_eta_len = nscen * local_scen_count
37
- self ._locals = np .zeros (nscen * local_scen_count + vbuflen + 1 )
38
- self ._coefs = np .zeros (nscen * (nscen + self .nonant_per_scen ) + 1 + 1 )
39
- self ._new_locals = False
40
43
41
- # local, remote
42
- # send, receive
43
- self ._make_windows (nscen * (self .nonant_per_scen + 1 + 1 ), nscen * local_scen_count + vbuflen )
44
+ self .all_nonants = self .register_recv_field (Field .NONANT , 0 , vbuflen )
45
+ self .all_etas = self .register_recv_field (Field .CROSS_SCENARIO_COST , 0 , nscen * nscen )
44
46
45
- def _got_kill_signal (self ):
46
- ''' returns True if a kill signal was received,
47
- and refreshes the array and _locals'''
48
- self ._new_locals = self .spoke_from_hub (self ._locals )
49
- return self .remote_write_id == - 1
47
+ self .all_coefs = self .register_send_field (Field .CROSS_SCENARIO_CUT ,
48
+ nscen * (self .nonant_per_scen + 1 + 1 ))
49
+
50
+ return
50
51
51
52
def prep_cs_cuts (self ):
52
53
# create a map scenario -> index, this index is used for various lists containing scenario dependent info.
@@ -62,7 +63,7 @@ def prep_cs_cuts(self):
62
63
# add copies of the nonanticipatory variables to the root problem
63
64
# NOTE: the LShaped code expects the nonant vars to be in a particular
64
65
# order and with a particular *name*.
65
- # We're also creating an index for reference against later
66
+ # We're also creating an index for reference against later
66
67
nonant_vid_to_copy_map = dict ()
67
68
root_vars = list ()
68
69
for v in non_ants :
@@ -84,7 +85,7 @@ def prep_cs_cuts(self):
84
85
self .opt .root .eta = pyo .Var (self .opt .all_scenario_names )
85
86
86
87
self .opt .root .bender = LShapedCutGenerator ()
87
- self .opt .root .bender .set_input (root_vars = self .opt .root_vars ,
88
+ self .opt .root .bender .set_input (root_vars = self .opt .root_vars ,
88
89
tol = 1e-4 , comm = self .cylinder_comm )
89
90
self .opt .root .bender .set_ls (self .opt )
90
91
@@ -127,36 +128,38 @@ def make_eta_lb_cut(self):
127
128
## we'll be storing a matrix as an array
128
129
## row_len is the length of each row
129
130
row_len = 1 + 1 + len (self .root_nonants )
130
- all_coefs = np .zeros ( self .nscen * row_len + 1 , dtype = 'd' )
131
+ all_coefs = self .all_coefs
132
+ all_coefs .value_array ().fill (0.0 )
131
133
for idx , k in enumerate (self .opt .all_scenario_names ):
132
134
## cut_array -- [ constant, eta_coef, *nonant_coefs ]
133
135
## this cut -- [ LB, -1, *0s ], i.e., -1*\eta + LB <= 0
134
136
all_coefs [row_len * idx ] = self ._eta_lb_array [idx ]
135
137
all_coefs [row_len * idx + 1 ] = - 1
136
- self .spoke_to_hub (all_coefs )
138
+ self .spoke_to_hub (all_coefs , Field . CROSS_SCENARIO_CUT )
137
139
138
140
def make_cut (self ):
139
141
140
142
## cache opt
141
143
opt = self .opt
142
144
143
145
## unpack these the way they were packed:
144
- all_nonants_and_etas = self ._locals
146
+ all_nonants = self .all_nonants
145
147
nonants = dict ()
146
148
etas = dict ()
147
149
ci = 0
148
150
for k , s in opt .local_scenarios .items ():
149
151
for ndn , i in s ._mpisppy_data .nonant_indices :
150
- nonants [k , ndn , i ] = all_nonants_and_etas [ci ]
152
+ nonants [k , ndn , i ] = all_nonants [ci ]
151
153
ci += 1
152
154
153
155
# get all the etas
156
+ all_etas = self .all_etas
157
+ ci = 0
154
158
for k , s in opt .local_scenarios .items ():
155
159
for sn in opt .all_scenario_names :
156
- etas [k , sn ] = all_nonants_and_etas [ci ]
160
+ etas [k , sn ] = all_etas [ci ]
157
161
ci += 1
158
162
159
- ## self.nscen == len(opt.all_scenario_names)
160
163
# compute local min etas
161
164
min_eta_vals = np .fromiter (( min (etas [k ,sn ] for k in opt .local_scenarios ) \
162
165
for sn in opt .all_scenario_names ),
@@ -215,7 +218,7 @@ def make_cut(self):
215
218
216
219
# if we are the winner, grab the xhat and bcast it to the other ranks
217
220
if self .cylinder_comm .Get_rank () == global_rank [0 ]:
218
- farthest_xhat = np .fromiter ( (nonants [local_winner , nname , ix ]
221
+ farthest_xhat = np .fromiter ( (nonants [local_winner , nname , ix ]
219
222
for nname , ix in root_nonants ),
220
223
dtype = 'd' , count = len (root_nonants ) )
221
224
else :
@@ -283,13 +286,14 @@ def make_cut(self):
283
286
## we'll be storing a matrix as an array
284
287
## row_len is the length of each row
285
288
row_len = 1 + 1 + len (root_nonants )
286
- all_coefs = np .zeros ( self .nscen * row_len + 1 , dtype = 'd' )
289
+ # all_coefs = np.zeros( self.nscen*row_len +1, dtype='d')
290
+ all_coefs = self .all_coefs
287
291
for idx , k in enumerate (opt .all_scenario_names ):
288
292
if k in coef_dict :
289
293
all_coefs [row_len * idx :row_len * (idx + 1 )] = coef_dict [k ]
290
294
elif feas_cuts :
291
295
all_coefs [row_len * idx :row_len * (idx + 1 )] = feas_cuts .pop ()
292
- self .spoke_to_hub (all_coefs )
296
+ self .spoke_to_hub (all_coefs , Field . CROSS_SCENARIO_CUT )
293
297
294
298
def main (self ):
295
299
# call main cut generation routine
@@ -299,5 +303,10 @@ def main(self):
299
303
300
304
# main loop
301
305
while not (self .got_kill_signal ()):
302
- if self ._new_locals :
306
+ # if self._new_locals:
307
+ if self .all_nonants .is_new () and self .all_etas .is_new ():
303
308
self .make_cut ()
309
+ ## End if
310
+ ## End while
311
+
312
+ return
0 commit comments