Почему PulP возвращает отрицательные значения для проблемы смешивания, в то время как lowBound установлен на ноль? - PullRequest
2 голосов
/ 30 октября 2019

Я использую Python PulP для решения проблемы смешивания. Вывод странный, он возвращает отрицательные значения. То, что я пытаюсь


from pulp import *
import pandas as pd 

data = pd.read_csv('feed.csv')

# Get required data from user
Ingredients = ['CORN', 'SBM', 'LIMESTONE', 'SALT', 'BONE_MEAL', 'PALM_OIL']


def cost():
    costs = {}
    for i in Ingredients:
        costs[i] = data.loc[data['feed'] == i, 'cost'].iloc[0]
    return costs

def protein():
    proteinPercent = {}
    for i in Ingredients:
        proteinPercent[i] = data.loc[data['feed'] == i, 'protein'].iloc[0]
    return proteinPercent

def energy():
    energyKcal = {}
    for i in Ingredients:
        energyKcal[i] = data.loc[data['feed'] == i, 'energy'].iloc[0]
    return energyKcal


def calcium():
    cal = {}
    for i in Ingredients:
        cal[i] = data.loc[data['feed'] == i, 'calcium'].iloc[0]
    return cal

def sodium():
    sod = {}
    for i in Ingredients:
        sod[i] = data.loc[data['feed'] == i, 'sodium'].iloc[0]
    return sod

def minimum():
    min = {}
    for i in Ingredients:
        min[i] = data.loc[data['feed'] == i, 'min'].iloc[0]
    return min

def maximum():
    max = {}
    for i in Ingredients:
        max[i] = data.loc[data['feed'] == i, 'max'].iloc[0]
    return max

# Initialize
costs = cost()
proteinPercent = protein()
energyKcal = energy()
cal = calcium()
sod = sodium()
L = minimum()
U = maximum()

# Create the 'prob' variable to contain the problem data
prob = LpProblem("Broiler Ration Formulation", LpMinimize)

# A dictionary called 'ingredient_vars' is created to contain the referenced Variables
ingredient_vars = LpVariable.dicts("Ingr",Ingredients, lowBound = 1, upBound = 60)

for i in Ingredients:
    prob += ingredient_vars[i] >= L[i]
    prob += ingredient_vars[i] <= U[i]


# The objective function is added to 'prob' first
prob += lpSum([costs[i]*ingredient_vars[i] for i in Ingredients]), "Total Cost of Ingredients per can"

# The five constraints are added to 'prob'
prob += lpSum([ingredient_vars[i] for i in Ingredients]) == 100, "Percentages Sum"
prob += lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) >= 19.0, "Min Protein Requirement"
prob += lpSum([proteinPercent[i] * ingredient_vars[i] for i in Ingredients]) <= 21.0, "Max Protein Requirement"
prob += lpSum([energyKcal[i] * ingredient_vars[i] for i in Ingredients]) >= 3100.0, "Min Energy Requirements"
prob += lpSum([energyKcal[i] * ingredient_vars[i] for i in Ingredients]) <= 3300.0, "Max Energy Requirements"
prob += lpSum([cal[i] * ingredient_vars[i] for i in Ingredients]) <= 1.1, "Calcium Requirements"
prob += lpSum([sod[i] * ingredient_vars[i] for i in Ingredients]) <= 0.2, "Sodium Requirements"


# The problem is solved using PuLP's choice of Solver
prob.solve()

# The status of the solution is printed to the screen
print("Status:", LpStatus[prob.status])

# Each of the variables is printed with it's resolved optimum value
for v in prob.variables():
    print (v.name, "=", v.varValue)

# The optimised objective function value is printed to the screen    
print ("Total Cost of Ingredients per feed= ", value(prob.objective))
File feed.csv content
feed    min max cost    energy  protein calcium sodium
BONE_MEAL   0   10  0.7 1600    0   30.32   1
CORN    0   60  0.5 3350    9.4 0.05    0
.
.
.

Некоторые из выходных переменных являются отрицательными:

Status: Infeasible

Ingr_BONE_MEAL = 1.0

Ingr_CORN = 37.862563

Ingr_FISH_MEAL = 1.0

Ingr_LIMESTONE = -0.81506256

Ingr_PALM_OIL = 60.0

Ingr_SALT = -0.0475

Ingr_SBM = 1.0

Total Cost of Ingredients per Feed =  32.988153372

Я искал возможное решение, но не смог найти какое-либо объяснение или, возможно, мой код неверенно не могу понять, что именно пошло не так.

Ответы [ 2 ]

0 голосов
/ 03 ноября 2019

Первым, что вы добавите в свой экземпляр LpProblem, всегда будет целевая функция, как описано в документации PuLP . В вашем коде есть следующий порядок:

for i in Ingredients:
   prob += ingredient_vars[i] >= L[i]
   prob += ingredient_vars[i] <= U[i]


# The objective function is added to 'prob' first
prob += lpSum([costs[i]*ingredient_vars[i] for i in Ingredients]), "Total Cost of Ingredients per can"

Целевая функция вашего экземпляра задачи - ingredient_vars[i] >= L[i], что не имеет смысла.

Правильный способ - добавить часть целевой функции передваши ограничения, например, так:

# The objective function is added to 'prob' first
prob += lpSum([costs[i]*ingredient_vars[i] for i in Ingredients]), "Total Cost of Ingredients per can"

for i in Ingredients:
   prob += ingredient_vars[i] >= L[i]
   prob += ingredient_vars[i] <= U[i]

. Хороший способ отладки вашей линейной программы - это использование метода prob.writeLP('blendingprob'), который записывает файл .lp, который показывает действительную линейную программу

0 голосов
/ 30 октября 2019

Это может произойти, когда у вас есть Infeasible решение, которое у вас есть здесь. Поскольку решение недостижимо, целлюлоза просто выбрасывает решение, при котором некоторые из ограничений могут быть нарушены. Вам необходимо выяснить, какие из ваших ограничений несовместимы с другими, и изменить их. Возможно, вам нужно расширить диапазон калорий, например.

...