Skip to content

Commit

Permalink
Merge pull request #37 from chrhansk/feature-runner-time-limit
Browse files Browse the repository at this point in the history
Add strict time limit to runners
  • Loading branch information
chrhansk authored Jan 23, 2024
2 parents 6c90bf8 + 92f6165 commit 4f94a42
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 11 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
project = "pygradflow"
copyright = "2023, Christoph Hansknecht"
author = "Christoph Hansknecht"
release = "0.3.7"
release = "0.3.8"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
50 changes: 41 additions & 9 deletions pygradflow/runners/runner.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import datetime
import enum
import itertools
import logging
import os
from abc import ABC, abstractmethod
from multiprocessing import Pool, TimeoutError, cpu_count
from multiprocessing.pool import ThreadPool

import numpy as np

from pygradflow.log import logger
from pygradflow.params import Params
Expand All @@ -20,11 +25,28 @@ def try_solve_instance(instance, params, log_filename):
logger.handlers.clear()
logger.addHandler(handler)
logger.setLevel(logging.INFO)
return instance.solve(params)

def solve():
return instance.solve(params)

# No time limit
if params.time_limit == np.inf:
return solve()

# Solve in thread pool so we can
# await the result
thread_pool = ThreadPool(1)

try:
res = thread_pool.apply_async(solve)
return res.get(params.time_limit)
except TimeoutError:
logger.error("Reached timeout, aborting")
return "timeout"
except Exception as exc:
logger.error("Error solving %s", instance.name)
logger.exception(exc, exc_info=(type(exc), exc, exc.__traceback__))
return None
return "error"


class Runner(ABC):
Expand Down Expand Up @@ -57,9 +79,6 @@ def log_filename(instance):
return self.output_filename(args, f"{instance.name}.log")

if args.parallel is not None:
import itertools
from multiprocessing import Pool, cpu_count

if args.parallel is True:
num_procs = cpu_count()
else:
Expand All @@ -72,7 +91,7 @@ def log_filename(instance):

solve_args = zip(instances, all_params, all_log_filenames)

with Pool(num_procs) as pool:
with Pool(num_procs, maxtasksperchild=1) as pool:
results = pool.starmap(try_solve_instance, solve_args)

else:
Expand Down Expand Up @@ -138,7 +157,9 @@ def output_filename(self, args, filename):

def main(self):
run_logger.setLevel(logging.INFO)
run_logger.addHandler(logging.StreamHandler())
handler = logging.StreamHandler()
handler.setFormatter(formatter)
run_logger.addHandler(handler)

args = self.parser().parse_args()

Expand Down Expand Up @@ -184,7 +205,18 @@ def write_results(self, args, params, instances, results):
"size": instance.size,
}

if result is None:
if result == "timeout":
writer.writerow(
{
**info,
"status": "timeout",
"total_time": args.time_limit,
"iterations": 0,
"num_accepted_steps": 0,
}
)

elif result == "error":
writer.writerow(
{
**info,
Expand All @@ -198,7 +230,7 @@ def write_results(self, args, params, instances, results):
writer.writerow(
{
**info,
"status": SolverStatus.short_name(result),
"status": SolverStatus.short_name(result.status),
"total_time": result.total_time,
"iterations": result.iterations,
"num_accepted_steps": result.num_accepted_steps,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "pygradflow"
version = "0.3.7"
version = "0.3.8"
description = "PyGradFlow is a simple implementation of the sequential homotopy method to be used to solve general nonlinear programs."
authors = ["Christoph Hansknecht <[email protected]>"]
readme = "README.md"
Expand Down

0 comments on commit 4f94a42

Please sign in to comment.