From f255c70a70df3237945f70b02199a5bda7cdbaec Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Thu, 13 Jun 2024 13:25:30 +0100 Subject: [PATCH 1/9] Add readStatistics --- src/pyscipopt/scip.pxi | 79 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 5b9c1b398..6d58a7956 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -5087,6 +5087,85 @@ cdef class Model: PY_SCIP_CALL(SCIPprintStatistics(self._scip, cfile)) locale.setlocale(locale.LC_NUMERIC,user_locale) + + def readStatistics(self, filename): + """ + Given a .stats file, reads it and returns a dictionary with some statistics. + + Keyword arguments: + filename -- name of the input file + """ + available_stats = ["Total Time", "solving", "presolving", "reading", "copying", + "Problem name", "Variables", "Constraints", "number of runs", + "nodes", "Root LP Estimate", "Solutions found", "First Solution", + "Primal Bound", "Dual Bound", "Gap", "primal-dual"] + + result = {} + file = open(filename) + data = file.readlines() + seen_cons = 0 + for i, line in enumerate(data): + split_line = line.split(":") + split_line[1] = split_line[1][:-1] # removing \n + stat_name = split_line[0].strip() + + if seen_cons == 2 and stat_name == "Constraints": + continue + + if stat_name in available_stats: + cur_stat = split_line[0].strip() + relevant_value = split_line[1].strip() + + if stat_name == "Variables": + relevant_value = relevant_value[:-1] # removing ")" + var_stats = {} + split_var = relevant_value.split("(") + var_stats["total"] = int(split_var[0]) + split_var = split_var[1].split(",") + + for var_type in split_var: + split_result = var_type.strip().split(" ") + var_stats[split_result[1]] = int(split_result[0]) + + if "Original" in data[i-2]: + result["Variables"] = var_stats + else: + result["Presolved Variables"] = var_stats + + continue + + if stat_name == "Constraints": + seen_cons += 1 + con_stats = {} + split_con = relevant_value.split(",") + for con_type in split_con: + split_result = con_type.strip().split(" ") + con_stats[split_result[1]] = int(split_result[0]) + + if "Original" in data[i-3]: + result["Constraints"] = con_stats + else: + result["Presolved Constraints"] = con_stats + continue + + relevant_value = relevant_value.split(" ")[0] + if stat_name == "Problem name": + if "Original" in data[i-1]: + result["Problem name"] = relevant_value + else: + result["Presolved Problem name"] = relevant_value + continue + + if stat_name == "Gap": + result["Gap (%)"] = float(relevant_value[:-1]) + continue + + if _is_number(relevant_value): + result[cur_stat] = float(relevant_value) + else: # it's a string + result[cur_stat] = relevant_value + + return result def getNLPs(self): """gets total number of LPs solved so far""" From 0252cb190393ce9c4729fee8ad17aa55fc9c514c Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Thu, 13 Jun 2024 13:25:37 +0100 Subject: [PATCH 2/9] Add test for readStatistics --- tests/test_reader.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/tests/test_reader.py b/tests/test_reader.py index 2f4712271..dabae6c82 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -78,4 +78,32 @@ def test_sudoku_reader(): input = f.readline() assert input == "sudoku" - deleteFile("model.sod") \ No newline at end of file + deleteFile("model.sod") + +def test_readStatistics(): + m = Model(problemName="readStats") + x = m.addVar(vtype="I") + y = m.addVar() + + m.addCons(x+y <= 3) + m.writeStatistics(os.path.join("tests", "data", "readStatistics.stats")) + + m2 = Model() + result = m2.readStatistics(os.path.join("tests", "data", "readStatistics.stats")) + + assert result["Variables"]["total"] == 2 + assert result["Variables"]["integer"] == 1 + + m.optimize() + m.writeStatistics(os.path.join("tests", "data", "readStatistics.stats")) + result = m2.readStatistics(os.path.join("tests", "data", "readStatistics.stats")) + + assert type(result["Total Time"]) == float + assert result["Problem name"] == "readStats" + assert result["Presolved Problem name"] == "t_readStats" + assert type(result["primal-dual"]) == float + assert result["Solutions found"] == 1 + assert type(result["Gap (%)"]) == float + assert result["Presolved Constraints"] == {"initial": 1, "maximal": 1} + assert result["Variables"] == {"total": 2, "binary": 0, "integer": 1, "implicit": 0, "continuous": 1} + assert result["Presolved Variables"] == {"total": 0, "binary": 0, "integer": 0, "implicit": 0, "continuous": 0} \ No newline at end of file From ef5d30c4e00059c6798b35277f4c4ab4df5153e5 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Thu, 13 Jun 2024 13:26:07 +0100 Subject: [PATCH 3/9] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1508233f..54e5f40c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Added +- Added parser to read .stats file - Added recipe with reformulation for detecting infeasible constraints - Wrapped SCIPcreateOrigSol and added tests - Added verbose option for writeProblem and writeParams From 19430ce7f5600898b316425ba8dc0b184bac45c3 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Tue, 18 Jun 2024 11:08:04 +0100 Subject: [PATCH 4/9] Organize statistics in class --- src/pyscipopt/scip.pxi | 84 +++++++++++++++++++++++++++++++++++++++--- tests/test_reader.py | 30 +++++++-------- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 6d58a7956..1ef271e22 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -17,6 +17,7 @@ from posix.stdio cimport fileno from collections.abc import Iterable from itertools import repeat +from dataclasses import dataclass include "expr.pxi" include "lp.pxi" @@ -5095,14 +5096,16 @@ cdef class Model: Keyword arguments: filename -- name of the input file """ - available_stats = ["Total Time", "solving", "presolving", "reading", "copying", - "Problem name", "Variables", "Constraints", "number of runs", - "nodes", "Root LP Estimate", "Solutions found", "First Solution", - "Primal Bound", "Dual Bound", "Gap", "primal-dual"] - result = {} file = open(filename) data = file.readlines() + + assert "problem is solved" in data[0], "readStatistics can only be called if the problem was solved" + available_stats = ["Total Time", "solving", "presolving", "reading", "copying", + "Problem name", "Variables", "Constraints", "number of runs", + "nodes", "Solutions found", "First Solution", "Primal Bound", + "Dual Bound", "Gap", "primal-dual"] + seen_cons = 0 for i, line in enumerate(data): split_line = line.split(":") @@ -5165,7 +5168,16 @@ cdef class Model: else: # it's a string result[cur_stat] = relevant_value - return result + treated_keys = {"Total Time": "total_time", "solving":"solving_time", "presolving":"presolving_time", "reading":"reading_time", "copying":"copying_time", + "Problem name": "problem_name", "Presolved Problem name": "presolved_problem_name", "Variables":"_variables", + "Presolved Variables":"_presolved_variables", "Constraints": "_constraints", "Presolved Constraints":"_presolved_constraints", + "number of runs": "n_runs", "nodes":"n_nodes", "Solutions found": "solutions_found", "First Solution": "first_solution", + "Primal Bound":"primal_bound", "Dual Bound":"dual_bound", "Gap (%)":"gap", "primal-dual":"primal_dual_integral"} + treated_result = dict((treated_keys[key], value) for (key, value) in result.items()) + stats = Statistics(**treated_result) + stats._populate_remaining() + + return stats def getNLPs(self): """gets total number of LPs solved so far""" @@ -5509,6 +5521,66 @@ cdef class Model: """Get an estimation of the final tree size """ return SCIPgetTreesizeEstimation(self._scip) +@dataclass +class Statistics: + # Total time since model was created + total_time: float + # Time spent solving the problem + solving_time: float + # Time spent on presolving + presolving_time: float + # Time spent on reading + reading_time: float + # Time spent on copying + copying_time: float + # Name of problem + problem_name: str + # Name of presolved problem + presolved_problem_name: str + # Dictionary with number of variables by type + _variables: dict + # Dictionary with number of presolved variables by type + _presolved_variables: dict + # Dictionary with number of constraints by type + _constraints: dict + # Dictionary with number of presolved constraints by type + _presolved_constraints: dict + # The number of restarts it took to solve the problem (TODO: check this) + n_runs: int + # The number of nodes explored in the branch-and-bound tree + n_nodes: int + # number of found solutions + solutions_found: int + # objective value of first found solution + first_solution: float + # The best primal bound found + primal_bound: float + # The best dual bound found + dual_bound: float + # The gap between the primal and dual bounds + gap: float + # The primal-dual integral + primal_dual_integral: float + + def _populate_remaining(self): + self.n_vars: int = self._variables["total"] + self.n_binary_vars: int = self._variables["binary"] + self.n_implicit_integer_vars: int = self._variables["implicit"] + self.n_continuous_vars: int = self._variables["continuous"] + + self.n_presolved_vars: int = self._presolved_variables["total"] + self.n_presolved_binary_vars: int = self._presolved_variables["binary"] + self.n_presolved_implicit_integer_vars: int = self._presolved_variables["implicit"] + self.n_presolved_continuous_vars: int = self._presolved_variables["continuous"] + + self.n_conss: int = self._constraints["initial"] + self.n_maximal_cons: int = self._constraints["maximal"] + + self.n_presolved_conss: int = self._presolved_constraints["initial"] + self.n_presolved_maximal_cons: int = self._presolved_constraints["maximal"] + + + # debugging memory management def is_memory_freed(): return BMSgetMemoryUsed() == 0 diff --git a/tests/test_reader.py b/tests/test_reader.py index dabae6c82..71a04066b 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -87,23 +87,19 @@ def test_readStatistics(): m.addCons(x+y <= 3) m.writeStatistics(os.path.join("tests", "data", "readStatistics.stats")) - - m2 = Model() - result = m2.readStatistics(os.path.join("tests", "data", "readStatistics.stats")) - - assert result["Variables"]["total"] == 2 - assert result["Variables"]["integer"] == 1 + m.hideOutput() m.optimize() m.writeStatistics(os.path.join("tests", "data", "readStatistics.stats")) - result = m2.readStatistics(os.path.join("tests", "data", "readStatistics.stats")) - - assert type(result["Total Time"]) == float - assert result["Problem name"] == "readStats" - assert result["Presolved Problem name"] == "t_readStats" - assert type(result["primal-dual"]) == float - assert result["Solutions found"] == 1 - assert type(result["Gap (%)"]) == float - assert result["Presolved Constraints"] == {"initial": 1, "maximal": 1} - assert result["Variables"] == {"total": 2, "binary": 0, "integer": 1, "implicit": 0, "continuous": 1} - assert result["Presolved Variables"] == {"total": 0, "binary": 0, "integer": 0, "implicit": 0, "continuous": 0} \ No newline at end of file + result = m.readStatistics(os.path.join("tests", "data", "readStatistics.stats")) + + assert len([k for k, val in result.__dict__.items() if not str(hex(id(val))) in str(val)]) == 31 # number of attributes. See https://stackoverflow.com/a/57431390/9700522 + assert type(result.total_time) == float + assert result.problem_name == "readStats" + assert result.presolved_problem_name == "t_readStats" + assert type(result.primal_dual_integral) == float + assert result.solutions_found == 1 + assert type(result.gap) == float + assert result._presolved_constraints == {"initial": 1, "maximal": 1} + assert result._variables == {"total": 2, "binary": 0, "integer": 1, "implicit": 0, "continuous": 1} + assert result._presolved_variables == {"total": 0, "binary": 0, "integer": 0, "implicit": 0, "continuous": 0} \ No newline at end of file From 2e8dd6707038de39a17d47b308cee42d13221dc8 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Tue, 18 Jun 2024 11:14:51 +0100 Subject: [PATCH 5/9] Add some comments --- src/pyscipopt/scip.pxi | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 4ef04f20b..65cca80ba 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -5572,20 +5572,32 @@ class Statistics: primal_dual_integral: float def _populate_remaining(self): + # number of variables in the model self.n_vars: int = self._variables["total"] + # number of binary variables in the model self.n_binary_vars: int = self._variables["binary"] + # number of implicit integer variables in the model self.n_implicit_integer_vars: int = self._variables["implicit"] + # number of continuous variables in the model self.n_continuous_vars: int = self._variables["continuous"] + # number of variables in the presolved model self.n_presolved_vars: int = self._presolved_variables["total"] + # number of binary variables in the presolved model self.n_presolved_binary_vars: int = self._presolved_variables["binary"] + # number of implicit integer variables in the presolved model self.n_presolved_implicit_integer_vars: int = self._presolved_variables["implicit"] + # number of continuous variables in the presolved model self.n_presolved_continuous_vars: int = self._presolved_variables["continuous"] + # number of initial constraints in the model self.n_conss: int = self._constraints["initial"] + # number of maximal constraints in the model self.n_maximal_cons: int = self._constraints["maximal"] + # number of initial constraints in the presolved model self.n_presolved_conss: int = self._presolved_constraints["initial"] + # number of maximal constraints in the presolved model self.n_presolved_maximal_cons: int = self._presolved_constraints["maximal"] From 14a39a927dea089a13a4b20d375eb793636c03d6 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Tue, 18 Jun 2024 11:30:15 +0100 Subject: [PATCH 6/9] Some more comments --- src/pyscipopt/scip.pxi | 11 ++++++----- tests/test_reader.py | 2 -- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 65cca80ba..bf8af3ec9 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -5100,7 +5100,8 @@ cdef class Model: def readStatistics(self, filename): """ - Given a .stats file, reads it and returns a dictionary with some statistics. + Given a .stats file of a solved model, reads it and returns an instance of the Statistics class + holding some statistics. Keyword arguments: filename -- name of the input file @@ -5121,7 +5122,7 @@ cdef class Model: split_line[1] = split_line[1][:-1] # removing \n stat_name = split_line[0].strip() - if seen_cons == 2 and stat_name == "Constraints": + if seen_cons == 2 and stat_name == "Constraints": continue if stat_name in available_stats: @@ -5177,14 +5178,16 @@ cdef class Model: else: # it's a string result[cur_stat] = relevant_value + # changing keys to pythonic variable names treated_keys = {"Total Time": "total_time", "solving":"solving_time", "presolving":"presolving_time", "reading":"reading_time", "copying":"copying_time", "Problem name": "problem_name", "Presolved Problem name": "presolved_problem_name", "Variables":"_variables", "Presolved Variables":"_presolved_variables", "Constraints": "_constraints", "Presolved Constraints":"_presolved_constraints", "number of runs": "n_runs", "nodes":"n_nodes", "Solutions found": "solutions_found", "First Solution": "first_solution", "Primal Bound":"primal_bound", "Dual Bound":"dual_bound", "Gap (%)":"gap", "primal-dual":"primal_dual_integral"} treated_result = dict((treated_keys[key], value) for (key, value) in result.items()) + stats = Statistics(**treated_result) - stats._populate_remaining() + stats._populate_remaining() # retrieve different variable/constraint types from the variable/constraint dictionary return stats @@ -5600,8 +5603,6 @@ class Statistics: # number of maximal constraints in the presolved model self.n_presolved_maximal_cons: int = self._presolved_constraints["maximal"] - - # debugging memory management def is_memory_freed(): return BMSgetMemoryUsed() == 0 diff --git a/tests/test_reader.py b/tests/test_reader.py index 71a04066b..ff1af5c7a 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -86,8 +86,6 @@ def test_readStatistics(): y = m.addVar() m.addCons(x+y <= 3) - m.writeStatistics(os.path.join("tests", "data", "readStatistics.stats")) - m.hideOutput() m.optimize() m.writeStatistics(os.path.join("tests", "data", "readStatistics.stats")) From 044d961d3f653f34d7bdc4f8a3d6c9403e0b1e03 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Fri, 21 Jun 2024 14:09:33 +0100 Subject: [PATCH 7/9] Update Statistics class and documentation --- src/pyscipopt/scip.pxi | 181 +++++++++++++++++++++++++++++------------ tests/test_reader.py | 10 ++- 2 files changed, 136 insertions(+), 55 deletions(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index bf8af3ec9..56a1c01eb 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -5182,13 +5182,11 @@ cdef class Model: treated_keys = {"Total Time": "total_time", "solving":"solving_time", "presolving":"presolving_time", "reading":"reading_time", "copying":"copying_time", "Problem name": "problem_name", "Presolved Problem name": "presolved_problem_name", "Variables":"_variables", "Presolved Variables":"_presolved_variables", "Constraints": "_constraints", "Presolved Constraints":"_presolved_constraints", - "number of runs": "n_runs", "nodes":"n_nodes", "Solutions found": "solutions_found", "First Solution": "first_solution", + "number of runs": "n_runs", "nodes":"n_nodes", "Solutions found": "n_solutions_found", "First Solution": "first_solution", "Primal Bound":"primal_bound", "Dual Bound":"dual_bound", "Gap (%)":"gap", "primal-dual":"primal_dual_integral"} treated_result = dict((treated_keys[key], value) for (key, value) in result.items()) stats = Statistics(**treated_result) - stats._populate_remaining() # retrieve different variable/constraint types from the variable/constraint dictionary - return stats def getNLPs(self): @@ -5535,74 +5533,153 @@ cdef class Model: @dataclass class Statistics: - # Total time since model was created + """ + Attributes + ---------- + total_time : float + Total time since model was created + solving_time: float + Time spent solving the problem + presolving_time: float + Time spent on presolving + reading_time: float + Time spent on reading + copying_time: float + Time spent on copying + problem_name: str + Name of problem + presolved_problem_name: str + Name of presolved problem + _variables: dict + Dictionary with number of variables by type + _presolved_variables: dict + Dictionary with number of presolved variables by type + _constraints: dict + Dictionary with number of constraints by type + _presolved_constraints: dict + Dictionary with number of presolved constraints by type + n_nodes: int + The number of nodes explored in the branch-and-bound tree + n_solutions_found: int + number of found solutions + first_solution: float + objective value of first found solution + primal_bound: float + The best primal bound found + dual_bound: float + The best dual bound found + gap: float + The gap between the primal and dual bounds + primal_dual_integral: float + The primal-dual integral + n_vars: int + number of variables in the model + n_binary_vars: int + number of binary variables in the model + n_integer_vars: int + number of integer variables in the model + n_implicit_integer_vars: int + number of implicit integer variables in the model + n_continuous_vars: int + number of continuous variables in the model + n_presolved_vars: int + number of variables in the presolved model + n_presolved_continuous_vars: int + number of continuous variables in the presolved model + n_presolved_binary_vars: int + number of binary variables in the presolved model + n_presolved_integer_vars: int + number of integer variables in the presolved model + n_presolved_implicit_integer_vars: int + number of implicit integer variables in the presolved model + n_maximal_cons: int + number of maximal constraints in the model + n_initial_cons: int + number of initial constraints in the presolved model + n_presolved_maximal_cons + number of maximal constraints in the presolved model + n_presolved_conss + number of initial constraints in the model + """ + total_time: float - # Time spent solving the problem solving_time: float - # Time spent on presolving presolving_time: float - # Time spent on reading reading_time: float - # Time spent on copying copying_time: float - # Name of problem problem_name: str - # Name of presolved problem presolved_problem_name: str - # Dictionary with number of variables by type _variables: dict - # Dictionary with number of presolved variables by type _presolved_variables: dict - # Dictionary with number of constraints by type _constraints: dict - # Dictionary with number of presolved constraints by type _presolved_constraints: dict - # The number of restarts it took to solve the problem (TODO: check this) n_runs: int - # The number of nodes explored in the branch-and-bound tree n_nodes: int - # number of found solutions - solutions_found: int - # objective value of first found solution + n_solutions_found: int first_solution: float - # The best primal bound found primal_bound: float - # The best dual bound found dual_bound: float - # The gap between the primal and dual bounds gap: float - # The primal-dual integral primal_dual_integral: float - def _populate_remaining(self): - # number of variables in the model - self.n_vars: int = self._variables["total"] - # number of binary variables in the model - self.n_binary_vars: int = self._variables["binary"] - # number of implicit integer variables in the model - self.n_implicit_integer_vars: int = self._variables["implicit"] - # number of continuous variables in the model - self.n_continuous_vars: int = self._variables["continuous"] - - # number of variables in the presolved model - self.n_presolved_vars: int = self._presolved_variables["total"] - # number of binary variables in the presolved model - self.n_presolved_binary_vars: int = self._presolved_variables["binary"] - # number of implicit integer variables in the presolved model - self.n_presolved_implicit_integer_vars: int = self._presolved_variables["implicit"] - # number of continuous variables in the presolved model - self.n_presolved_continuous_vars: int = self._presolved_variables["continuous"] - - # number of initial constraints in the model - self.n_conss: int = self._constraints["initial"] - # number of maximal constraints in the model - self.n_maximal_cons: int = self._constraints["maximal"] - - # number of initial constraints in the presolved model - self.n_presolved_conss: int = self._presolved_constraints["initial"] - # number of maximal constraints in the presolved model - self.n_presolved_maximal_cons: int = self._presolved_constraints["maximal"] - + # unpacking the _variables, _presolved_variables, _constraints + # _presolved_constraints dictionaries + @property + def n_vars(self): + return self._variables["total"] + + @property + def n_binary_vars(self): + return self._variables["binary"] + + @property + def n_integer_vars(self): + return self._variables["integer"] + + @property + def n_implicit_integer_vars(self): + return self._variables["implicit"] + + @property + def n_continuous_vars(self): + return self._variables["continuous"] + + @property + def n_presolved_vars(self): + return self._presolved_variables["total"] + + @property + def n_presolved_binary_vars(self): + return self._presolved_variables["binary"] + + @property + def n_presolved_integer_vars(self): + return self._presolved_variables["integer"] + + @property + def n_presolved_implicit_integer_vars(self): + return self._presolved_variables["implicit"] + + @property + def n_presolved_continuous_vars(self): + return self._presolved_variables["continuous"] + + @property + def n_conss(self): + return self._constraints["initial"] + + @property + def n_maximal_cons(self): + return self._constraints["maximal"] + + @property + def n_presolved_conss(self): + return self._presolved_constraints["initial"] + + @property + def n_presolved_maximal_cons(self): + return self._presolved_constraints["maximal"] + # debugging memory management def is_memory_freed(): return BMSgetMemoryUsed() == 0 diff --git a/tests/test_reader.py b/tests/test_reader.py index ff1af5c7a..4bc976c76 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -91,13 +91,17 @@ def test_readStatistics(): m.writeStatistics(os.path.join("tests", "data", "readStatistics.stats")) result = m.readStatistics(os.path.join("tests", "data", "readStatistics.stats")) - assert len([k for k, val in result.__dict__.items() if not str(hex(id(val))) in str(val)]) == 31 # number of attributes. See https://stackoverflow.com/a/57431390/9700522 + assert len([k for k, val in result.__dict__.items() if not str(hex(id(val))) in str(val)]) == 19 # number of attributes. See https://stackoverflow.com/a/57431390/9700522 assert type(result.total_time) == float assert result.problem_name == "readStats" assert result.presolved_problem_name == "t_readStats" assert type(result.primal_dual_integral) == float - assert result.solutions_found == 1 + assert result.n_solutions_found == 1 assert type(result.gap) == float assert result._presolved_constraints == {"initial": 1, "maximal": 1} assert result._variables == {"total": 2, "binary": 0, "integer": 1, "implicit": 0, "continuous": 1} - assert result._presolved_variables == {"total": 0, "binary": 0, "integer": 0, "implicit": 0, "continuous": 0} \ No newline at end of file + assert result._presolved_variables == {"total": 0, "binary": 0, "integer": 0, "implicit": 0, "continuous": 0} + assert result.n_vars == 2 + assert result.n_presolved_vars == 0 + assert result.n_binary_vars == 0 + assert result.n_integer_vars == 1 \ No newline at end of file From ab94980037fe09ce6630e0ce92ee2c52de767992 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Fri, 21 Jun 2024 14:09:39 +0100 Subject: [PATCH 8/9] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a3105f56..6aafd04e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Added +- Created Statistics class - Added parser to read .stats file - Added SCIPprintExternalCodes (retrieves version of linked symmetry, lp solver, nl solver etc) - Added recipe with reformulation for detecting infeasible constraints From 67c8db5866d6dc00cdbcebdd392487d5c1ddd01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Dion=C3=ADsio?= <57299939+Joao-Dionisio@users.noreply.github.com> Date: Sat, 22 Jun 2024 13:49:56 +0100 Subject: [PATCH 9/9] Update documentation --- src/pyscipopt/scip.pxi | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 507931481..943e27f93 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -5555,15 +5555,7 @@ class Statistics: problem_name: str Name of problem presolved_problem_name: str - Name of presolved problem - _variables: dict - Dictionary with number of variables by type - _presolved_variables: dict - Dictionary with number of presolved variables by type - _constraints: dict - Dictionary with number of constraints by type - _presolved_constraints: dict - Dictionary with number of presolved constraints by type + Name of presolved problem n_nodes: int The number of nodes explored in the branch-and-bound tree n_solutions_found: int @@ -5602,9 +5594,9 @@ class Statistics: number of maximal constraints in the model n_initial_cons: int number of initial constraints in the presolved model - n_presolved_maximal_cons + n_presolved_maximal_cons: int number of maximal constraints in the presolved model - n_presolved_conss + n_presolved_conss: int number of initial constraints in the model """ @@ -5615,10 +5607,10 @@ class Statistics: copying_time: float problem_name: str presolved_problem_name: str - _variables: dict - _presolved_variables: dict - _constraints: dict - _presolved_constraints: dict + _variables: dict # Dictionary with number of variables by type + _presolved_variables: dict # Dictionary with number of presolved variables by type + _constraints: dict # Dictionary with number of constraints by type + _presolved_constraints: dict # Dictionary with number of presolved constraints by type n_runs: int n_nodes: int n_solutions_found: int