mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Make progress bars optional; other minor fixes
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user