The focus in this post is on applying solver settings and retrieving solution information.
Solver settings
Although the elytica platform accommodates multiple solver technologies, there are some common settings that are useful. Within a mixed integer linear programming context, the two most important settings are, setting time limits and setting gap limits. These settings are applied within the main Python function, after the initialization of the model, and prior to solving the model.
Time limits are specified as seconds using the following function.
elytica.set_time_limit("name_of_your_model", limit_as_seconds);
Gap limits are specified as fractions with the following function
elytica.set_gap_limit("name_of_your_model", limit_as_fraction);
As an example, if you want to terminate the optimization of a very large traveling salesman problem (since it may take a very long time to solve it to optimality due to the computational complexity of the problem), you may do so by setting both a time limit and a gap limit. For instance, by setting a time limit of an hour and a gap limit of, say 30%, the optimization run will terminated on reaching either of the two limits.
The corresponding main function with the specified limits, is the following:
def main(): elytica.init_model("ts") elytica.set_time_limit("ts", 3600); elytica.set_gap_limit("ts", 0.3); elytica.run_model("ts") #insert code here to print solution return 0
Retrieving solution information
Irrespective of the specific solver technology available to you through your subscription, there is some basic solution information you can retrieve following the completion of an optimization run.
The most important piece of information is the solution status. That is, whether the optimization solver terminated with an optimal solution, just a feasible solution (which is what you may get when applying a time limit or a gap limit), an unbounded solution, no solution due to infeasibility, or no solution yet within the specified limits.
The solution status of an optimization run is provided as the return value of the function elytica.run_model(.)
. This return value is a string which may take on the following:
- OPTIMAL – an optimal solution was obtained
- FEASIBLE – one or more feasible solutions were generated within the specified limits (time or gap)
- UNBOUNDED – the solution is unbounded
- INFEASIBLE – no solution is possible given the model constraints
- UNKNOWN – on termination due to reaching a specified time or gap limit, no feasible solution has been generated yet, but infeasibility cannot be confirmed either.
Retrieving the solution status is especially useful for post optimization purposes – e.g. to determine if solution values of decision variables should be printed or not. The following code segment illustrates the idea:
def main(): elytica.init_model("ts") status = elytica.run_model("ts") if status == "FEASIBLE" or status == "OPTIMAL": #insert code here for post processing - eg print solution return 0
Some other useful information to know includes e.g., what the bounds are for the current solution, or how long an optimization run took to complete. The following code segment shows you how to do this.
import time def main(): elytica.init_model("ts") start_time = time.time() status = elytica.run_model("ts") end_time = time.time() if status == "FEASIBLE" or status == "OPTIMAL": best_pb = elytica.get_best_primal_bound("ts") best_db = elytica.get_best_dual_bound("ts") print("Solution primal bound: ", best_pb) print("Solution dual bound: ", best_db) print("Run time: ", end_time - start_time) return 0