8.6. Python API

本章节提供MindOpt的Python API手册,内容见下文。

8.6.22. Examples

8.6.22.1. 定制食谱

from mindoptpy import *

req =     {
    # requirement: ( lower bound,   upper bound)
    "Cal"        : (         2000, MDO.INFINITY),
    "Carbo"      : (          350,          375),
    "Protein"    : (           55, MDO.INFINITY),
    "VitA"       : (          100, MDO.INFINITY),
    "VitC"       : (          100, MDO.INFINITY),
    "Calc"       : (          100, MDO.INFINITY),
    "Iron"       : (          100, MDO.INFINITY),
    "Volume"     : (-MDO.INFINITY,           75)
}

food =     {
    # food            : ( lower bound,  upper bound, cost)
    "Cheeseburger"    : (           0, MDO.INFINITY, 1.84),
    "HamSandwich"     : (           0, MDO.INFINITY, 2.19),
    "Hamburger"       : (           0, MDO.INFINITY, 1.84),
    "FishSandwich"    : (           0, MDO.INFINITY, 1.44),
    "ChickenSandwich" : (           0, MDO.INFINITY, 2.29),
    "Fries"           : (           0, MDO.INFINITY, 0.77),
    "SausageBiscuit"  : (           0, MDO.INFINITY, 1.29),
    "LowfatMilk"      : (           0, MDO.INFINITY, 0.60),
    "OrangeJuice"     : (           0, MDO.INFINITY, 0.72)
}

req_value =     {
    # (requirement, food              ) : value
    ( "Cal",        "Cheeseburger"    ) : 510,
    ( "Cal",        "HamSandwich"     ) : 370,
    ( "Cal",        "Hamburger"       ) : 500,
    ( "Cal",        "FishSandwich"    ) : 370,
    ( "Cal",        "ChickenSandwich" ) : 400,
    ( "Cal",        "Fries"           ) : 220,
    ( "Cal",        "SausageBiscuit"  ) : 345,
    ( "Cal",        "LowfatMilk"      ) : 110,
    ( "Cal",        "OrangeJuice"     ) : 80,

    ( "Carbo",      "Cheeseburger"    ) : 34,
    ( "Carbo",      "HamSandwich"     ) : 35,
    ( "Carbo",      "Hamburger"       ) : 42,
    ( "Carbo",      "FishSandwich"    ) : 38,
    ( "Carbo",      "ChickenSandwich" ) : 42,
    ( "Carbo",      "Fries"           ) : 26,
    ( "Carbo",      "SausageBiscuit"  ) : 27,
    ( "Carbo",      "LowfatMilk"      ) : 12,
    ( "Carbo",      "OrangeJuice"     ) : 20,

    ( "Protein",    "Cheeseburger"    ) : 28,
    ( "Protein",    "HamSandwich"     ) : 24,
    ( "Protein",    "Hamburger"       ) : 25,
    ( "Protein",    "FishSandwich"    ) : 14,
    ( "Protein",    "ChickenSandwich" ) : 31,
    ( "Protein",    "Fries"           ) : 3,
    ( "Protein",    "SausageBiscuit"  ) : 15,
    ( "Protein",    "LowfatMilk"      ) : 9,
    ( "Protein",    "OrangeJuice"     ) : 1,

    ( "VitA",       "Cheeseburger"    ) : 15,
    ( "VitA",       "HamSandwich"     ) : 15,
    ( "VitA",       "Hamburger"       ) : 6,
    ( "VitA",       "FishSandwich"    ) : 2,
    ( "VitA",       "ChickenSandwich" ) : 8,
    ( "VitA",       "Fries"           ) : 0,
    ( "VitA",       "SausageBiscuit"  ) : 4,
    ( "VitA",       "LowfatMilk"      ) : 10,
    ( "VitA",       "OrangeJuice"     ) : 2,

    ( "VitC",       "Cheeseburger"    ) : 6,
    ( "VitC",       "HamSandwich"     ) : 10,
    ( "VitC",       "Hamburger"       ) : 2,
    ( "VitC",       "FishSandwich"    ) : 0,
    ( "VitC",       "ChickenSandwich" ) : 15,
    ( "VitC",       "Fries"           ) : 15,
    ( "VitC",       "SausageBiscuit"  ) : 0,
    ( "VitC",       "OrangeJuice"     ) : 4,
    ( "VitC",       "LowfatMilk"      ) : 120,

    ( "Calc",       "Cheeseburger"    ) : 30,
    ( "Calc",       "HamSandwich"     ) : 20,
    ( "Calc",       "Hamburger"       ) : 25,
    ( "Calc",       "FishSandwich"    ) : 15,
    ( "Calc",       "ChickenSandwich" ) : 15,
    ( "Calc",       "Fries"           ) : 0,
    ( "Calc",       "SausageBiscuit"  ) : 20,
    ( "Calc",       "LowfatMilk"      ) : 30,
    ( "Calc",       "OrangeJuice"     ) : 2,

    ( "Iron",       "Cheeseburger"    ) : 20,
    ( "Iron",       "HamSandwich"     ) : 20,
    ( "Iron",       "Hamburger"       ) : 20,
    ( "Iron",       "FishSandwich"    ) : 10,
    ( "Iron",       "ChickenSandwich" ) : 8,
    ( "Iron",       "Fries"           ) : 2,
    ( "Iron",       "SausageBiscuit"  ) : 15,
    ( "Iron",       "LowfatMilk"      ) : 0,
    ( "Iron",       "OrangeJuice"     ) : 2,

    ( "Volume",     "Cheeseburger"    ) : 4,
    ( "Volume",     "HamSandwich"     ) : 7.5,
    ( "Volume",     "Hamburger"       ) : 3.5,
    ( "Volume",     "FishSandwich"    ) : 5,
    ( "Volume",     "ChickenSandwich" ) : 7.3,
    ( "Volume",     "Fries"           ) : 2.6,
    ( "Volume",     "SausageBiscuit"  ) : 4.1,
    ( "Volume",     "LowfatMilk"      ) : 8,
    ( "Volume",     "OrangeJuice"     ) : 12
}

# 建立模型
m = Model()

# 添加决策变量
variable = {}
for food_name, food_data in food.items():
    variable[food_name] = m.addVar(
        lb=food_data[0], ub=food_data[1], vtype=MDO.CONTINUOUS, name=food_name
    )

# 添加约束
# 应满足每日获取的各种营养素在建议的范围内
cons = {}
for req_name, req_data in req.items():
    cons[req_name] = m.addRange(
        quicksum(
            variable[food_name] * req_value[(req_name, food_name)]
            for food_name in food.keys()
        ),
        req_data[0],
        req_data[1],
    )

# 添加目标函数
objective = quicksum(variable[i] * food[i][2] for i in food.keys())
m.setObjective(objective, MDO.MINIMIZE)
m.optimize()

# 打印结果
for i in variable:
    print("Amount of " + i + " intake :" + str(variable[i].X))
print("Total meal cost : " + str(objective.getValue()))
for req_name, req_data in req.items():
    print(
        "Final intake amount of "
        + req_name
        + ": "
        + str(
            quicksum(
                variable[food_name] * req_value[(req_name, food_name)]
                for food_name in food.keys()
            ).getValue()
        )
    )

8.6.22.2. 设施选址

from mindoptpy import *

# 本例子的目标是为了找到最小成本的仓库建造和运输方案

# 有两个商场,商场的位置已经确定,分别是(0, 1.7)和(1.4, 2.9), 所需要的货物重量为100单位和200单位
market_info = tupledict([((0, 1.7), 100), ((1.4, 2.9), 200)])
market_keys = list(market_info.keys())
market_num = len(market_info)

# 仓库位置和建造成本
facilities_info = tupledict(
    [
        ((0, 1),   3),
        ((0, 2),   1),
        ((1, 0), 1.5),
        ((1, 1), 1.3),
        ((1, 2), 1.8),
        ((2, 0), 1.6),
        ((2, 1), 1.1),
        ((2, 2), 1.9),
    ]
)
facilities_keys = list(facilities_info.keys())
facilities_num = len(facilities_info)
transport_fee_per_m = 1.23

# 建立模型
m = Model("Facilities")

# 添加决策变量
# x代表是否在该地建仓库
x = m.addVars(len(facilities_info), vtype=MDO.BINARY)
# y代表从j仓库运向i商场的货物量,值的类型为CONTINUOUS类型,下限为0代表不能从j仓库运送小于0单位的货物到i商场
provide_quantity = [(i, j) for j in range(facilities_num) for i in range(market_num)]
y = m.addVars(provide_quantity, lb=0, vtype=MDO.CONTINUOUS)

# 增加约束
# 约束1 已经决定建造的仓库必须满足所有商场的货物需求
m.addConstrs(
    (
        quicksum(y[(i, j)] for j in range(facilities_num))
        == market_info[market_keys[i]]
        for i in range(market_num)
    ),
    name="is_satisfy",
)
# 约束2 如果不建仓库,则此仓库位置运送给所有商场的货物为0
m.addConstrs(
    (
        y[(i, j)] / market_info[market_keys[i]] <= x[j]
        for i in range(market_num)
        for j in range(facilities_num)
    ),
    name="is_built",
)

# 增加目标函数: 最小化运输费用和建造仓库的费用的总和
# 假设从a地运往b地的运输费用只和距离有关,和货物重量无关
def transportation_fee(pos1, pos2):
    x1 = pos1[0] - pos2[0]
    x2 = pos1[1] - pos2[1]
    return (x1 * x1 + x2 * x2) * transport_fee_per_m

objective1 = quicksum(
    x[j] * facilities_info[facilities_keys[j]] for j in range(facilities_num)
)

objective2 = quicksum(
    x[j] * transportation_fee(market_keys[i], facilities_keys[j])
    for j in range(facilities_num)
    for i in range(market_num)
)

m.setObjective(objective1+objective2, MDO.MINIMIZE)

# 开始优化
m.optimize()

# 打印结果
for i in x:
    if x[i].X:
        print(
            "A warehouse should build at No."
            + str(i + 1)
            + " location at "
            + str(facilities_keys[i])
        )
        for j in range(market_num):
            print(
                "This warehouse transport "
                + str(y[(j, i)].X)
                + " units of goods to "
                + str(j)
                + "supermarkets"
            )

8.6.22.3. 人力分配

from mindoptpy import *

# 每天需要的人力数
day_name, workers_per_day = multidict(
    {
        "Monday":    3,
        "Tuesday":   1,
        "Wednesday": 4,
        "Thursday":  2,
        "Friday":    1,
        "Saturday":  3,
        "Sunday":    3,
    }
)

# 每个工人一天的工资
workers, pay = multidict(
    {
        "Xiaoming": 13,
        "Huahua":   10,
        "HongHong": 11,
        "Dahua":     8,
        "Lihua":     9,
        "Niuniu":   14,
        "Gouzi":    14,
    }
)

# 每个工人可以出勤的时间
availability = tuplelist(
    [
        ("Xiaoming",   "Tuesday"),
        ("Xiaoming", "Wednesday"),
        ("Xiaoming",    "Friday"),
        ("Xiaoming",    "Sunday"),
        ("Huahua",      "Monday"),
        ("Huahua",     "Tuesday"),
        ("Huahua",      "Friday"),
        ("Huahua",    "Saturday"),
        ("HongHong", "Wednesday"),
        ("HongHong",  "Thursday"),
        ("HongHong",    "Friday"),
        ("HongHong",    "Sunday"),
        ("Dahua",      "Tuesday"),
        ("Dahua",    "Wednesday"),
        ("Dahua",       "Friday"),
        ("Dahua",     "Saturday"),
        ("Lihua",       "Monday"),
        ("Lihua",      "Tuesday"),
        ("Lihua",    "Wednesday"),
        ("Lihua",     "Thursday"),
        ("Lihua",       "Friday"),
        ("Lihua",       "Sunday"),
        ("Niuniu",      "Monday"),
        ("Niuniu",     "Tuesday"),
        ("Niuniu",   "Wednesday"),
        ("Niuniu",    "Saturday"),
        ("Gouzi",       "Monday"),
        ("Gouzi",      "Tuesday"),
        ("Gouzi",    "Wednesday"),
        ("Gouzi",       "Friday"),
        ("Gouzi",     "Saturday"),
        ("Gouzi",       "Sunday"),
    ]
)

# 建立模型
m = Model('WorkForce')

# 添加决策变量
# x[(worker, day)]这个变量代表该工人是否在当天工作
# 用(worker, day)来初始化决策变量,可以保证每个工人只在他们允许的天内出勤
x = m.addVars(availability, vtype=MDO.BINARY, name="schedule")


# 增加约束
# 约束: 满足每天的人力需求
c1 = m.addConstrs((x.sum("*", day) == workers_per_day[day] for day in day_name))

# 增加目标函数
objective = quicksum(
    pay[worker_day[0]] * x[(worker_day[0], worker_day[1])]
    for worker_day in availability
)
m.setObjective(objective, MDO.MINIMIZE)

# 开始优化
m.optimize()

# 打印结果
for worker, day in availability:
    if x[(worker, day)].X:
        print(worker + " should work at " + day)

print("The total cost is " + str(objective.getValue()))