Source code for formulation_bench.problem

import json
from functools import cached_property
from pathlib import Path

from .formulation import Formulation
from .models import Parameter, Solution


[docs] class Problem: """A problem in the FormulationBench dataset. A problem is an optimization problem (e.g., TSP, CWLP) that admits one or more MILP formulations. Each problem is identified by a unique integer ID. Problems are often referred to by ``pN.F`` where ``N`` is the problem ID and ``F`` is a formulation ID (e.g., ``p12.a``). Parameters ---------- path : str or pathlib.Path Path to the directory containing this problem. See :ref:`problem-directory` for the expected directory structure. Attributes ---------- path : pathlib.Path Resolved absolute path to the problem directory. name : str Human-readable problem name. parameters : dict[str, Parameter] Problem data parameters keyed by their names. description : str Natural-language description of the problem. formulations : dict[str, Formulation] MILP formulations associated with this problem, keyed by their formulation ID (e.g., ``"a"``, ``"b"``, etc.). This may include formulations that are unfaithful or otherwise invalid; use the ``valid`` attribute of :class:`Formulation` to filter. data : dict[str, object] or None A single concrete instance of problem data. solution : Solution or None Optimal solution to the provided instance data, if available. metadata : dict[str, object] Free-form metadata about the problem. Typically includes a ``source`` field with details about the origin of the problem and a ``notes`` field with additional commentary. Examples -------- Examine problem :doc:`/problems/p12`:: >>> from formulation_bench import Dataset >>> ds = Dataset("dataset") >>> p12 = ds.problems[12] >>> p12.name 'Traveling Salesman Problem (TSP)' >>> p12.description 'The Traveling Salesman Problem (TSP) aims to find the shortest cycle ...' Get the list of valid/invalid formulations:: >>> [f_id for f_id, f in p12.formulations.items() if f.valid] ['a', 'b', 'c', 'g', 'h', 'i'] >>> [f_id for f_id, f in p12.formulations.items() if not f.valid] ['d', 'e', 'f'] Access the problem data and solution, if available:: >>> p12.data is not None True >>> p12.solution is not None True >>> p12.data["n"] 16 >>> p12.solution.objective 6859 """ def __init__(self, path: str | Path) -> None: self.path = Path(path).resolve() raw = json.loads((self.path / "problem.json").read_text()) self.name: str = raw["name"] self.parameters: dict[str, Parameter] = { k: Parameter.from_dict(v) for k, v in raw["parameters"].items() } self.metadata: dict[str, object] = raw.get("metadata", {}) @cached_property def description(self) -> str: return (self.path / "description.md").read_text() @cached_property def data(self) -> dict[str, object] | None: data_file = self.path / "data.json" return json.loads(data_file.read_text()) if data_file.exists() else None @cached_property def formulations(self) -> dict[str, Formulation]: formulations_dir = self.path / "formulations" if not formulations_dir.exists(): return {} result = {} for d in sorted(formulations_dir.iterdir()): if d.is_dir(): f = Formulation(d, self) result[d.name] = f return result @cached_property def solution(self) -> Solution | None: solution_file = self.path / "solution.json" if not solution_file.exists(): return None raw = json.loads(solution_file.read_text()) return Solution(variables=raw["variables"], objective=raw["objective"]) def __repr__(self) -> str: return f"Problem(path={self.path!r})"