@@ -17,6 +17,7 @@ from posix.stdio cimport fileno
17
17
18
18
from collections.abc import Iterable
19
19
from itertools import repeat
20
+ from dataclasses import dataclass
20
21
21
22
include " expr.pxi"
22
23
include " lp.pxi"
@@ -5102,6 +5103,97 @@ cdef class Model:
5102
5103
PY_SCIP_CALL(SCIPprintStatistics(self ._scip, cfile))
5103
5104
5104
5105
locale.setlocale(locale.LC_NUMERIC,user_locale)
5106
+
5107
+ def readStatistics (self , filename ):
5108
+ """
5109
+ Given a .stats file of a solved model, reads it and returns an instance of the Statistics class
5110
+ holding some statistics.
5111
+
5112
+ Keyword arguments:
5113
+ filename -- name of the input file
5114
+ """
5115
+ result = {}
5116
+ file = open (filename)
5117
+ data = file .readlines()
5118
+
5119
+ assert " problem is solved" in data[0 ], " readStatistics can only be called if the problem was solved"
5120
+ available_stats = [" Total Time" , " solving" , " presolving" , " reading" , " copying" ,
5121
+ " Problem name" , " Variables" , " Constraints" , " number of runs" ,
5122
+ " nodes" , " Solutions found" , " First Solution" , " Primal Bound" ,
5123
+ " Dual Bound" , " Gap" , " primal-dual" ]
5124
+
5125
+ seen_cons = 0
5126
+ for i, line in enumerate (data):
5127
+ split_line = line.split(" :" )
5128
+ split_line[1 ] = split_line[1 ][:- 1 ] # removing \n
5129
+ stat_name = split_line[0 ].strip()
5130
+
5131
+ if seen_cons == 2 and stat_name == " Constraints" :
5132
+ continue
5133
+
5134
+ if stat_name in available_stats:
5135
+ cur_stat = split_line[0 ].strip()
5136
+ relevant_value = split_line[1 ].strip()
5137
+
5138
+ if stat_name == " Variables" :
5139
+ relevant_value = relevant_value[:- 1 ] # removing ")"
5140
+ var_stats = {}
5141
+ split_var = relevant_value.split(" (" )
5142
+ var_stats[" total" ] = int (split_var[0 ])
5143
+ split_var = split_var[1 ].split(" ," )
5144
+
5145
+ for var_type in split_var:
5146
+ split_result = var_type.strip().split(" " )
5147
+ var_stats[split_result[1 ]] = int (split_result[0 ])
5148
+
5149
+ if " Original" in data[i- 2 ]:
5150
+ result[" Variables" ] = var_stats
5151
+ else :
5152
+ result[" Presolved Variables" ] = var_stats
5153
+
5154
+ continue
5155
+
5156
+ if stat_name == " Constraints" :
5157
+ seen_cons += 1
5158
+ con_stats = {}
5159
+ split_con = relevant_value.split(" ," )
5160
+ for con_type in split_con:
5161
+ split_result = con_type.strip().split(" " )
5162
+ con_stats[split_result[1 ]] = int (split_result[0 ])
5163
+
5164
+ if " Original" in data[i- 3 ]:
5165
+ result[" Constraints" ] = con_stats
5166
+ else :
5167
+ result[" Presolved Constraints" ] = con_stats
5168
+ continue
5169
+
5170
+ relevant_value = relevant_value.split(" " )[0 ]
5171
+ if stat_name == " Problem name" :
5172
+ if " Original" in data[i- 1 ]:
5173
+ result[" Problem name" ] = relevant_value
5174
+ else :
5175
+ result[" Presolved Problem name" ] = relevant_value
5176
+ continue
5177
+
5178
+ if stat_name == " Gap" :
5179
+ result[" Gap (%)" ] = float (relevant_value[:- 1 ])
5180
+ continue
5181
+
5182
+ if _is_number(relevant_value):
5183
+ result[cur_stat] = float (relevant_value)
5184
+ else : # it's a string
5185
+ result[cur_stat] = relevant_value
5186
+
5187
+ # changing keys to pythonic variable names
5188
+ treated_keys = {" Total Time" : " total_time" , " solving" :" solving_time" , " presolving" :" presolving_time" , " reading" :" reading_time" , " copying" :" copying_time" ,
5189
+ " Problem name" : " problem_name" , " Presolved Problem name" : " presolved_problem_name" , " Variables" :" _variables" ,
5190
+ " Presolved Variables" :" _presolved_variables" , " Constraints" : " _constraints" , " Presolved Constraints" :" _presolved_constraints" ,
5191
+ " number of runs" : " n_runs" , " nodes" :" n_nodes" , " Solutions found" : " n_solutions_found" , " First Solution" : " first_solution" ,
5192
+ " Primal Bound" :" primal_bound" , " Dual Bound" :" dual_bound" , " Gap (%)" :" gap" , " primal-dual" :" primal_dual_integral" }
5193
+ treated_result = dict ((treated_keys[key], value) for (key, value) in result.items())
5194
+
5195
+ stats = Statistics(** treated_result)
5196
+ return stats
5105
5197
5106
5198
def getNLPs (self ):
5107
5199
""" gets total number of LPs solved so far"""
@@ -5445,6 +5537,147 @@ cdef class Model:
5445
5537
""" Get an estimation of the final tree size """
5446
5538
return SCIPgetTreesizeEstimation(self ._scip)
5447
5539
5540
+ @dataclass
5541
+ class Statistics :
5542
+ """
5543
+ Attributes
5544
+ ----------
5545
+ total_time : float
5546
+ Total time since model was created
5547
+ solving_time: float
5548
+ Time spent solving the problem
5549
+ presolving_time: float
5550
+ Time spent on presolving
5551
+ reading_time: float
5552
+ Time spent on reading
5553
+ copying_time: float
5554
+ Time spent on copying
5555
+ problem_name: str
5556
+ Name of problem
5557
+ presolved_problem_name: str
5558
+ Name of presolved problem
5559
+ n_nodes: int
5560
+ The number of nodes explored in the branch-and-bound tree
5561
+ n_solutions_found: int
5562
+ number of found solutions
5563
+ first_solution: float
5564
+ objective value of first found solution
5565
+ primal_bound: float
5566
+ The best primal bound found
5567
+ dual_bound: float
5568
+ The best dual bound found
5569
+ gap: float
5570
+ The gap between the primal and dual bounds
5571
+ primal_dual_integral: float
5572
+ The primal-dual integral
5573
+ n_vars: int
5574
+ number of variables in the model
5575
+ n_binary_vars: int
5576
+ number of binary variables in the model
5577
+ n_integer_vars: int
5578
+ number of integer variables in the model
5579
+ n_implicit_integer_vars: int
5580
+ number of implicit integer variables in the model
5581
+ n_continuous_vars: int
5582
+ number of continuous variables in the model
5583
+ n_presolved_vars: int
5584
+ number of variables in the presolved model
5585
+ n_presolved_continuous_vars: int
5586
+ number of continuous variables in the presolved model
5587
+ n_presolved_binary_vars: int
5588
+ number of binary variables in the presolved model
5589
+ n_presolved_integer_vars: int
5590
+ number of integer variables in the presolved model
5591
+ n_presolved_implicit_integer_vars: int
5592
+ number of implicit integer variables in the presolved model
5593
+ n_maximal_cons: int
5594
+ number of maximal constraints in the model
5595
+ n_initial_cons: int
5596
+ number of initial constraints in the presolved model
5597
+ n_presolved_maximal_cons: int
5598
+ number of maximal constraints in the presolved model
5599
+ n_presolved_conss: int
5600
+ number of initial constraints in the model
5601
+ """
5602
+
5603
+ total_time: float
5604
+ solving_time: float
5605
+ presolving_time: float
5606
+ reading_time: float
5607
+ copying_time: float
5608
+ problem_name: str
5609
+ presolved_problem_name: str
5610
+ _variables: dict # Dictionary with number of variables by type
5611
+ _presolved_variables: dict # Dictionary with number of presolved variables by type
5612
+ _constraints: dict # Dictionary with number of constraints by type
5613
+ _presolved_constraints: dict # Dictionary with number of presolved constraints by type
5614
+ n_runs: int
5615
+ n_nodes: int
5616
+ n_solutions_found: int
5617
+ first_solution: float
5618
+ primal_bound: float
5619
+ dual_bound: float
5620
+ gap: float
5621
+ primal_dual_integral: float
5622
+
5623
+ # unpacking the _variables, _presolved_variables, _constraints
5624
+ # _presolved_constraints dictionaries
5625
+ @property
5626
+ def n_vars (self ):
5627
+ return self ._variables[" total" ]
5628
+
5629
+ @property
5630
+ def n_binary_vars (self ):
5631
+ return self ._variables[" binary" ]
5632
+
5633
+ @property
5634
+ def n_integer_vars (self ):
5635
+ return self ._variables[" integer" ]
5636
+
5637
+ @property
5638
+ def n_implicit_integer_vars (self ):
5639
+ return self ._variables[" implicit" ]
5640
+
5641
+ @property
5642
+ def n_continuous_vars (self ):
5643
+ return self ._variables[" continuous" ]
5644
+
5645
+ @property
5646
+ def n_presolved_vars (self ):
5647
+ return self ._presolved_variables[" total" ]
5648
+
5649
+ @property
5650
+ def n_presolved_binary_vars (self ):
5651
+ return self ._presolved_variables[" binary" ]
5652
+
5653
+ @property
5654
+ def n_presolved_integer_vars (self ):
5655
+ return self ._presolved_variables[" integer" ]
5656
+
5657
+ @property
5658
+ def n_presolved_implicit_integer_vars (self ):
5659
+ return self ._presolved_variables[" implicit" ]
5660
+
5661
+ @property
5662
+ def n_presolved_continuous_vars (self ):
5663
+ return self ._presolved_variables[" continuous" ]
5664
+
5665
+ @property
5666
+ def n_conss (self ):
5667
+ return self ._constraints[" initial" ]
5668
+
5669
+ @property
5670
+ def n_maximal_cons (self ):
5671
+ return self ._constraints[" maximal" ]
5672
+
5673
+ @property
5674
+ def n_presolved_conss (self ):
5675
+ return self ._presolved_constraints[" initial" ]
5676
+
5677
+ @property
5678
+ def n_presolved_maximal_cons (self ):
5679
+ return self ._presolved_constraints[" maximal" ]
5680
+
5448
5681
# debugging memory management
5449
5682
def is_memory_freed ():
5450
5683
return BMSgetMemoryUsed() == 0
0 commit comments