【问题标题】:Minimize cost based on purchased volume Pyomo根据购买量最小化成本 Pyomo
【发布时间】:2020-12-19 10:52:10
【问题描述】:

我想找到从供应商处购买商品的最佳解决方案,其中运费取决于从指定供应商处购买商品的成本。我正在使用 Pyomo。到目前为止我的代码是:

model = ConcreteModel(name="(MN_2)")

# products
N = ['prod1', 'prod2', 'prod3']

# suppliers
M = ['A', 'B']

# price
p = {('prod1', 'A'): 10,
     ('prod2', 'A'): 9,
     ('prod3', 'A'): 50,
     ('prod1', 'B'): 16,
     ('prod2', 'B'): 20,
     ('prod3', 'B'): 35}

# user quantity contraint
q_u = {('prod1', 'A'): 2,
     ('prod2', 'A'): 1,
     ('prod3', 'A'): 1,
     ('prod1', 'B'): 1,
     ('prod2', 'B'): 1,
     ('prod3', 'B'): 1}

# seller quantity contraint
q_s = {('prod1', 'A'): 20,
     ('prod2', 'A'): 10,
     ('prod3', 'A'): 10,
     ('prod1', 'B'): 10,
     ('prod2', 'B'): 10,
     ('prod3', 'B'): 10}


# quantity of product n bough in shop m
model.x = Var(N, M, bounds=(0,10))

def obj_rule(model):
    return sum(p[n,m]*model.x[n,m] for n in N for m in M)
model.obj = Objective(rule=obj_rule)

def user_quantity(model, n, m):
    return model.x[n,m] >= q_u[n,m]
model.user_quantity = Constraint(N, M, rule=user_quantity)

def seller_quantity(model, n, m):
    return model.x[n,m] <= q_s[n,m]
model.seller_quantity = Constraint(N, M, rule=seller_quantity)

solver = SolverFactory('glpk')
solver.solve(model)
model.x.pprint()

我正在努力解决的问题是如何包含取决于从给定供应商处购买的商品成本的运费。例如:

对于供应商 A:运费为 =

  • 如果从他们那里购买的产品的成本总和为
  • 0 如果从他们那里购买的产品的成本总和> 100

对于供应商 B:运费为 =

  • 8 如果从他们那里购买的产品的成本总和为
  • 0 如果从他们那里购买的产品的成本总和> 150

【问题讨论】:

  • 您的额外费用似乎各有两个可能的值。从数学上讲,您将如何建模/跟踪总成本是否超过阈值?
  • 我会说例如 A: shipping_cost = {10 for model.x[n,m] * p[n,m]
  • 您不能在整数程序中直接使用该逻辑语句,因为它不是线性函数。您正在寻找的是使用二进制变量的 if-else 约束的实现。 (这里)[math.stackexchange.com/q/2500415] 是对这个概念的一个很好的数学解释。如果成本总和达到阈值,您可以为每个供应商添加一个二进制变量和使变量为 1 的相应约束。然后你可以将该变量乘以成本
  • 为什么假设这应该被表述为线性问题?
  • 对不起,在这种情况下我对线性的使用很松散。我只是说您在评论中对运输成本的定义并没有直接转化为(混合整数)线性编程模型中的实现。但是使用二元变量的约束的 if-else 公式确实如此。

标签: optimization pyomo


【解决方案1】:

您描述的约束是 here 描述的 if-then 条件的实现。奇怪的是,如果您的采购成本小于或等于某个阈值而不是严格小于阈值,则您的条件要求二进制变量为 1。我们可以在不影响遵守条件的阈值上添加一个非常小的数字 (0.0001),并允许我们在严格小于不等式的情况下使用新值。

在您的初始模型中,您可以为每个卖家添加一个新的二进制变量 (model.shipping_bin),并为每个二进制变量添加一个约束,如果成本小于阈值,则强制二进制变量为 1,否则为 0。然后,我们可以将目标函数中的运费乘以这些变量。

# add new binary variables to track costs per supplier
model.shipping_bin = Var(M,within = Binary)
shipping_costs = {'A':10,'B':8}
shipping_thresholds = {'A':100,'B':150} # threshold to meet to not incur shipping

# We need big M values to multiply the binaries to enforce the constraint without constraining the procurement cost incurred.
# We can set to the maximum amount we expect to procure from the seller
# The largest cost you could incur from each seller is
# the price times the max quantity
shipping_big_m = {seller: sum([p[(prod,seller)] * q_s[(prod,seller)] for prod in N])
                  for seller in M}


# add constraints
def shipping_bin_rule(model,seller):
    # Sets shipping binary var to 1 if threshold not met
    # Allows it to be 0 otherwise
    # 790 * (model.shipping_bin['A']) >= 100.0001 + cost of products from seller 'A'
        # if cost of products from 'A' < 100.0001 then binary variable = 1
    # 710 * (model.shipping_bin['B']) >= 150.0001 + cost of products from seller 'B'
        # if cost of products from 'B' < 150.0001 then binary variable = 1
    epsilon = .0001 # to make sure case where cost == threshold is still accounted for
    return(shipping_big_m[seller] * model.shipping_bin[seller] >= shipping_thresholds[seller] + epsilon - sum([p[(product,seller)] * model.x[product,seller]
                                                                             for product in N]))

model.shipping_bin_con = Constraint(M,rule = shipping_bin_rule)

# new objective function adding the shipping cost
def obj_with_shipping_rule(model):
    orig_cost = obj_rule(model) # call the original function, but can combine into one function if desired
    # apply the shipping cost if cost of products is less than threshold (binary is 0)
    shipping_cost = sum([shipping_costs[seller] * model.shipping_bin[seller]
                         for seller in M])
    return(orig_cost + shipping_cost)

# deactivate the original objective to apply the new one
model.obj.deactivate()
model.obj_with_shipping = Objective(rule = obj_with_shipping_rule)


# solve the model with new obj
solver.solve(model)
model.x.pprint() # x values remain unchanged
# x : Size=6, Index=x_index
#     Key            : Lower : Value : Upper : Fixed : Stale : Domain
#     ('prod1', 'A') :     0 :   2.0 :    10 : False : False :  Reals
#     ('prod1', 'B') :     0 :   1.0 :    10 : False : False :  Reals
#     ('prod2', 'A') :     0 :   1.0 :    10 : False : False :  Reals
#     ('prod2', 'B') :     0 :   1.0 :    10 : False : False :  Reals
#     ('prod3', 'A') :     0 :   1.0 :    10 : False : False :  Reals
#     ('prod3', 'B') :     0 :   1.0 :    10 : False : False :  Reals

# cost from A = 2 * 10 + 1 * 9 + 1 * 50 = 79 < 100 so model.shipping_bin['A'] = 1
# cost from B = 1 * 16 + 1 * 20 + 1 * 35 = 71 < 150 so model.shipping_bin['B'] = 1
model.shipping_bin.pprint()
# shipping_bin : Size=2, Index=shipping_bin_index
#     Key : Lower : Value : Upper : Fixed : Stale : Domain
#       A :     0 :   1.0 :     1 : False : False : Binary
#       B :     0 :   1.0 :     1 : False : False : Binary
value(model.obj_with_shipping) # 168 (18 units larger than original because of shipping)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 2020-03-18
    相关资源
    最近更新 更多