@@ -138,7 +138,6 @@ def reset(self):
138138
139139 """
140140 logger .debug ("Resetting interpolation constraints" )
141-
142141
143142 def add_constraints_to_least_squares (self , A , B , idc , w = 1.0 , name = "undefined" ):
144143 """
@@ -255,7 +254,7 @@ def remove_constraints_from_least_squares(
255254
256255 pass
257256
258- def add_equality_constraints (self , node_idx , values ,name = "undefined" ):
257+ def add_equality_constraints (self , node_idx , values , name = "undefined" ):
259258 """
260259 Adds hard constraints to the least squares system. For now this just
261260 sets
@@ -283,8 +282,7 @@ def add_equality_constraints(self, node_idx, values,name="undefined"):
283282 "B" : values [outside ].tolist (),
284283 "col" : idc [outside ].tolist (),
285284 # "w": w,
286- "row" : np .arange (self .eq_const_c , self .eq_const_c + idc [outside ].shape [0 ])
287-
285+ "row" : np .arange (self .eq_const_c , self .eq_const_c + idc [outside ].shape [0 ]),
288286 }
289287 self .eq_const_c += idc [outside ].shape [0 ]
290288 # ,'C':np.ones(idc[outside].shape[0]).tolist(),}
@@ -317,20 +315,38 @@ def add_inequality_constraints_to_matrix(self, A, l, u, idc, name="undefined"):
317315
318316 """
319317 # map from mesh node index to region node index
320- gi = np .zeros (self .support .n_nodes )
318+ gi = np .zeros (self .support .n_nodes , dtype = int )
321319 gi [:] = - 1
322- gi [self .region ] = np .arange (0 , self .nx )
320+ gi [self .region ] = np .arange (0 , self .nx , dtype = int )
323321 idc = gi [idc ]
324- rows = np .arange (self .ineq_const_c , self .ineq_const_c + idc .shape [0 ])
325- rows = np .tile (rows , (A .shape [- 1 ], 1 )).T
326- self .ineq_constraints [name ] = {
327- "A" : A ,
328- "l" : l ,
329- "col" : idc ,
330- "u" : u ,
331- "row" : rows
332- }
333- self .ineq_const_c += idc .shape [0 ]
322+ rows = np .arange (self .ineq_const_c , self .ineq_const_c + idc .shape [0 ])
323+ rows = np .tile (rows , (A .shape [- 1 ], 1 )).T
324+ self .ineq_constraints [name ] = {"A" : A , "l" : l , "col" : idc , "u" : u , "row" : rows }
325+ self .ineq_const_c += idc .shape [0 ]
326+
327+ def add_inequality_feature (self , feature , lower = True , mask = None ):
328+
329+ # add inequality value for the nodes of the mesh
330+ # flag lower determines whether the feature is a lower bound or upper bound
331+ # mask is just a boolean array determining which nodes to apply it to
332+
333+ value = feature (self .support .nodes )
334+ if mask is None :
335+ mask = np .ones (value .shape [0 ], dtype = bool )
336+ l = np .zeros (value .shape [0 ]) - np .inf
337+ u = np .zeros (value .shape [0 ]) + np .inf
338+ mask = np .logical_and (mask , ~ np .isnan (value ))
339+ if lower :
340+ l [mask ] = value [mask ]
341+ if lower == False :
342+ u [mask ] = value [mask ]
343+
344+ self .add_inequality_constraints_to_matrix (
345+ np .ones ((value .shape [0 ], 1 )),
346+ l ,
347+ u ,
348+ np .arange (0 , self .nx , dtype = int ),
349+ )
334350
335351 def add_tangent_constraints (self , w = 1.0 ):
336352 """
@@ -426,20 +442,21 @@ def build_matrix(self, square=True, damp=0.0, ie=False):
426442 nc = 0
427443 for c in self .equal_constraints .values ():
428444 aa = (c ["A" ]).flatten ()
429- b .extend ((c ["B" ] ).tolist ())
445+ b .extend ((c ["B" ]).tolist ())
430446 mask = aa == 0
431447 a .extend (aa [~ mask ].tolist ())
432448 rows .extend (c ["row" ].flatten ()[~ mask ].tolist ())
433449 cols .extend (c ["col" ].flatten ()[~ mask ].tolist ())
434450 C = coo_matrix (
435-
436- (np .array (a ), (np .array (rows ), cols )), shape = (self .eq_const_c , self .nx ), dtype = float
437- ).tocsr ()
438-
451+ (np .array (a ), (np .array (rows ), cols )),
452+ shape = (self .eq_const_c_ , self .nx ),
453+ dtype = float ,
454+ ).tocsr ()
455+
439456 d = np .array (b )
440457 ATA = bmat ([[ATA , C .T ], [C , None ]])
441458 ATB = np .hstack ([ATB , d ])
442-
459+
443460 if isinstance (damp , bool ):
444461 if damp == True :
445462 damp = np .finfo ("float" ).eps
@@ -449,7 +466,7 @@ def build_matrix(self, square=True, damp=0.0, ie=False):
449466 logger .info ("Adding eps to matrix diagonal" )
450467 ATA += eye (ATA .shape [0 ]) * damp
451468 if len (self .ineq_constraints ) > 0 and ie :
452- print (' using inequality constraints' )
469+ print (" using inequality constraints" )
453470 a = []
454471 l = []
455472 u = []
@@ -465,16 +482,19 @@ def build_matrix(self, square=True, damp=0.0, ie=False):
465482 rows .extend (c ["row" ].flatten ()[~ mask ].tolist ())
466483 cols .extend (c ["col" ].flatten ()[~ mask ].tolist ())
467484 Aie = coo_matrix (
468- (np .array (a ), (np .array (rows ), cols )), shape = (self .ineq_const_c , self .nx ), dtype = float
469- ).tocsc () # .tocsr()
485+ (np .array (a ), (np .array (rows ), cols )),
486+ shape = (self .ineq_const_c , self .nx ),
487+ dtype = float ,
488+ ).tocsc () # .tocsr()
470489
471490 uie = np .array (u )
472491 lie = np .array (l )
473492
474493 return ATA , ATB , Aie .T .dot (Aie ), Aie .T .dot (uie ), Aie .T .dot (lie )
475494 return ATA , ATB
476- def _solve_osqp (self , P , A , q , l , u ):
477-
495+
496+ def _solve_osqp (self , P , A , q , l , u ,mkl = False ):
497+
478498 try :
479499 import osqp
480500 except ImportError :
@@ -502,13 +522,21 @@ def _solve_osqp(self, P, A, q, l, u):
502522 # # Solve problem
503523 # res = prob.solve()
504524
505-
506525 # Create an OSQP object
507526 prob = osqp .OSQP ()
508527
509528 # Setup workspace
510529 # osqp likes csc matrices
511- prob .setup (P .tocsc (), np .array (q ), A .tocsc (), np .array (u ), np .array (l ))
530+ linsys_solver = 'qdldl'
531+ if mkl :
532+ linsys_solver = 'mkl pardiso'
533+
534+ try :
535+ prob .setup (P .tocsc (), np .array (q ), A .tocsc (), np .array (u ), np .array (l ),linsys_solver = linsys_solver )
536+ except ValueError :
537+ if mkl :
538+ logger .error ('MKL solver library path not correct. Please add to LD_LIBRARY_PATH' )
539+ raise LoopImportError ("Cannot import MKL pardiso" )
512540 res = prob .solve ()
513541 return res .x
514542
@@ -676,8 +704,8 @@ def _solve(self, solver="cg", **kwargs):
676704 damp = True
677705 if solver == "lsqr" :
678706 A , B = self .build_matrix (False )
679- elif solver == ' osqp' :
680- P , q , A , l , u = self .build_matrix (True ,ie = True )
707+ elif solver == " osqp" :
708+ P , q , A , l , u = self .build_matrix (True , ie = True )
681709 else :
682710 A , B = self .build_matrix (damp = damp )
683711
@@ -702,8 +730,8 @@ def _solve(self, solver="cg", **kwargs):
702730 if solver == "external" :
703731 logger .warning ("Using external solver" )
704732 self .c [self .region ] = kwargs ["external" ](A , B )[: self .nx ]
705- if solver == ' osqp' :
706- self .c [self .region ] = self ._solve_osqp (P , A , q , l , u ) # , **kwargs)
733+ if solver == " osqp" :
734+ self .c [self .region ] = self ._solve_osqp (P , A , q , l , u , mkl = kwargs . get ( 'mkl' , False )) # , **kwargs)
707735 # check solution is not nan
708736 # self.support.properties[self.propertyname] = self.c
709737 if np .all (self .c == np .nan ):
0 commit comments