Scipy优化-最小化用于组合问题 – 仅整数解

huangapple go评论106阅读模式
英文:

Scipy Optimize Minimize For Combinatorics - Integer only solutions

问题

我有以下代码,试图将最佳产品(a或b)分配给每个工厂(1、2、3、4或5)。
如何设置约束/边界,以仅使用整数值0或1?
例如,我已设置x0 = [1,0,1,1,0],对于此示例数据的解决方案是[0,0,0,0,0]。
此外,这种方法是否正确解决了这个问题?我还尝试过pulp,但似乎不支持动态排放费用,因为这些费用是按总量计算的,而不是按工厂水平计算的。

  1. import numpy as np
  2. from scipy.optimize import minimize
  3. data = {
  4. '1': (1.2,),
  5. '2': (1.0,),
  6. '3': (1.7,),
  7. '4': (1.8,),
  8. '5': (1.6,)
  9. }
  10. unit_cost_b = 8
  11. limit_b = 100
  12. echarge_b = 0.004
  13. unit_cost_a = 5
  14. limit_a = 60
  15. echarge_a = 0.007
  16. def objective(x):
  17. x_a = [data[str(i+1)][0] for i in range(len(data)) if x[i] == 0]
  18. x_b = [data[str(i+1)][0] for i in range(len(data)) if x[i] == 1]
  19. total_unit_cost = (len(x_a) * unit_cost_a) + (len(x_b) * unit_cost_b)
  20. total_usage_a = np.sum(x_a)
  21. total_usage_b = np.sum(x_b)
  22. emission_cost_a = max(total_usage_a - (len(x_a) * limit_a), 0) * echarge_a * 31
  23. emission_cost_b = max(total_usage_b - (len(x_b) * limit_b), 0) * echarge_b * 31
  24. return total_unit_cost + emission_cost_a + emission_cost_b
  25. bounds = [(0, 1)] * len(data)
  26. eq_cons = {'type': 'eq', 'fun': lambda x: np.sum(x) - 5}
  27. x0 = [1,0,1,1,0]
  28. print(x0)
  29. result = minimize(objective, x0, method='SLSQP', bounds=bounds, constraints=[eq_cons])
英文:

I have the below code that tries to assign the best product (a or b) to each factory (1,2,3,4 or 5).
How do I set constraints/bounds so that it only uses the integer values 0 or 1?
e.g I have set x0 = [1,0,1,1,0] and the solution for this sample data is [0,0,0,0,0]
Also, Is this even this correct approach to solve this problem? I have also tried pulp,
but it does not seem to support the dynamic emission charges, as these are calculated in aggregate not at a factory level.

  1. import numpy as np
  2. from scipy.optimize import minimize
  3. data = {
  4. '1': (1.2,),
  5. '2': (1.0,),
  6. '3': (1.7,),
  7. '4': (1.8,),
  8. '5': (1.6,)
  9. }
  10. unit_cost_b = 8
  11. limit_b = 100
  12. echarge_b = 0.004
  13. unit_cost_a = 5
  14. limit_a = 60
  15. echarge_a = 0.007
  16. def objective(x):
  17. x_a = [data[str(i+1)][0] for i in range(len(data)) if x[i] == 0]
  18. x_b = [data[str(i+1)][0] for i in range(len(data)) if x[i] == 1]
  19. total_unit_cost = (len(x_a) * unit_cost_a) + (len(x_b) * unit_cost_b)
  20. total_usage_a = np.sum(x_a)
  21. total_usage_b = np.sum(x_b)
  22. emission_cost_a = max(total_usage_a - (len(x_a) * limit_a), 0) * echarge_a * 31
  23. emission_cost_b = max(total_usage_b - (len(x_b) * limit_b), 0) * echarge_b * 31
  24. return total_unit_cost + emission_cost_a + emission_cost_b
  25. bounds = [(0, 1)] * len(data)
  26. eq_cons = {'type': 'eq', 'fun': lambda x: np.sum(x) - 5}
  27. x0 = [1,0,1,1,0]
  28. print(x0)
  29. result = minimize(objective, x0, method='SLSQP', bounds=bounds, constraints=[eq_cons])

答案1

得分: 2

以下是翻译好的代码部分:

  1. 这是下面的`pulp`实现*可以*`scipy`中使用`scipy.optimize.MILP`包来完成这个任务但我相当肯定你需要将问题以矩阵格式开发这是可行的但是像`pulp``pyomo`这样的包可以为你完成而且我认为它们更容易上手
  2. 请注意**不能**在问题制定中使用`if``max()`因为这些东西是非线性的当问题交给求解器时变量的值是*未知的*`pulp`大部分工作是制定要交付的问题……
  3. 在你的模型中由于`limit`的值很大排放数据似乎总是为零但也许在更大的背景下或者你可以根据以下进行调整这是有道理的
  4. #### 代码:
  5. ```python
  6. # 工厂选择器
  7. import pulp
  8. data = {
  9. 'f1': (1.2,),
  10. 'f2': (1.0,),
  11. 'f3': (1.7,),
  12. 'f4': (1.8,),
  13. 'f5': (1.6,)
  14. }
  15. # unit_cost_b = 8
  16. # limit_b = 100
  17. # echarge_b = 0.004
  18. # unit_cost_a = 5
  19. # limit_a = 60
  20. # echarge_a = 0.007
  21. unit_cost = {'a': 5, 'b': 8}
  22. limit = {'a': 60, 'b': 100}
  23. echarge = {'a': 0.007, 'b': 0.004}
  24. # 便利项....
  25. factories = list(data.keys())
  26. products = ['a', 'b']
  27. fp = [(f, p) for f in factories for p in products]
  28. ### 问题设置
  29. prob = pulp.LpProblem('factory_assignments', pulp.LpMinimize)
  30. ### 变量
  31. # 如果制造该产品,则make[factory, product] == 1,否则为0...
  32. make = pulp.LpVariable.dicts('make', fp, cat=pulp.LpBinary)
  33. emission = pulp.LpVariable.dicts('emission', products, lowBound=0)
  34. ### 目标
  35. # 一个便捷的表达式...
  36. tot_unit_cost = sum(unit_cost

    * make[f, p] for f, p in fp)

  37. prob += tot_unit_cost + sum(emission

    for p in products)

  38. ### 约束

  39. # 限制排放大于等于表达式。它已经有一个lowBound=0,因此可以处理线性表达式中的max()。

  40. for p in products:

  41. prob += emission

    >= sum(make[f, p] * (data[f][0] - limit

    ) for f in factories) * echarge

    * 31

  42. # 每个工厂必须生产*某物*(只有一个东西)(这在原问题中是暗含的)

  43. for f in factories:

  44. prob += sum(make[f, p] for p in products) == 1

  45. ### 进行质量保证...

  46. print(prob)

  47. ### 解决

  48. soln = prob.solve()

  49. for f, p in fp:

  50. if make[f, p].varValue: # 如果变量为1,则为True

  51. print(f'make {p} in factory {f}')

  52. print(f'tot unit cost: {pulp.value(tot_unit_cost)}')

  53. for p in products:

  54. print(f'emission cost for {p} is: {emission

    .varValue}')

输出(截断):

  1. 结果 - 找到最优解
  2. 目标值:25.00000000
  3. 枚举节点:0
  4. 总迭代次数:0
  5. 时间(CPU秒):0.00
  6. 时间(墙钟秒):0.00
  7. 打印选项已从正常更改为全部
  8. 总时间(CPU秒):0.00(墙钟秒):0.00
  9. 在工厂f1中制造a
  10. 在工厂f2中制造a
  11. 在工厂f3中制造a
  12. 在工厂f4中制造a
  13. 在工厂f5中制造a
  14. tot unit cost: 25.0
  15. a的排放成本为:0.0
  16. b的排放成本为:0.0

[在100毫秒内完成]

  1. <details>
  2. <summary>英文:</summary>
  3. This is a `pulp` implementation below. You *could* do this in `scipy` using the `scipy.optimize.MILP` pkg, but I&#39;m pretty sure you would need to develop the problem in matrix format, which is doable, but packages such as `pulp` or `pyomo` do it for you and I think they are much more approachable.
  4. Realize, you **cannot** use `if` or `max()` in problem formulation as those things are nonlinear and the value of the variables is *not known* when the problem is handed over to a solver. And the bulk of what `pulp` is doing is making the problem to hand over....
  5. On your model, the emissions data is odd and seems to always be zero because of the large values of `limit`, but perhaps it makes sense in larger context or you can adjust from the below.
  6. #### CODE:
  7. # factory picker
  8. import pulp
  9. data = {
  10. &#39;f1&#39;: (1.2,),
  11. &#39;f2&#39;: (1.0,),
  12. &#39;f3&#39;: (1.7,),
  13. &#39;f4&#39;: (1.8,),
  14. &#39;f5&#39;: (1.6,)
  15. }
  16. # unit_cost_b = 8
  17. # limit_b = 100
  18. # echarge_b = 0.004
  19. # unit_cost_a = 5
  20. # limit_a = 60
  21. # echarge_a = 0.007
  22. unit_cost = {&#39;a&#39;: 5, &#39;b&#39;: 8}
  23. limit = {&#39;a&#39;: 60, &#39;b&#39;: 100}
  24. echarge = {&#39;a&#39;: 0.007, &#39;b&#39;: 0.004}
  25. # conveniences....
  26. factories = list(data.keys())
  27. products = [&#39;a&#39;, &#39;b&#39;]
  28. fp = [(f, p) for f in factories for p in products]
  29. ### PROB SETUP
  30. prob = pulp.LpProblem(&#39;factory_assignments&#39;, pulp.LpMinimize)
  31. ### VARS
  32. # make[factory, product] == 1 if making that product, else 0...
  33. make = pulp.LpVariable.dicts(&#39;make&#39;, fp, cat=pulp.LpBinary)
  34. emission = pulp.LpVariable.dicts(&#39;emission&#39;, products, lowBound=0)
  35. ### OBJECTIVE
  36. # a convenience expression...
  37. tot_unit_cost = sum(unit_cost

    * make[f, p] for f, p in fp)

  38. prob += tot_unit_cost + sum(emission

    for p in products)

  39. ### CONSTRAINTS

  40. # constrain the emission to GTE the expression. It already has a lowBound=0, so that

  41. # handles the max() expression in a linear expression

  42. for p in products:

  43. prob += emission

    &gt;= sum(make[f, p] * (data[f][0] - limit

    ) for f in factories) * echarge

    * 31

  44. # each factory must produce *something* (and only 1 thing) (this is implied in the orig problem)

  45. for f in factories:

  46. prob += sum(make[f, p] for p in products) == 1

  47. ### QA it...

  48. print(prob)

  49. ### SOLVE

  50. soln = prob.solve()

  51. for f, p in fp:

  52. if make[f, p].varValue: # True if the variable is 1

  53. print(f&#39;make {p} in factory {f}&#39;)

  54. print(f&#39;tot unit cost: {pulp.value(tot_unit_cost)}&#39;)

  55. for p in products:

  56. print(f&#39;emission cost for {p} is: {emission

    .varValue}&#39;)

  57. #### OUTPUT (truncated):

  58. Result - Optimal solution found

  59. Objective value: 25.00000000

  60. Enumerated nodes: 0

  61. Total iterations: 0

  62. Time (CPU seconds): 0.00

  63. Time (Wallclock seconds): 0.00

  64. Option for printingOptions changed from normal to all

  65. Total time (CPU seconds): 0.00 (Wallclock seconds): 0.00

  66. make a in factory f1

  67. make a in factory f2

  68. make a in factory f3

  69. make a in factory f4

  70. make a in factory f5

  71. tot unit cost: 25.0

  72. emission cost for a is: 0.0

  73. emission cost for b is: 0.0

  74. [Finished in 100ms]

  75. </details>

  76. # 答案2

  77. **得分**: 1

  78. 最简单的方法是使用蛮力法。如果无法将您的目标表达为线性函数,那可能是最佳选择。

  79. 示例:

  80. ```python

  81. dims = 5

  82. ranges = ((slice(0, 2, 1),) * dims)

  83. x = scipy.optimize.brute(objective, ranges, finish=None)

英文:

The simplest approach would be to use brute force. If you can't express your objective as a linear function, that might be your best bet.

Example:

  1. dims = 5
  2. ranges = ((slice(0, 2, 1),) * dims)
  3. x = scipy.optimize.brute(objective, ranges, finish=None)

huangapple
  • 本文由 发表于 2023年6月19日 06:03:54
  • 转载请务必保留本文链接:https://go.coder-hub.com/76502699.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定