@@ -62,6 +62,77 @@ def _is_numeric(x):
62
62
return True
63
63
64
64
65
+ class _VariableData (object ):
66
+ def __init__ (self , solver_model ):
67
+ self ._solver_model = solver_model
68
+ self .lb = []
69
+ self .ub = []
70
+ self .types = []
71
+ self .names = []
72
+
73
+ def add (self , lb , ub , type_ , name ):
74
+ self .lb .append (lb )
75
+ self .ub .append (ub )
76
+ self .types .append (type_ )
77
+ self .names .append (name )
78
+
79
+ def __enter__ (self ):
80
+ return self
81
+
82
+ def __exit__ (self , * excinfo ):
83
+ self ._solver_model .variables .add (
84
+ lb = self .lb , ub = self .ub , types = self .types , names = self .names
85
+ )
86
+
87
+
88
+ class _LinearConstraintData (object ):
89
+ def __init__ (self , solver_model ):
90
+ self ._solver_model = solver_model
91
+ self .lin_expr = []
92
+ self .senses = []
93
+ self .rhs = []
94
+ self .range_values = []
95
+ self .names = []
96
+
97
+ def add (self , cplex_expr , sense , rhs , range_values , name ):
98
+ self .lin_expr .append ([cplex_expr .variables , cplex_expr .coefficients ])
99
+ self .senses .append (sense )
100
+ self .rhs .append (rhs )
101
+ self .range_values .append (range_values )
102
+ self .names .append (name )
103
+
104
+ def __enter__ (self ):
105
+ return self
106
+
107
+ def __exit__ (self , * excinfo ):
108
+ self ._solver_model .linear_constraints .add (
109
+ lin_expr = self .lin_expr ,
110
+ senses = self .senses ,
111
+ rhs = self .rhs ,
112
+ range_values = self .range_values ,
113
+ names = self .names ,
114
+ )
115
+
116
+
117
+ class nullcontext (object ):
118
+ """Context manager that does no additional processing.
119
+ Used as a stand-in for a normal context manager, when a particular
120
+ block of code is only sometimes used with a normal context manager:
121
+ cm = optional_cm if condition else nullcontext()
122
+ with cm:
123
+ # Perform operation, using optional_cm if condition is True
124
+ """
125
+
126
+ def __init__ (self , enter_result = None ):
127
+ self .enter_result = enter_result
128
+
129
+ def __enter__ (self ):
130
+ return self .enter_result
131
+
132
+ def __exit__ (self , * excinfo ):
133
+ pass
134
+
135
+
65
136
@SolverFactory .register ('cplex_direct' , doc = 'Direct python interface to CPLEX' )
66
137
class CPLEXDirect (DirectSolver ):
67
138
@@ -248,7 +319,7 @@ def _get_expr_from_pyomo_expr(self, expr, max_degree=2):
248
319
249
320
return cplex_expr , referenced_vars
250
321
251
- def _add_var (self , var ):
322
+ def _add_var (self , var , cplex_var_data = None ):
252
323
varname = self ._symbol_map .getSymbol (var , self ._labeler )
253
324
vtype = self ._cplex_vtype_from_var (var )
254
325
if var .has_lb ():
@@ -260,7 +331,14 @@ def _add_var(self, var):
260
331
else :
261
332
ub = self ._cplex .infinity
262
333
263
- self ._solver_model .variables .add (lb = [lb ], ub = [ub ], types = [vtype ], names = [varname ])
334
+
335
+ ctx = (
336
+ _VariableData (self ._solver_model )
337
+ if cplex_var_data is None
338
+ else nullcontext (cplex_var_data )
339
+ )
340
+ with ctx as cplex_var_data :
341
+ cplex_var_data .add (lb = lb , ub = ub , type_ = vtype , name = varname )
264
342
265
343
self ._pyomo_var_to_solver_var_map [var ] = varname
266
344
self ._solver_var_to_pyomo_var_map [varname ] = var
@@ -303,7 +381,49 @@ def _set_instance(self, model, kwds={}):
303
381
"by overwriting its bounds in the CPLEX instance."
304
382
% (var .name , self ._pyomo_model .name ,))
305
383
306
- def _add_constraint (self , con ):
384
+ def _add_block (self , block ):
385
+ with _VariableData (self ._solver_model ) as cplex_var_data :
386
+ for var in block .component_data_objects (
387
+ ctype = pyomo .core .base .var .Var , descend_into = True , active = True , sort = True
388
+ ):
389
+ self ._add_var (var , cplex_var_data )
390
+
391
+ with _LinearConstraintData (self ._solver_model ) as cplex_lin_con_data :
392
+ for sub_block in block .block_data_objects (descend_into = True , active = True ):
393
+ for con in sub_block .component_data_objects (
394
+ ctype = pyomo .core .base .constraint .Constraint ,
395
+ descend_into = False ,
396
+ active = True ,
397
+ sort = True ,
398
+ ):
399
+ if not con .has_lb () and not con .has_ub ():
400
+ assert not con .equality
401
+ continue # non-binding, so skip
402
+
403
+ self ._add_constraint (con , cplex_lin_con_data )
404
+
405
+ for con in sub_block .component_data_objects (
406
+ ctype = pyomo .core .base .sos .SOSConstraint ,
407
+ descend_into = False ,
408
+ active = True ,
409
+ sort = True ,
410
+ ):
411
+ self ._add_sos_constraint (con )
412
+
413
+ obj_counter = 0
414
+ for obj in sub_block .component_data_objects (
415
+ ctype = pyomo .core .base .objective .Objective ,
416
+ descend_into = False ,
417
+ active = True ,
418
+ ):
419
+ obj_counter += 1
420
+ if obj_counter > 1 :
421
+ raise ValueError (
422
+ "Solver interface does not support multiple objectives."
423
+ )
424
+ self ._set_objective (obj )
425
+
426
+ def _add_constraint (self , con , cplex_lin_con_data = None ):
307
427
if not con .active :
308
428
return None
309
429
@@ -314,12 +434,12 @@ def _add_constraint(self, con):
314
434
315
435
if con ._linear_canonical_form :
316
436
cplex_expr , referenced_vars = self ._get_expr_from_pyomo_repn (
317
- con .canonical_form (),
318
- self . _max_constraint_degree )
437
+ con .canonical_form (), self . _max_constraint_degree
438
+ )
319
439
else :
320
440
cplex_expr , referenced_vars = self ._get_expr_from_pyomo_expr (
321
- con .body ,
322
- self . _max_constraint_degree )
441
+ con .body , self . _max_constraint_degree
442
+ )
323
443
324
444
if con .has_lb ():
325
445
if not is_fixed (con .lower ):
@@ -330,39 +450,39 @@ def _add_constraint(self, con):
330
450
raise ValueError ("Upper bound of constraint {0} "
331
451
"is not constant." .format (con ))
332
452
453
+ range_ = 0.0
333
454
if con .equality :
334
- my_sense = 'E'
335
- my_rhs = [value (con .lower ) - cplex_expr .offset ]
336
- my_range = []
455
+ sense = "E"
456
+ rhs = value (con .lower ) - cplex_expr .offset
337
457
elif con .has_lb () and con .has_ub ():
338
- my_sense = 'R'
458
+ sense = "R"
339
459
lb = value (con .lower )
340
460
ub = value (con .upper )
341
- my_rhs = [ ub - cplex_expr .offset ]
342
- my_range = [ lb - ub ]
461
+ rhs = ub - cplex_expr .offset
462
+ range_ = lb - ub
343
463
self ._range_constraints .add (con )
344
464
elif con .has_lb ():
345
- my_sense = 'G'
346
- my_rhs = [value (con .lower ) - cplex_expr .offset ]
347
- my_range = []
465
+ sense = "G"
466
+ rhs = value (con .lower ) - cplex_expr .offset
348
467
elif con .has_ub ():
349
- my_sense = 'L'
350
- my_rhs = [value (con .upper ) - cplex_expr .offset ]
351
- my_range = []
468
+ sense = "L"
469
+ rhs = value (con .upper ) - cplex_expr .offset
352
470
else :
353
- raise ValueError ("Constraint does not have a lower "
354
- "or an upper bound: {0} \n " .format (con ))
471
+ raise ValueError (
472
+ "Constraint does not have a lower "
473
+ "or an upper bound: {0} \n " .format (con )
474
+ )
355
475
356
476
if len (cplex_expr .q_coefficients ) == 0 :
357
- self . _solver_model . linear_constraints . add (
358
- lin_expr = [[ cplex_expr . variables ,
359
- cplex_expr . coefficients ]],
360
- senses = my_sense ,
361
- rhs = my_rhs ,
362
- range_values = my_range ,
363
- names = [ conname ] )
477
+ ctx = (
478
+ _LinearConstraintData ( self . _solver_model )
479
+ if cplex_lin_con_data is None
480
+ else nullcontext ( cplex_lin_con_data )
481
+ )
482
+ with ctx as cplex_lin_con_data :
483
+ cplex_lin_con_data . add ( cplex_expr , sense , rhs , range_ , conname )
364
484
else :
365
- if my_sense == 'R' :
485
+ if sense == 'R' :
366
486
raise ValueError ("The CPLEXDirect interface does not "
367
487
"support quadratic range constraints: "
368
488
"{0}" .format (con ))
@@ -372,8 +492,8 @@ def _add_constraint(self, con):
372
492
quad_expr = [cplex_expr .q_variables1 ,
373
493
cplex_expr .q_variables2 ,
374
494
cplex_expr .q_coefficients ],
375
- sense = my_sense ,
376
- rhs = my_rhs [ 0 ] ,
495
+ sense = sense ,
496
+ rhs = rhs ,
377
497
name = conname )
378
498
379
499
for var in referenced_vars :
0 commit comments