@@ -113,6 +113,12 @@ def debug(type, value, tb):
113113 # get a list of modules to iterate through
114114 iterate_modules = get_iteration_list (model )
115115
116+ # Very that the proper modules are installed
117+ if model .options .solver == "cplex_direct" :
118+ import cplex
119+ elif model .options .solver in ("gurobi_direct" , "gurobi_aug" ):
120+ import gurobipy
121+
116122 if model .options .verbose :
117123 print ("\n =======================================================================" )
118124 print ("Switch {}, http://switch-model.org" .format (switch_model .__version__ ))
@@ -434,8 +440,6 @@ def define_arguments(argparser):
434440
435441 # Define solver-related arguments
436442 # These are a subset of the arguments offered by "pyomo solve --solver=cplex --help"
437- argparser .add_argument ("--solver" , default = "glpk" ,
438- help = 'Name of Pyomo solver to use for the model (default is "glpk")' )
439443 argparser .add_argument ("--solver-manager" , default = "serial" ,
440444 help = 'Name of Pyomo solver manager to use for the model ("neos" to use remote NEOS server)' )
441445 argparser .add_argument ("--solver-io" , default = None , help = "Method for Pyomo to use to communicate with solver" )
@@ -444,7 +448,7 @@ def define_arguments(argparser):
444448 argparser .add_argument ("--solver-options-string" , default = "" ,
445449 help = 'A quoted string of options to pass to the model solver. Each option must be of the form option=value. '
446450 '(e.g., --solver-options-string "mipgap=0.001 primalopt=\' \' advance=2 threads=1")' )
447- argparser .add_argument ("--solver-method" , default = None , type = int ,
451+ argparser .add_argument ("--solver-method" , default = None , type = str ,
448452 help = "Specify the solver method to use." )
449453 argparser .add_argument ("--keepfiles" , action = 'store_true' , default = None ,
450454 help = "Keep temporary files produced by the solver (may be useful with --symbolic-solver-labels)" )
@@ -535,6 +539,12 @@ def define_arguments(argparser):
535539 "--graph" , default = False , action = 'store_true' ,
536540 help = "Automatically run switch graph after post solve"
537541 )
542+ argparser .add_argument (
543+ "--no-crossover" , default = False , action = 'store_true' ,
544+ help = "Disables crosssover when using the barrier algorithm. This reduces"
545+ ' the solve time greatly however may result in less accurate values and may fail to find an optimal'
546+ " solution. If you find that the solver returns a suboptimal solution remove this flag."
547+ )
538548
539549 argparser .add_argument (
540550 "--threads" , type = int , default = None ,
@@ -575,16 +585,17 @@ def add_recommended_args(argparser):
575585
576586 argparser .add_argument (
577587 "--recommended-fast" , default = False , action = 'store_true' ,
578- help = 'Equivalent to --recommended however disables crossover during solving. This reduces'
579- ' the solve time greatly however may result in less accurate values and may fail to find an optimal'
580- ' solution. If you find that the solver returns a suboptimal solution use --recommended.'
588+ help = 'Equivalent to --recommended with --no-crossover.'
581589 )
582590
583591 argparser .add_argument (
584592 "--recommended-debug" , default = False , action = 'store_true' ,
585593 help = 'Same as --recommended but adds the flags --keepfiles --tempdir temp --symbolic-solver-labels which are useful when debugging Gurobi.'
586594 )
587595
596+ argparser .add_argument ("--solver" , default = "gurobi" ,
597+ help = 'Name of Pyomo solver to use for the model (default is "gurobi")' )
598+
588599
589600def parse_recommended_args (args ):
590601 argparser = _ArgumentParser (add_help = False , allow_abbrev = False )
@@ -597,21 +608,20 @@ def parse_recommended_args(args):
597608 if flags_used == 0 :
598609 return args
599610
600- # Note we don't append but rather prepend so that flags can override the --recommend flags.
611+ # Note we don't append but rather prepend so that flags can override the --recommend flags to allow for overriding .
601612 args = [
602- '--solver' , 'gurobi' ,
603613 '-v' ,
604614 '--sorted-output' ,
605615 '--stream-output' ,
606616 '--log-run' ,
607617 '--debug' ,
608618 '--graph' ,
609- '--solver-method' , '2' , # Method 2 is barrier solve which is much faster than default
619+ '--solver-method' , 'barrier' ,
610620 ] + args
611- solver_options_string = "BarHomogeneous=1 FeasibilityTol=1e-5"
621+ if options .solver in ("gurobi" , "gurobi_direct" , "gurobi_aug" ):
622+ args = ['--solver-options-string' , "BarHomogeneous=1 FeasibilityTol=1e-5" ] + args
612623 if options .recommended_fast :
613- solver_options_string += " crossover=0"
614- args = ['--solver-options-string' , solver_options_string ] + args
624+ args = ["--no-crossover" ] + args
615625 if options .recommended_debug :
616626 args = ['--keepfiles' , '--tempdir' , 'temp' , '--symbolic-solver-labels' ] + args
617627
@@ -690,35 +700,43 @@ def add_extra_suffixes(model):
690700
691701
692702def solve (model ):
703+ options_string = model .options .solver_options_string
704+ method = model .options .solver_method
705+
706+ # If we need warm start switch the solver to our augmented version that supports warm starting
707+ solver_type = model .options .solver
708+ gurobi_types = ("gurobi" , "gurobi_direct" , "gurobi_aug" )
709+ cplex_types = ("cplex" , "cplex_direct" )
710+
711+ if model .options .warm_start is not None or model .options .save_warm_start :
712+ if solver_type not in gurobi_types :
713+ raise NotImplementedError ("Warm start functionality requires --solver gurobi" )
714+ model .options .solver = "gurobi_aug"
715+
716+ # Method 1 (dual simplex) is required since it supports warm starting.
717+ if model .options .warm_start is not None :
718+ method = 1
719+
693720 if hasattr (model , "solver" ):
694721 solver = model .solver
695722 solver_manager = model .solver_manager
696723 else :
697- # If we need warm start switch the solver to our augmented version that supports warm starting
698- if model .options .warm_start is not None or model .options .save_warm_start :
699- if model .options .solver not in ("gurobi" , "gurobi_direct" ):
700- raise NotImplementedError ("Warm start functionality requires --solver gurobi" )
701- model .options .solver = "gurobi_aug"
702-
703- if model .options .warm_start is not None :
704- # Method 1 (dual simplex) is required since it supports warm starting.
705- model .options .solver_method = 1
706-
707724 # Create a solver object the first time in. We don't do this until a solve is
708725 # requested, because sometimes a different solve function may be used,
709726 # with its own solver object (e.g., with runph or a parallel solver server).
710727 # In those cases, we don't want to go through the expense of creating an
711728 # unused solver object, or get errors if the solver options are invalid.
712729 #
713730 # Note previously solver was saved in model however this is very memory inefficient.
714- solver = SolverFactory (model .options .solver , solver_io = model .options .solver_io )
731+ solver = SolverFactory (solver_type , solver_io = model .options .solver_io )
732+ solver_manager = SolverManagerFactory (model .options .solver_manager )
715733
716- if model .options .gurobi_find_iis and model .options .gurobi_make_mps :
717- raise Exception ("Can't use --gurobi-find-iis with --gurobi-make-mps." )
734+ if model .options .gurobi_find_iis and model .options .gurobi_make_mps :
735+ raise Exception ("Can't use --gurobi-find-iis with --gurobi-make-mps." )
718736
719- if model .options .gurobi_find_iis or model .options .gurobi_make_mps :
720- # If we are outputting a file we want to enable symbolic labels to help debugging
721- model .options .symbolic_solver_labels = True
737+ if model .options .gurobi_find_iis or model .options .gurobi_make_mps :
738+ # If we are outputting a file we want to enable symbolic labels to help debugging
739+ model .options .symbolic_solver_labels = True
722740
723741 # If this option is enabled, gurobi will output an IIS to outputs\iis.ilp.
724742 if model .options .gurobi_find_iis :
@@ -729,21 +747,32 @@ def solve(model):
729747 # Output the input file and set time limit to zero to ensure it doesn't actually solve
730748 model .options .solver_options_string += f" ResultFile=problem.mps TimeLimit=0"
731749
732- if model .options .threads :
733- model .options .solver_options_string += f" Threads={ model .options .threads } "
734-
735- if model .options .solver_method is not None :
736- # If no string is passed make the string empty so we can add to it
737- if model .options .solver_options_string is None :
738- model .options .solver_options_string = ""
739-
740- model .options .solver_options_string += f" method={ model .options .solver_method } "
741-
742- solver_manager = SolverManagerFactory (model .options .solver_manager )
750+ if model .options .no_crossover :
751+ if solver_type in gurobi_types :
752+ options_string += " crossover=0"
753+ elif solver_type in cplex_types :
754+ options_string = " solutiontype=2"
755+ else :
756+ raise NotImplementedError (f"--no-crossover not implemented for solver { solver } " )
757+
758+ if model .options .threads is not None :
759+ options_string += f" threads={ model .options .threads } "
760+
761+ if method is not None :
762+ if solver_type in gurobi_types :
763+ if method == "barrier" :
764+ method = 2
765+ options_string += f" method={ method } "
766+ elif solver_type in cplex_types :
767+ if method == "barrier" :
768+ method = 4
769+ options_string += f" LPMethod={ method } "
770+ else :
771+ raise NotImplementedError (f"Can't specify method { method } for solver { solver_type } " )
743772
744773 # get solver arguments
745774 solver_args = dict (
746- options_string = model . options . solver_options_string ,
775+ options_string = options_string ,
747776 keepfiles = model .options .keepfiles ,
748777 tee = model .options .tee ,
749778 symbolic_solver_labels = model .options .symbolic_solver_labels ,
0 commit comments