Skip to content

Commit

Permalink
Merge pull request #89 from chrhansk/feature-solver-refactor
Browse files Browse the repository at this point in the history
Move status / result classes into dedicated files
  • Loading branch information
chrhansk authored May 17, 2024
2 parents 89cd53d + 319e792 commit f2c2107
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 129 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.4.19"
release = "0.4.20"

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
66 changes: 66 additions & 0 deletions pygradflow/result.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import numpy as np

from pygradflow.status import SolverStatus


class SolverResult:
"""
The result of a solution of a :py:class:`pygradflow.problem.Problem`
instance with a :py:class:`pygradflow.solver.Solver`
"""

def __init__(
self,
x: np.ndarray,
y: np.ndarray,
d: np.ndarray,
status: SolverStatus,
iterations: int,
num_accepted_steps: int,
total_time: float,
dist_factor: float,
):
self._x = x
self._y = y
self._d = d
self._status = status
self.iterations = iterations
self.num_accepted_steps = num_accepted_steps
self.total_time = total_time
self.dist_factor = dist_factor

@property
def status(self) -> SolverStatus:
"""
The status of the solve as a :py:class:`pygradflow.solver.SolverStatus`
"""
return self._status

@property
def x(self) -> np.ndarray:
"""
The primal solution :math:`x \\in \\mathbb{R}^{n}`
"""
return self._x

@property
def y(self) -> np.ndarray:
"""
The dual solution :math:`y \\in \\mathbb{R}^{m}`
"""
return self._y

@property
def d(self) -> np.ndarray:
"""
The dual solution :math:`d \\in \\mathbb{R}^{n}`
with respect to the variable bounds
"""
return self._d

def __repr__(self) -> str:
return "SolverResult(status={0})".format(self.status)

@property
def success(self):
return SolverStatus.success(self.status)
2 changes: 2 additions & 0 deletions pygradflow/runners/runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def try_solve_instance(instance, params, log_filename, verbose):
try:
logger.handlers.clear()

np.seterr(divide="raise", over="raise", invalid="raise")

handler = logging.FileHandler(log_filename)
handler.setFormatter(formatter)
logger.addHandler(handler)
Expand Down
129 changes: 2 additions & 127 deletions pygradflow/solver.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import time
from enum import Enum, auto
from typing import Optional, cast

import numpy as np
Expand All @@ -13,7 +12,9 @@
from pygradflow.params import Params
from pygradflow.penalty import penalty_strategy
from pygradflow.problem import Problem
from pygradflow.result import SolverResult
from pygradflow.scale import create_scaling
from pygradflow.status import SolverStatus
from pygradflow.step.step_control import (
StepController,
StepControlResult,
Expand All @@ -22,132 +23,6 @@
from pygradflow.timer import Timer
from pygradflow.transform import Transformation


class SolverStatus(Enum):
Optimal = auto()
"""
The algorithm has converged to a solution satisfying
the optimality conditions according to given tolerances
"""

IterationLimit = auto()
"""
Reached the iteration limit precribed by the algorithmic
parameters
"""

TimeLimit = auto()
"""
Reached the time limit precribed by the algorithmic
parameters
"""

Unbounded = auto()
"""
Problem appearst unbounded (found feasible point with extremely
small objective value)
"""

LocallyInfeasible = auto()
"""
Local infeasibility detected (found infeasible point being
a local minimum with respect to constraint violation)
"""

@staticmethod
def short_name(status):
return {
SolverStatus.Optimal: "optimal",
SolverStatus.IterationLimit: "iteration_limit",
SolverStatus.TimeLimit: "time_limit",
SolverStatus.Unbounded: "unbounded",
SolverStatus.LocallyInfeasible: "infeasible",
}[status]

@staticmethod
def description(status):
return {
SolverStatus.Optimal: "Converged to first-order optimal solution",
SolverStatus.IterationLimit: "Reached iteration limit",
SolverStatus.TimeLimit: "Reached time limit",
SolverStatus.Unbounded: "Problem appears unbounded",
SolverStatus.LocallyInfeasible: "Local infeasibility detected",
}[status]

@staticmethod
def success(status):
"""
Returns
-------
bool
Whether the status indicates a successful solve
"""
return status == SolverStatus.Optimal


class SolverResult:
"""
The result of a solution of a :py:class:`pygradflow.problem.Problem`
instance with a :py:class:`pygradflow.solver.Solver`
"""

def __init__(
self,
x: np.ndarray,
y: np.ndarray,
d: np.ndarray,
status: SolverStatus,
iterations: int,
num_accepted_steps: int,
total_time: float,
dist_factor: float,
):
self._x = x
self._y = y
self._d = d
self._status = status
self.iterations = iterations
self.num_accepted_steps = num_accepted_steps
self.total_time = total_time
self.dist_factor = dist_factor

@property
def status(self) -> SolverStatus:
"""
The status of the solve as a :py:class:`pygradflow.solver.SolverStatus`
"""
return self._status

@property
def x(self) -> np.ndarray:
"""
The primal solution :math:`x \\in \\mathbb{R}^{n}`
"""
return self._x

@property
def y(self) -> np.ndarray:
"""
The dual solution :math:`y \\in \\mathbb{R}^{m}`
"""
return self._y

@property
def d(self) -> np.ndarray:
"""
The dual solution :math:`d \\in \\mathbb{R}^{n}`
with respect to the variable bounds
"""
return self._d

def __repr__(self) -> str:
return "SolverResult(status={0})".format(self.status)

@property
def success(self):
return SolverStatus.success(self.status)


header_interval = 25


Expand Down
63 changes: 63 additions & 0 deletions pygradflow/status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from enum import Enum, auto


class SolverStatus(Enum):
Optimal = auto()
"""
The algorithm has converged to a solution satisfying
the optimality conditions according to given tolerances
"""

IterationLimit = auto()
"""
Reached the iteration limit precribed by the algorithmic
parameters
"""

TimeLimit = auto()
"""
Reached the time limit precribed by the algorithmic
parameters
"""

Unbounded = auto()
"""
Problem appearst unbounded (found feasible point with extremely
small objective value)
"""

LocallyInfeasible = auto()
"""
Local infeasibility detected (found infeasible point being
a local minimum with respect to constraint violation)
"""

@staticmethod
def short_name(status):
return {
SolverStatus.Optimal: "optimal",
SolverStatus.IterationLimit: "iteration_limit",
SolverStatus.TimeLimit: "time_limit",
SolverStatus.Unbounded: "unbounded",
SolverStatus.LocallyInfeasible: "infeasible",
}[status]

@staticmethod
def description(status):
return {
SolverStatus.Optimal: "Converged to first-order optimal solution",
SolverStatus.IterationLimit: "Reached iteration limit",
SolverStatus.TimeLimit: "Reached time limit",
SolverStatus.Unbounded: "Problem appears unbounded",
SolverStatus.LocallyInfeasible: "Local infeasibility detected",
}[status]

@staticmethod
def success(status):
"""
Returns
-------
bool
Whether the status indicates a successful solve
"""
return status == SolverStatus.Optimal
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.4.19"
version = "0.4.20"
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 f2c2107

Please sign in to comment.