1

So I'm have trouble in python version of CPLEX. I would think it would be pretty easy to get like a summary of what the solver did i.e number of branches, etc.

Anyone know how to do this?

import cplex
from cplex.exceptions import CplexError
class knapsack:
    def __init__(self,N,g,square_list):
        self.N = N
        self.square_list= square_list
        self.g = g
    def solve_problem(self):
        try:
            my_prob = cplex.Cplex()
            prob =my_prob
            prob.set_log_stream(None)
            prob.set_error_stream(None)
            prob.set_warning_stream(None)
            prob.set_results_stream(None)
            my_obj = self.g
            my_ctype = "B"
            number_of_one = self.square_list.count(1.0)
            my_ctype = my_ctype*len(self.square_list)
            val = self.N  -number_of_one
            rhs=[val]
            my_sense="L"
            my_rownames = ["r1"]

            counter =0
            variable_list=[]
            coiff_list=[]
            for i in self.square_list:
                if i==0:
                    coiff_list.append(1.0)
                else:
                    coiff_list.append(-1.0)
                variable_list.append("w" + str(counter))
                counter+=1

            rows = [[variable_list, coiff_list]]
            prob.objective.set_sense(prob.objective.sense.minimize)

            prob.variables.add(obj=my_obj, types=my_ctype,
                        names=variable_list)
            prob.linear_constraints.add(lin_expr=rows, senses=my_sense,
                                    rhs=rhs)
            my_prob.solve()
            x = my_prob.solution.get_values()
            print(my_prob.solution.get_status())
            print("---")
            print(my_prob.solution.status())
            return x
        except CplexError as exc:
            print(exc)
            return

When I look at the methods associated with my_prob and myprob.solution I see

['MIP_starts', 'SOS', '_Cplex__copy_init', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_aborter', '_disposed', '_env', '_env_lp_ptr', '_invoke_generic_callback', '_is_MIP', '_is_special_filetype', '_lp', 'advanced', 'cleanup', 'conflict', 'copy_vmconfig', 'del_vmconfig', 'double_annotations', 'end', 'feasopt', 'get_aborter', 'get_dettime', 'get_num_cores', 'get_problem_name', 'get_problem_type', 'get_stats', 'get_time', 'get_version', 'get_versionnumber', 'has_vmconfig', 'indicator_constraints', 'linear_constraints', 'long_annotations', 'objective', 'order', 'parameters', 'populate_solution_pool', 'presolve', 'problem_type', 'pwl_constraints', 'quadratic_constraints', 'read', 'read_annotations', 'read_copy_vmconfig', 'register_callback', 'remove_aborter', 'runseeds', 'set_callback', 'set_error_stream', 'set_log_stream', 'set_problem_name', 'set_problem_type', 'set_results_stream', 'set_warning_stream', 'solution', 'solve', 'start', 'unregister_callback', 'use_aborter', 'variables', 'write', 'write_annotations', 'write_benders_annotation']
['MIP', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_add_iter', '_add_single', '_conv', '_cplex', '_env', '_get_index', '_get_index_function', 'advanced', 'basis', 'get_activity_levels', 'get_dual_values', 'get_float_quality', 'get_indicator_slacks', 'get_indices', 'get_integer_quality', 'get_linear_slacks', 'get_method', 'get_objective_value', 'get_quadratic_activity_levels', 'get_quadratic_dualslack', 'get_quadratic_slacks', 'get_quality_metrics', 'get_reduced_costs', 'get_solution_type', 'get_status', 'get_status_string', 'get_values', 'infeasibility', 'is_dual_feasible', 'is_primal_feasible', 'method', 'pool', 'progress', 'quality_metric', 'sensitivity', 'status', 'type', 'write']
Eigenvalue
  • 1,093
  • 1
  • 14
  • 35
  • Does the documentation [here](https://www.ibm.com/support/knowledgecenter/SSSA5P_12.6.2/ilog.odms.cplex.help/refpythoncplex/html/cplex.Cplex-class.html) help? – LarrySnyder610 May 30 '19 at 18:13
  • 1
    Thanks a lot. I was able to find something - I'll answer my question now. – Eigenvalue May 30 '19 at 19:42

2 Answers2

2

Found that there is a write function on the solution object

my_prob.solution.write("myanswer")

This contains all relevant information on the CPLEX run.

Eigenvalue
  • 1,093
  • 1
  • 14
  • 35
  • Indeed, you can read about SOL files [here](https://www.ibm.com/support/knowledgecenter/SSSA5P_12.9.0/ilog.odms.cplex.help/CPLEX/FileFormats/topics/SOL.html). – rkersh May 30 '19 at 19:49
1

If you are familiar with the CPLEX interactive, you may be used to seeing something like the following summary after the optimization:

MIP - Integer optimal, tolerance (0.0001/1e-06):  Objective = -2.0183208990e+02
Current MIP best bound = -2.0181209207e+02 (gap = 0.0199978, 0.01%)
Solution time =    1.43 sec.  Iterations = 25361  Nodes = 4335 (21)
Deterministic time = 686.22 ticks  (479.17 ticks/sec)

As suggested in the comments section, all of this information can be queried if you look through the documentation here. Most of it comes from the Cplex.solution interface as you suggested.

For example, consider the following interactive session:

>>> c.problem_type[c.get_problem_type()]
'MILP'
>>> c.solution.get_status_string()
'integer optimal, tolerance'
>>> c.parameters.mip.tolerances.mipgap.get()
0.0001
>>> c.parameters.mip.tolerances.absmipgap.get()
1e-06
>>> c.solution.get_objective_value()
-201.83208990000034
>>> c.solution.MIP.get_best_objective()
-201.8120920681663
>>> c.solution.MIP.get_mip_relative_gap()
9.908152783804216e-05
>>> print(c.solution.get_quality_metrics())
Incumbent solution:
MILP objective                                -2.0183208990e+02
MILP solution norm |x| (Total, Max)            4.65432e+02  2.02051e+02
MILP solution error (Ax=b) (Total, Max)        5.24512e-11  2.34035e-12
MILP x bound error (Total, Max)                0.00000e+00  0.00000e+00
MILP x integrality error (Total, Max)          0.00000e+00  0.00000e+00
MILP slack bound error (Total, Max)            4.54747e-13  4.54747e-13
>>> c.solution.MIP.get_incumbent_node()
4266
>>> c.solution.MIP.get_num_cuts(c.solution.MIP.cut_type.GUB_cover)
3
rkersh
  • 4,447
  • 2
  • 22
  • 31
  • Hey just curious - I used c.solution.MIP.get_incumbent_node() thinking this would tell me the number of branch and bounds nodes searched before writing solution but it always returns 0 - even though the output solution is different than the input. Advice? – Eigenvalue Mar 10 '20 at 04:09
  • Under the hood, `Cplex.solution.MIP.get_incumbent_node()` uses [CPXXgetnodeint](https://www.ibm.com/support/knowledgecenter/SSSA5P_12.10.0/ilog.odms.cplex.help/refcallablelibrary/mipapi/getnodeint.html) from the C API. Without seeing the engine log or knowing any other details about your model it's hard to say why you are getting this behavior. If you can provide a small example to reproduce the behavior this would be worthy of being asked as a new question on stackoverflow. Alternatively, if you prefer to keep this private, you can send me an email at rkersh at us dot ibm dot com. – rkersh Mar 10 '20 at 13:33
  • Hi rkersh, thanks for the reply. I made a new thread because I think this is a question that people who are solving IP and using your python version would find useful. https://stackoverflow.com/questions/60623801/getting-the-number-of-branch-and-bound-nodes-explored-cplex – Eigenvalue Mar 10 '20 at 18:29