Skip to content

Ps bhlp

Bilevel price-setting hub location — Julia Big-M reformulation, solved by gurobipy.

PS_BHLP(n_hubs, alpha, data)

Bases: BaseModel

PS-BHLP solved via Julia Big-M reformulation.

Requires Julia ≥ 1.10 with JuMP, Gurobi, BilevelJuMP, and JSON on PATH.

Source code in src/oracle_paper/models/ps_bhlp.py
def __init__(
    self,
    n_hubs: int,
    alpha: float,
    data: MultiEntityDataset,
) -> None:
    super().__init__(data)
    self._n_hubs = n_hubs
    self._alpha = alpha

    json_payload = self._build_json_payload()

    lp_path: str | None = None
    json_path: str | None = None

    try:
        with tempfile.NamedTemporaryFile(
            mode="w", suffix=".json", delete=False, encoding="utf-8"
        ) as jf:
            json.dump(json_payload, jf)
            json_path = jf.name

        lp_path = json_path.replace(".json", ".lp")

        result = subprocess.run(
            [
                _JULIA_EXE,
                str(self.julia_script),
                json_path,
                lp_path,
            ],
            capture_output=True,
            text=True,
            timeout=600,
        )

        if result.returncode != 0:
            raise RuntimeError(
                f"Julia PS_BHLP model generation failed.\n"
                f"STDERR:\n{result.stderr}\n"
                f"STDOUT:\n{result.stdout}"
            )

        if not os.path.exists(lp_path) or os.path.getsize(lp_path) == 0:
            raise RuntimeError(
                f"Julia exited successfully but no LP file was produced "
                f"at {lp_path}"
            )

        self.model = gp.read(lp_path)

    finally:
        for path in (json_path, lp_path):
            if path and os.path.exists(path):
                try:
                    os.unlink(path)
                except OSError:
                    pass