Skip to content

Commit bcded11

Browse files
committed
Merge remote-tracking branch 'rael/wecc' into cplex
2 parents 25c5ca4 + d84a114 commit bcded11

File tree

5 files changed

+423
-310
lines changed

5 files changed

+423
-310
lines changed

switch_model/solve.py

Lines changed: 67 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,6 @@ def debug(type, value, tb):
131131
# get a list of modules to iterate through
132132
iterate_modules = get_iteration_list(model)
133133

134-
# Very that the proper modules are installed
135-
if model.options.solver == "cplex_direct":
136-
import cplex
137-
elif model.options.solver in ("gurobi_direct", "gurobi_aug"):
138-
import gurobipy
139-
140134
if model.options.verbose:
141135
print(
142136
"\n======================================================================="
@@ -514,6 +508,11 @@ def define_arguments(argparser):
514508

515509
# Define solver-related arguments
516510
# These are a subset of the arguments offered by "pyomo solve --solver=cplex --help"
511+
argparser.add_argument(
512+
"--solver",
513+
default="glpk",
514+
help='Name of Pyomo solver to use for the model (default is "glpk")',
515+
)
517516
argparser.add_argument(
518517
"--solver-manager",
519518
default="serial",
@@ -535,7 +534,7 @@ def define_arguments(argparser):
535534
argparser.add_argument(
536535
"--solver-method",
537536
default=None,
538-
type=str,
537+
type=int,
539538
help="Specify the solver method to use.",
540539
)
541540
argparser.add_argument(
@@ -693,14 +692,6 @@ def define_arguments(argparser):
693692
action="store_true",
694693
help="Automatically run switch graph after post solve",
695694
)
696-
argparser.add_argument(
697-
"--no-crossover",
698-
default=False,
699-
action="store_true",
700-
help="Disables crosssover when using the barrier algorithm. This reduces"
701-
" the solve time greatly however may result in less accurate values and may fail to find an optimal"
702-
" solution. If you find that the solver returns a suboptimal solution remove this flag.",
703-
)
704695

705696
argparser.add_argument(
706697
"--threads",
@@ -724,6 +715,14 @@ def define_arguments(argparser):
724715
" that all variables must be the same between the previous and current scenario.",
725716
)
726717

718+
argparser.add_argument(
719+
"--gurobi-make-mps",
720+
default=False,
721+
action="store_true",
722+
help="Instead of solving just output a Gurobi .mps file that can be used for debugging numerical properties."
723+
" See https://github.com/staadecker/lp-analyzer/ for details.",
724+
)
725+
727726

728727
def add_recommended_args(argparser):
729728
"""
@@ -743,7 +742,9 @@ def add_recommended_args(argparser):
743742
"--recommended-fast",
744743
default=False,
745744
action="store_true",
746-
help="Equivalent to --recommended with --no-crossover.",
745+
help="Equivalent to --recommended however disables crossover during solving. This reduces"
746+
" the solve time greatly however may result in less accurate values and may fail to find an optimal"
747+
" solution. If you find that the solver returns a suboptimal solution use --recommended.",
747748
)
748749

749750
argparser.add_argument(
@@ -753,12 +754,6 @@ def add_recommended_args(argparser):
753754
help="Same as --recommended but adds the flags --keepfiles --tempdir temp --symbolic-solver-labels which are useful when debugging Gurobi.",
754755
)
755756

756-
argparser.add_argument(
757-
"--solver",
758-
default="gurobi",
759-
help='Name of Pyomo solver to use for the model (default is "gurobi")',
760-
)
761-
762757

763758
def parse_recommended_args(args):
764759
argparser = _ArgumentParser(add_help=False, allow_abbrev=False)
@@ -775,24 +770,23 @@ def parse_recommended_args(args):
775770
if flags_used == 0:
776771
return args
777772

778-
# Note we don't append but rather prepend so that flags can override the --recommend flags to allow for overriding.
773+
# Note we don't append but rather prepend so that flags can override the --recommend flags.
779774
args = [
775+
"--solver",
776+
"gurobi",
780777
"-v",
781778
"--sorted-output",
782779
"--stream-output",
783780
"--log-run",
784781
"--debug",
785782
"--graph",
786783
"--solver-method",
787-
"barrier",
784+
"2", # Method 2 is barrier solve which is much faster than default
788785
] + args
789-
if options.solver in ("gurobi", "gurobi_direct", "gurobi_aug"):
790-
args = [
791-
"--solver-options-string",
792-
"BarHomogeneous=1 FeasibilityTol=1e-5",
793-
] + args
786+
solver_options_string = "BarHomogeneous=1 FeasibilityTol=1e-5"
794787
if options.recommended_fast:
795-
args = ["--no-crossover"] + args
788+
solver_options_string += " crossover=0"
789+
args = ["--solver-options-string", solver_options_string] + args
796790
if options.recommended_debug:
797791
args = ["--keepfiles", "--tempdir", "temp", "--symbolic-solver-labels"] + args
798792

@@ -887,79 +881,66 @@ def add_extra_suffixes(model):
887881

888882

889883
def solve(model):
890-
options_string = model.options.solver_options_string
891-
method = model.options.solver_method
892-
893-
# If we need warm start switch the solver to our augmented version that supports warm starting
894-
solver_type = model.options.solver
895-
gurobi_types = ("gurobi", "gurobi_direct", "gurobi_aug")
896-
cplex_types = ("cplex", "cplex_direct")
897-
898-
if model.options.warm_start is not None or model.options.save_warm_start:
899-
if solver_type not in gurobi_types:
900-
raise NotImplementedError(
901-
"Warm start functionality requires --solver gurobi"
902-
)
903-
model.options.solver = "gurobi_aug"
904-
905-
# Method 1 (dual simplex) is required since it supports warm starting.
906-
if model.options.warm_start is not None:
907-
method = 1
908-
909884
if hasattr(model, "solver"):
910885
solver = model.solver
911886
solver_manager = model.solver_manager
912887
else:
888+
# If we need warm start switch the solver to our augmented version that supports warm starting
889+
if model.options.warm_start is not None or model.options.save_warm_start:
890+
if model.options.solver not in ("gurobi", "gurobi_direct"):
891+
raise NotImplementedError(
892+
"Warm start functionality requires --solver gurobi"
893+
)
894+
model.options.solver = "gurobi_aug"
895+
896+
if model.options.warm_start is not None:
897+
# Method 1 (dual simplex) is required since it supports warm starting.
898+
model.options.solver_method = 1
899+
913900
# Create a solver object the first time in. We don't do this until a solve is
914901
# requested, because sometimes a different solve function may be used,
915902
# with its own solver object (e.g., with runph or a parallel solver server).
916903
# In those cases, we don't want to go through the expense of creating an
917904
# unused solver object, or get errors if the solver options are invalid.
918905
#
919906
# Note previously solver was saved in model however this is very memory inefficient.
920-
solver = SolverFactory(solver_type, solver_io=model.options.solver_io)
921-
solver_manager = SolverManagerFactory(model.options.solver_manager)
922-
923-
# If this option is enabled, gurobi will output an IIS to outputs\iis.ilp.
924-
if model.options.gurobi_find_iis:
925-
# Enable symbolic labels since otherwise we can't debug the .ilp file.
926-
model.options.symbolic_solver_labels = True
927-
928-
# Add to the solver options 'ResultFile=iis.ilp'
929-
# https://stackoverflow.com/a/51994135/5864903
930-
iis_file_path = os.path.join(model.options.outputs_dir, "iis.ilp")
931-
options_string += f" ResultFile={iis_file_path}"
932-
933-
if model.options.no_crossover:
934-
if solver_type in gurobi_types:
935-
options_string += " crossover=0"
936-
elif solver_type in cplex_types:
937-
options_string = " solutiontype=2"
938-
else:
939-
raise NotImplementedError(
940-
f"--no-crossover not implemented for solver {solver}"
907+
solver = SolverFactory(model.options.solver, solver_io=model.options.solver_io)
908+
909+
if model.options.gurobi_find_iis and model.options.gurobi_make_mps:
910+
raise Exception("Can't use --gurobi-find-iis with --gurobi-make-mps.")
911+
912+
if model.options.gurobi_find_iis or model.options.gurobi_make_mps:
913+
# If we are outputting a file we want to enable symbolic labels to help debugging
914+
model.options.symbolic_solver_labels = True
915+
916+
# If this option is enabled, gurobi will output an IIS to outputs\iis.ilp.
917+
if model.options.gurobi_find_iis:
918+
# Add to the solver options 'ResultFile=iis.ilp'
919+
# https://stackoverflow.com/a/51994135/5864903
920+
model.options.solver_options_string += " ResultFile=iis.ilp"
921+
if model.options.gurobi_make_mps:
922+
# Output the input file and set time limit to zero to ensure it doesn't actually solve
923+
model.options.solver_options_string += (
924+
f" ResultFile=problem.mps TimeLimit=0"
941925
)
942926

943-
if model.options.threads is not None:
944-
options_string += f" threads={model.options.threads}"
945-
946-
if method is not None:
947-
if solver_type in gurobi_types:
948-
if method == "barrier":
949-
method = 2
950-
options_string += f" method={method}"
951-
elif solver_type in cplex_types:
952-
if method == "barrier":
953-
method = 4
954-
options_string += f" LPMethod={method}"
955-
else:
956-
raise NotImplementedError(
957-
f"Can't specify method {method} for solver {solver_type}"
927+
if model.options.threads:
928+
model.options.solver_options_string += f" Threads={model.options.threads}"
929+
930+
if model.options.solver_method is not None:
931+
# If no string is passed make the string empty so we can add to it
932+
if model.options.solver_options_string is None:
933+
model.options.solver_options_string = ""
934+
935+
model.options.solver_options_string += (
936+
f" method={model.options.solver_method}"
958937
)
959938

939+
solver_manager = SolverManagerFactory(model.options.solver_manager)
940+
960941
# get solver arguments
961942
solver_args = dict(
962-
options_string=options_string,
943+
options_string=model.options.solver_options_string,
963944
keepfiles=model.options.keepfiles,
964945
tee=model.options.tee,
965946
symbolic_solver_labels=model.options.symbolic_solver_labels,

0 commit comments

Comments
 (0)