快速入门¶
快速入门用三个小例子说明 OptAgent 的三条公开调用路径。完整代码维护在 公开示例仓库的固定提交中。
1. 统一建模与 solve¶
先使用 ModelBuilder 描述变量、约束和目标,再调用通用入口 solve(...)。
examples/quickstart/unified_solve.py
from __future__ import annotations
from optagent import ModelBuilder, solve
def build_model() -> ModelBuilder:
builder = ModelBuilder(metadata={"case": "quickstart_unified_solve"})
choose_a = builder.bool_var(name="choose_a")
choose_b = builder.bool_var(name="choose_b")
builder.constraint(choose_a + choose_b <= 1, name="capacity")
builder.maximize(choose_a * 3 + choose_b * 2, name="profit")
return builder
def main() -> None:
solution = solve(build_model(), time_limit_s=10, seed=7, threads=1, log_level="off")
print({"status": solution.status.value, "objective": solution.objective_values})
if __name__ == "__main__":
main()
完整文件:unified_solve.py
这条路径适合先验证问题表达,也适合目标或约束包含外部函数的模型。它不 自动等价于某个精确后端。
2. 线性模型的 solve 与 solve_optx¶
同一个线性模型可以先通过通用 solve(...) 验证,也可以明确选择 OptX 精确
路径。两条调用的求解语义不同:前者是通用搜索路径,后者要求模型完整落在
OptX 支持的线性 / 混合整数表达范围内。
examples/linear/quickstart_linear_routes.py
from __future__ import annotations
from optagent import ModelBuilder, OptxConfig, solve, solve_optx
def build_model() -> ModelBuilder:
builder = ModelBuilder(metadata={"case": "quickstart_linear_routes"})
worker_a = builder.bool_var(name="worker_a")
worker_b = builder.bool_var(name="worker_b")
builder.constraint(worker_a + worker_b <= 1, name="capacity")
builder.maximize(worker_a * 8 + worker_b * 6, name="profit")
return builder
def main() -> None:
heuristic_solution = solve(build_model(), time_limit_s=10, seed=7, threads=1, log_level="off")
exact_solution = solve_optx(
build_model(),
config=OptxConfig(time_limit_s=10, threads=1),
)
print(
{
"solve": heuristic_solution.objective_values,
"solve_optx": exact_solution.objective_values,
}
)
if __name__ == "__main__":
main()
完整文件:quickstart_linear_routes.py
3. 调度模型的统一求解¶
调度模型也可以直接使用统一的 solve(...) 入口,不需要额外安装第三方求解器。
examples/scheduling/job_shop_small.py
from __future__ import annotations
from pathlib import Path
import sys
from optagent import ModelBuilder, solve
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from _common import print_solution
def build_model() -> tuple[object, dict[str, int]]:
builder = ModelBuilder(metadata={"case": "job_shop_small"})
machine_a = builder.sequence_var(size=2, default=[0, 1], name="machine_a")
machine_b = builder.sequence_var(size=2, default=[0, 1], name="machine_b")
job1_a = builder.interval_var(start=0, length=2, lb_start=0, ub_start=8, lb_length=2, ub_length=2, name="job1_a")
job1_b = builder.interval_var(start=0, length=3, lb_start=0, ub_start=10, lb_length=3, ub_length=3, name="job1_b")
job2_b = builder.interval_var(start=0, length=2, lb_start=0, ub_start=8, lb_length=2, ub_length=2, name="job2_b")
job2_a = builder.interval_var(start=0, length=2, lb_start=0, ub_start=10, lb_length=2, ub_length=2, name="job2_a")
builder.constraint(builder.no_overlap(machine_a, job1_a, job2_a), name="machine_a_capacity")
builder.constraint(builder.no_overlap(machine_b, job1_b, job2_b), name="machine_b_capacity")
builder.constraint(builder.precedence(job1_a, job1_b, lag=0), name="job1_flow")
builder.constraint(builder.precedence(job2_b, job2_a, lag=0), name="job2_flow")
builder.minimize(builder.max(builder.interval_end(job1_b), builder.interval_end(job2_a)), name="makespan")
return builder.freeze(), {"machine_a": machine_a.node_id, "machine_b": machine_b.node_id}
def main() -> None:
program, sequence_ids = build_model()
solution = solve(program, time_limit_s=10.0, seed=7, threads=1, log_level="on")
print_solution("small job shop solved by unified solve", solution, extra={"machine_sequences": sequence_ids})
if __name__ == "__main__":
main()
完整文件:job_shop_small.py
该示例使用 interval_var、no_overlap 和 precedence 表达基础 Job Shop,
最后调用 solve(...)。典型调度问题页会进一步介绍何时使用 SchedulingModel
专用接口。