Make progress bars optional; other minor fixes

master
Alinson S. Xavier 4 years ago
parent 2fd04eb274
commit 2601ef1f9b

@ -5,6 +5,7 @@
from typing import Any, List, TYPE_CHECKING, Tuple, Dict, Optional from typing import Any, List, TYPE_CHECKING, Tuple, Dict, Optional
import numpy as np import numpy as np
from tqdm.auto import tqdm
from p_tqdm import p_umap from p_tqdm import p_umap
from miplearn.features.sample import Sample from miplearn.features.sample import Sample
@ -186,6 +187,7 @@ class Component:
components: List["Component"], components: List["Component"],
instances: List[Instance], instances: List[Instance],
n_jobs: int = 1, n_jobs: int = 1,
progress: bool = False,
) -> None: ) -> None:
# Part I: Pre-fit # Part I: Pre-fit
@ -203,7 +205,13 @@ class Component:
if n_jobs == 1: if n_jobs == 1:
pre = [_pre_sample_xy(instance) for instance in instances] pre = [_pre_sample_xy(instance) for instance in instances]
else: else:
pre = p_umap(_pre_sample_xy, instances, num_cpus=n_jobs) pre = p_umap(
_pre_sample_xy,
instances,
num_cpus=n_jobs,
desc="pre-sample-xy",
disable=not progress,
)
pre_combined: Dict = {} pre_combined: Dict = {}
for (cidx, comp) in enumerate(components): for (cidx, comp) in enumerate(components):
pre_combined[cidx] = [] pre_combined[cidx] = []
@ -237,8 +245,15 @@ class Component:
if n_jobs == 1: if n_jobs == 1:
xy = [_sample_xy(instance) for instance in instances] xy = [_sample_xy(instance) for instance in instances]
else: else:
xy = p_umap(_sample_xy, instances) xy = p_umap(_sample_xy, instances, desc="sample-xy", disable=not progress)
for (cidx, comp) in enumerate(components):
for (cidx, comp) in enumerate(
tqdm(
components,
desc="fit",
disable=not progress,
)
):
x_comp: Dict = {} x_comp: Dict = {}
y_comp: Dict = {} y_comp: Dict = {}
for (x, y) in xy: for (x, y) in xy:

@ -102,6 +102,8 @@ class DynamicConstraintsComponent(Component):
assert pre is not None assert pre is not None
known_cids: Set = set() known_cids: Set = set()
for cids in pre: for cids in pre:
if cids is None:
continue
known_cids |= set(list(cids)) known_cids |= set(list(cids))
self.known_cids.clear() self.known_cids.clear()
self.known_cids.extend(sorted(known_cids)) self.known_cids.extend(sorted(known_cids))

@ -68,5 +68,7 @@ def setup_logger(
handler.setFormatter(TimeFormatter(start_time, log_colors)) handler.setFormatter(TimeFormatter(start_time, log_colors))
logging.getLogger().addHandler(handler) logging.getLogger().addHandler(handler)
logging.getLogger("miplearn").setLevel(logging.INFO) logging.getLogger("miplearn").setLevel(logging.INFO)
logging.getLogger("gurobipy").setLevel(logging.ERROR)
logging.getLogger("pyomo.core").setLevel(logging.ERROR)
warnings.formatwarning = formatwarning_tb warnings.formatwarning = formatwarning_tb
logging.captureWarnings(True) logging.captureWarnings(True)

@ -7,7 +7,8 @@ import time
import traceback import traceback
from typing import Optional, List, Any, cast, Dict, Tuple from typing import Optional, List, Any, cast, Dict, Tuple
from p_tqdm import p_map from p_tqdm import p_map, p_umap
from tqdm.auto import tqdm
from miplearn.components.component import Component from miplearn.components.component import Component
from miplearn.components.dynamic_lazy import DynamicLazyConstraintsComponent from miplearn.components.dynamic_lazy import DynamicLazyConstraintsComponent
@ -40,7 +41,7 @@ _GLOBAL = [_GlobalVariables()]
def _parallel_solve( def _parallel_solve(
idx: int, idx: int,
) -> Tuple[Optional[LearningSolveStats], Optional[Instance]]: ) -> Tuple[Optional[int], Optional[LearningSolveStats], Optional[Instance]]:
solver = _GLOBAL[0].solver solver = _GLOBAL[0].solver
instances = _GLOBAL[0].instances instances = _GLOBAL[0].instances
discard_outputs = _GLOBAL[0].discard_outputs discard_outputs = _GLOBAL[0].discard_outputs
@ -52,11 +53,11 @@ def _parallel_solve(
discard_output=discard_outputs, discard_output=discard_outputs,
) )
instances[idx].free() instances[idx].free()
return stats, instances[idx] return idx, stats, instances[idx]
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()
logger.exception(f"Exception while solving {instances[idx]}. Ignoring.") logger.exception(f"Exception while solving {instances[idx]}. Ignoring.")
return None, None return None, None, None
class LearningSolver: class LearningSolver:
@ -363,8 +364,9 @@ class LearningSolver:
self, self,
instances: List[Instance], instances: List[Instance],
n_jobs: int = 4, n_jobs: int = 4,
label: str = "Solve", label: str = "solve",
discard_outputs: bool = False, discard_outputs: bool = False,
progress: bool = False,
) -> List[LearningSolveStats]: ) -> List[LearningSolveStats]:
""" """
Solves multiple instances in parallel. Solves multiple instances in parallel.
@ -394,23 +396,31 @@ class LearningSolver:
`[solver.solve(p) for p in instances]` `[solver.solve(p) for p in instances]`
""" """
if n_jobs == 1: if n_jobs == 1:
return [self.solve(p) for p in instances] return [
self.solve(p)
for p in tqdm(
instances,
disable=not progress,
desc=label,
)
]
else: else:
self.internal_solver = None self.internal_solver = None
self._silence_miplearn_logger() self._silence_miplearn_logger()
_GLOBAL[0].solver = self _GLOBAL[0].solver = self
_GLOBAL[0].instances = instances _GLOBAL[0].instances = instances
_GLOBAL[0].discard_outputs = discard_outputs _GLOBAL[0].discard_outputs = discard_outputs
results = p_map( results = p_umap(
_parallel_solve, _parallel_solve,
list(range(len(instances))), list(range(len(instances))),
num_cpus=n_jobs, num_cpus=n_jobs,
desc=label, desc=label,
disable=not progress,
) )
results = [r for r in results if r[0]] results = [r for r in results if r[1]]
stats = [] stats: List[LearningSolveStats] = [{} for _ in range(len(results))]
for (idx, (s, instance)) in enumerate(results): for (idx, s, instance) in results:
stats.append(s) stats[idx] = s
instances[idx] = instance instances[idx] = instance
self._restore_miplearn_logger() self._restore_miplearn_logger()
return stats return stats
@ -419,6 +429,7 @@ class LearningSolver:
self, self,
training_instances: List[Instance], training_instances: List[Instance],
n_jobs: int = 1, n_jobs: int = 1,
progress: bool = False,
) -> None: ) -> None:
if len(training_instances) == 0: if len(training_instances) == 0:
logger.warning("Empty list of training instances provided. Skipping.") logger.warning("Empty list of training instances provided. Skipping.")
@ -427,6 +438,7 @@ class LearningSolver:
list(self.components.values()), list(self.components.values()),
training_instances, training_instances,
n_jobs=n_jobs, n_jobs=n_jobs,
progress=progress,
) )
def _add_component(self, component: Component) -> None: def _add_component(self, component: Component) -> None:

@ -439,14 +439,16 @@ class BasePyomoSolver(InternalSolver):
tee=True, tee=True,
warmstart=self._is_warm_start_available, warmstart=self._is_warm_start_available,
) )
self._termination_condition = results["Solver"][0]["Termination condition"]
total_wallclock_time += results["Solver"][0]["Wallclock time"] total_wallclock_time += results["Solver"][0]["Wallclock time"]
if self.is_infeasible():
break
should_repeat = iteration_cb() should_repeat = iteration_cb()
if not should_repeat: if not should_repeat:
break break
log = streams[0].getvalue() log = streams[0].getvalue()
node_count = self._extract_node_count(log) node_count = self._extract_node_count(log)
ws_value = self._extract_warm_start_value(log) ws_value = self._extract_warm_start_value(log)
self._termination_condition = results["Solver"][0]["Termination condition"]
lb, ub = None, None lb, ub = None, None
self._has_mip_solution = False self._has_mip_solution = False
self._has_lp_solution = False self._has_lp_solution = False

Loading…
Cancel
Save