Skip to content

Commit bb4e65e

Browse files
Added basic support for logging
1 parent c279053 commit bb4e65e

13 files changed

+136
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,4 @@ MANIFEST
3434

3535
.pytest_cache/
3636
.DS_Store
37+
*.log

examples/min_flow_decomp.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
import networkx as nx
33

44
def main():
5+
# Configure logging
6+
fp.utils.configure_logging(
7+
level=fp.utils.logging.DEBUG,
8+
log_to_console=True,
9+
log_file="log_mfd_example.log",
10+
)
11+
512
# Create a simple graph
613
graph = nx.DiGraph()
714
graph.graph["id"] = "simple_graph"
@@ -17,6 +24,7 @@ def main():
1724
# We create a Minimum Flow Decomposition solver with default settings,
1825
# by specifying that the flow value of each edge is in the attribute `flow` of the edges.
1926
mfd_model = fp.MinFlowDecomp(graph, flow_attr="flow")
27+
fp.utils.logger.info("Created the Minimum Flow Decomposition solver")
2028

2129
# We solve it
2230
mfd_model.solve()

examples/min_path_error_iterative.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
import networkx as nx
33

44
def main():
5+
6+
# Configure logging
7+
fp.utils.configure_logging(
8+
level=fp.utils.logging.DEBUG,
9+
log_to_console=True,
10+
log_file="log_mpe_iterative_example.log",
11+
)
12+
513
# Create a simple graph
614
graph = nx.DiGraph()
715
graph.graph["id"] = "simple_graph"

flowpaths/abstractpathmodeldag.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import flowpaths.stdigraph as stdigraph
22
from flowpaths.utils import safetypathcovers
33
from flowpaths.utils import solverwrapper as sw
4+
import flowpaths.utils as utils
45
import time
56
from abc import ABC, abstractmethod
67

@@ -547,8 +548,11 @@ def solve(self) -> bool:
547548
(either 'kOptimal' (highs) or status code 2 (gurobi)), it sets the solved attribute to True and returns True.
548549
Otherwise, it sets the solved attribute to False and returns False.
549550
"""
551+
utils.logger.info(f"{__name__}: solving...")
552+
550553
# If we already received an external solution, we don't need to solve the model
551554
if self.external_solution_paths is not None:
555+
utils.logger.info(f"{__name__}: no need to solve, we have an external solution.")
552556
self.__is_solved = True
553557
return True
554558

@@ -568,6 +572,7 @@ def solve(self) -> bool:
568572
or self.solver.get_model_status() == 2
569573
):
570574
self.__is_solved = True
575+
utils.logger.info(f"{__name__}: solved successfully. Objective value: {self.get_objective_value()}")
571576
return True
572577

573578
self.__is_solved = False

flowpaths/kflowdecomp.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import flowpaths.utils.graphutils as gu
55
import flowpaths.abstractpathmodeldag as pathmodel
66
import flowpaths.utils.safetyflowdecomp as sfd
7+
import flowpaths.utils as utils
78

89
class kFlowDecomp(pathmodel.AbstractPathModelDAG):
910
"""
@@ -187,6 +188,8 @@ def __init__(
187188
# The given weights optimization
188189
self.__encode_given_weights()
189190

191+
utils.logger.info(f"{__name__}: initialized with graph id = {id(G)}, k = {self.k}")
192+
190193
def __encode_flow_decomposition(self):
191194

192195
# Encodes the flow decomposition constraints for the given graph.

flowpaths/kleastabserrors.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import networkx as nx
22
import flowpaths.stdigraph as stdigraph
33
import flowpaths.abstractpathmodeldag as pathmodel
4+
import flowpaths.utils as utils
45

56

67
class kLeastAbsErrors(pathmodel.AbstractPathModelDAG):
78
"""
89
This class implements the k-LeastAbsoluteErrors, namely it looks for a decomposition of a weighted DAG into
910
k weighted paths (specified by `num_paths`), minimizing the absolute errors on the edges. The error on an edge
10-
is defiened as the absolute value of the difference between the weight of the edge and the sum of the weights of
11+
is defined as the absolute value of the difference between the weight of the edge and the sum of the weights of
1112
the paths that go through it.
1213
"""
1314
def __init__(
@@ -185,6 +186,8 @@ def __init__(
185186
# This method is called from the current class to add the objective function
186187
self.__encode_objective()
187188

189+
utils.logger.info(f"{__name__}: initialized with graph id = {id(G)}, k = {self.k}")
190+
188191
def __encode_leastabserrors_decomposition(self):
189192

190193
# pi vars from https://arxiv.org/pdf/2201.10923 page 14

flowpaths/kminpatherror.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import networkx as nx
22
import flowpaths.stdigraph as stdigraph
33
import flowpaths.abstractpathmodeldag as pathmodel
4+
import flowpaths.utils as utils
45

56

67
class kMinPathError(pathmodel.AbstractPathModelDAG):
@@ -221,6 +222,8 @@ def __init__(
221222
# This method is called from the current class to add the objective function
222223
self.__encode_objective()
223224

225+
utils.logger.info(f"{__name__}: initialized with graph id = {id(G)}, k = {self.k}")
226+
224227
def __encode_minpatherror_decomposition(self):
225228

226229
# path weights

flowpaths/minerrorflow.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import flowpaths.utils.solverwrapper as sw
22
import flowpaths.stdigraph as stdigraph
3+
import flowpaths.utils as utils
34
import networkx as nx
45
from copy import deepcopy
56
import time
@@ -99,7 +100,9 @@ def __init__(
99100

100101
self.__encode_flow()
101102

102-
self.__encode_objective()
103+
self.__encode_objective()
104+
105+
utils.logger.info(f"{__name__}: initialized with graph id = {id(G)}")
103106

104107
def __create_solver(self):
105108

flowpaths/minflowdecomp.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import flowpaths.abstractpathmodeldag as pathmodel
66
import flowpaths.utils.graphutils as gu
77
import flowpaths.mingenset as mgs
8+
import flowpaths.utils as utils
89
import copy
910
import math
1011

@@ -129,6 +130,8 @@ def __init__(
129130
self.__all_subgraph_weights = None
130131
self.__given_weights_model = None
131132

133+
utils.logger.info(f"{__name__}: initialized with graph id = {id(G)}")
134+
132135
def solve(self) -> bool:
133136
"""
134137
Attempts to solve the flow distribution problem using a model with varying number of paths.
@@ -148,7 +151,8 @@ def solve(self) -> bool:
148151
if self.optimization_options.get("optimize_with_given_weights", MinFlowDecomp.optimize_with_given_weights):
149152
self.__solve_with_given_weights()
150153

151-
for i in range(self.get_lowerbound_k(), self.G.number_of_edges()):
154+
for i in range(self.get_lowerbound_k(), self.G.number_of_edges()):
155+
utils.logger.info(f"{__name__}: iteration with k = {i}")
152156
fd_model = None
153157
# Checking if we have already found a solution with the same number of paths
154158
# via the min gen set and given weights approach
@@ -229,6 +233,7 @@ def __solve_with_given_weights(self) -> bool:
229233

230234
if given_weights_kfd_solver.is_solved():
231235
self.__given_weights_model = given_weights_kfd_solver
236+
utils.logger.info(f"{__name__}: found an MFD solution with given weights")
232237

233238
def __get_partition_constraints_for_min_gen_set(
234239
self,
@@ -291,7 +296,7 @@ def __get_partition_constraints_for_min_gen_set(
291296
if limit_num_constraints is not None:
292297
partition_constraints_list = partition_constraints_list[:limit_num_constraints]
293298

294-
print("partition_constraints", partition_constraints_list)
299+
utils.logger.debug(f"{__name__}: partition_constraints = {partition_constraints_list}")
295300

296301
return partition_constraints_list
297302

@@ -324,6 +329,7 @@ def __get_lowerbound_with_min_gen_set(self) -> int:
324329
if mingenset_model.is_solved():
325330
self.__generating_set = mingenset_model.get_solution()
326331
min_gen_set_lowerbound = len(self.__generating_set)
332+
utils.logger.info(f"{__name__}: found a min gen set solution with {min_gen_set_lowerbound} elements ({self.__generating_set})")
327333

328334
self.solve_statistics["min_gen_set_solve_time"] = time.time() - start_time
329335

flowpaths/numpathsoptimization.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import time
22
import flowpaths.abstractpathmodeldag as pathmodel
3+
import flowpaths.utils as utils
34

45
class NumPathsOptimization(pathmodel.AbstractPathModelDAG): # Note that we inherit from AbstractPathModelDAG to be able to use this class to also compute safe paths,
56

@@ -106,6 +107,8 @@ def __init__(
106107
self.__solution = None
107108
self.solve_statistics = None
108109

110+
utils.logger.info(f"{__name__}: created NumPathsOptimization with model_type = {model_type}")
111+
109112
def solve(self) -> bool:
110113
"""
111114
Attempts to solve the optimization problem by iterating over a range of path counts, creating and
@@ -151,29 +154,35 @@ def solve(self) -> bool:
151154

152155
for k in range(max(self.min_num_paths,self.get_lowerbound_k()), self.max_num_paths+1):
153156
# Create the model
157+
utils.logger.info(f"{__name__}: model id = {id(self)}, iteration with k = {k}")
154158
model = self.model_type(**self.kwargs, k=k)
155159
model.solve()
156160
if model.is_solved():
157161
found_feasible = True
162+
current_solution_objective_value = model.get_objective_value()
163+
utils.logger.info(f"{__name__}: model id = {id(self)}, iteration with k = {k}, current_solution_objective_value = {current_solution_objective_value}")
158164
if self.stop_on_first_feasible:
159165
solve_status = NumPathsOptimization.solved_status_name
160166
break
161167
if self.stop_on_delta_abs:
162168
if previous_solution_objective_value is None:
163-
previous_solution_objective_value = model.get_objective_value()
169+
previous_solution_objective_value = current_solution_objective_value
164170
else:
165-
if abs(previous_solution_objective_value - model.get_objective_value()) <= self.stop_on_delta_abs:
171+
if abs(previous_solution_objective_value - current_solution_objective_value) <= self.stop_on_delta_abs:
166172
solve_status = NumPathsOptimization.solved_status_name
167173
break
168174
if self.stop_on_delta_rel:
169175
if previous_solution_objective_value is None:
170-
previous_solution_objective_value = model.get_objective_value()
176+
previous_solution_objective_value = current_solution_objective_value
171177
else:
172-
if abs(previous_solution_objective_value - model.get_objective_value()) / previous_solution_objective_value <= self.stop_on_delta_rel:
178+
if abs(previous_solution_objective_value - current_solution_objective_value) / previous_solution_objective_value <= self.stop_on_delta_rel:
173179
solve_status = NumPathsOptimization.solved_status_name
174180
break
181+
else:
182+
utils.logger.info(f"{__name__}: model id = {id(self)}, iteration with k = {k}, model is not solved")
175183
if time.time() - start_time > self.time_limit:
176184
solve_status = NumPathsOptimization.timeout_status_name
185+
utils.logger.info(f"{__name__}: model id = {id(self)}, iteration with k = {k}, time out")
177186
break
178187

179188
if solve_status != NumPathsOptimization.timeout_status_name:

0 commit comments

Comments
 (0)