设施选址¶
设施选址¶
在固定开设成本和客户服务成本之间做权衡:决定开设哪些设施,并把每个客户 分配给一个已开设设施。开设变量、分配变量、容量和服务半径共同形成模型。
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()