From c17ba175bac5ef765af8830e2463614f7129fddf Mon Sep 17 00:00:00 2001 From: Christoph Hansknecht Date: Tue, 23 Jan 2024 13:47:43 +0100 Subject: [PATCH 1/3] Add "timeout" result to runner --- pygradflow/runners/runner.py | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/pygradflow/runners/runner.py b/pygradflow/runners/runner.py index 9dd1b68..49b74fe 100644 --- a/pygradflow/runners/runner.py +++ b/pygradflow/runners/runner.py @@ -24,7 +24,7 @@ def try_solve_instance(instance, params, log_filename): 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): @@ -138,7 +138,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() @@ -184,7 +186,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, @@ -198,7 +211,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, From 78344d1b8ed7213e9451294308653689a6e62014 Mon Sep 17 00:00:00 2001 From: Christoph Hansknecht Date: Tue, 23 Jan 2024 14:08:22 +0100 Subject: [PATCH 2/3] Kill processes after reaching respective time limits --- pygradflow/runners/runner.py | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/pygradflow/runners/runner.py b/pygradflow/runners/runner.py index 49b74fe..0859bbb 100644 --- a/pygradflow/runners/runner.py +++ b/pygradflow/runners/runner.py @@ -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 @@ -20,7 +25,24 @@ 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__)) @@ -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: @@ -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: From 92f61654ef56b2a7c6a88eac7a18ccb67cdee3b2 Mon Sep 17 00:00:00 2001 From: Christoph Hansknecht Date: Tue, 23 Jan 2024 14:08:51 +0100 Subject: [PATCH 3/3] Bump version --- docs/conf.py | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 3072fc7..caf329e 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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 diff --git a/pyproject.toml b/pyproject.toml index ea050ec..e6b735f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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 "] readme = "README.md"