跳转至
OptAgent 1.2.0

设施选址

设施选址

在固定开设成本和客户服务成本之间做权衡:决定开设哪些设施,并把每个客户 分配给一个已开设设施。开设变量、分配变量、容量和服务半径共同形成模型。

facility_location_small.py 使用整数变量表达开设与分配,默认通过统一的 solve(...) 入口求解。

示例默认使用统一的 solve(...) 接口;完整代码中的注释展示了如何切换到 solve_optx(...) 精确求解,同一份 ModelBuilder 模型无需重复编写。

完整代码

examples/linear/facility_location_small.py
from __future__ import annotations

from pathlib import Path
import sys

from optagent import ModelBuilder, solve
# from optagent import OptxConfig, solve_optx

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from _common import print_solution


def build_model() -> tuple[object, dict[str, object]]:
    builder = ModelBuilder(metadata={"case": "facility_location_small"})
    open_cost = {"north": 6, "south": 5}
    service_cost = {
        ("north", "alpha"): 2,
        ("north", "beta"): 4,
        ("north", "gamma"): 5,
        ("south", "alpha"): 3,
        ("south", "beta"): 2,
        ("south", "gamma"): 3,
    }

    open_north = builder.int_var(default=0, lb=0, ub=1, name="open_north")
    open_south = builder.int_var(default=0, lb=0, ub=1, name="open_south")

    assign_north_alpha = builder.int_var(default=0, lb=0, ub=1, name="assign_north_alpha")
    assign_north_beta = builder.int_var(default=0, lb=0, ub=1, name="assign_north_beta")
    assign_north_gamma = builder.int_var(default=0, lb=0, ub=1, name="assign_north_gamma")
    assign_south_alpha = builder.int_var(default=0, lb=0, ub=1, name="assign_south_alpha")
    assign_south_beta = builder.int_var(default=0, lb=0, ub=1, name="assign_south_beta")
    assign_south_gamma = builder.int_var(default=0, lb=0, ub=1, name="assign_south_gamma")

    builder.constraint(assign_north_alpha + assign_south_alpha == 1, name="serve_alpha")
    builder.constraint(assign_north_beta + assign_south_beta == 1, name="serve_beta")
    builder.constraint(assign_north_gamma + assign_south_gamma == 1, name="serve_gamma")

    builder.constraint(assign_north_alpha <= open_north, name="alpha_requires_north")
    builder.constraint(assign_north_beta <= open_north, name="beta_requires_north")
    builder.constraint(assign_north_gamma <= open_north, name="gamma_requires_north")
    builder.constraint(assign_south_alpha <= open_south, name="alpha_requires_south")
    builder.constraint(assign_south_beta <= open_south, name="beta_requires_south")
    builder.constraint(assign_south_gamma <= open_south, name="gamma_requires_south")

    builder.minimize(
        (open_north * open_cost["north"])
        + (open_south * open_cost["south"])
        + (assign_north_alpha * service_cost[("north", "alpha")])
        + (assign_north_beta * service_cost[("north", "beta")])
        + (assign_north_gamma * service_cost[("north", "gamma")])
        + (assign_south_alpha * service_cost[("south", "alpha")])
        + (assign_south_beta * service_cost[("south", "beta")])
        + (assign_south_gamma * service_cost[("south", "gamma")]),
        name="total_cost",
    )
    return builder.freeze(), {
        "open_cost": open_cost,
        "service_cost": {f"{facility}:{customer}": cost for (facility, customer), cost in service_cost.items()},
    }


def main() -> None:
    program, data = build_model()
    solution = solve(program, time_limit_s=10.0, seed=7, threads=1, log_level="on")
    # To use the OptX exact solver instead, replace the line above with:
    # solution = solve_optx(program, config=OptxConfig(time_limit_s=10.0, threads=1))
    print_solution("facility location solved by unified solve", solution, extra=data)


if __name__ == "__main__":
    main()

打开或下载 facility_location_small.py