Search code examples
pythonpandasmodelpyomolinear-optimization

I don't have the right outcome in my pyomo model


Task: Larry Edison is the director of the Computer Center for Buckly College. He now needs to schedule the staffing of the center. It is open from 8 AM until midnight. Larry has monitored the usage of the center at various times of the day, and determined that the following number of computer consultants are required: Shift Time of Day Minimum Number of Consultants A 8 AM - noon 4 B Noon - 4 PM 8 C 4 PM - 8 PM 10 D 8 PM - midnight 6

Two types of computer consultants can be hired: full-time and part-time. The full-time consultants work for 8 consecutive hours in any of the following shifts: morning (8 AM - 4 PM), afternoon (noon - 8 PM), and evening (4 PM - midnight). Full-time consultants are paid €40 per hour. Part-time consultants can be hired to work any of the four shifts listed in the table above. Part-time consultants are paid €30 per hour. An additional requirement is that during every time period, there must be at least two full-time consultants on duty for every part-time consultant on duty.

Larry would like to determine how many full-time and how many part-time workers should work each shift to meet the above requirements at the minimum possible cost. Formulate a linear programming model with indexed variables, and solve it using Pyomo. Make sure that the solution can only take integer values, since it is not possible to hire a worker for only part of a shift.

Write a function:

named model_larry_edison, with no arguments returning a pyomo model The model should have: one objective, named wage, minimizing the amount of wage that has to be paid. two variables: full_time with three indices, one for each full time shift, indicating how many full-time workers are on duty during that shift. part_time with four indices, one for each part time shift, indicating how many part-time workers are on duty during that shift. eight constraints four (A_total, B_total, C_total, D_total) making sure the number of required consultants is satisfied for each 4-hour shift. four (A_ratio, B_ratio, C_ratio, D_ratio) making sure that at each 4-hour shift, the number of full time consultants is at least twice the number of part time consultants.

def model_larry_edison():

  salary_df_ft = pd.DataFrame()
  salary_df_ft.at["FT1", "Salary"] = 40
  salary_df_ft.at["FT2", "Salary"] = 40
  salary_df_ft.at["FT3", "Salary"] = 40

  salary_df_pt = pd.DataFrame()
  salary_df_pt.at["PT1", "Salary"] = 30
  salary_df_pt.at["PT2", "Salary"] = 30
  salary_df_pt.at["PT3", "Salary"] = 30
  salary_df_pt.at["PT4", "Salary"] = 30

  salary_ft = salary_df_ft.astype(int)
  salary_pt = salary_df_pt.astype(int)

  model = pyo.ConcreteModel('ProductionPlan')

  model.salary_ft = pyo.Set(initialize = salary_ft.index)
  model.salary_pt = pyo.Set(initialize = salary_pt.index)

  model.full_time = pyo.Var(model.salary_ft, bounds=(0,None))
  model.part_time = pyo.Var(model.salary_pt, bounds=(0,None))

  model.wage = pyo.Objective(expr = 320*(model.full_time["FT1"]+model.full_time["FT2"]+model.full_time["FT3"]) + 120*(model.part_time["PT1"]+model.part_time["PT2"]+model.part_time["PT3"]+model.part_time["PT4"]), sense=pyo.minimize)

  model.A_total = pyo.Constraint(expr = (model.full_time["FT1"] + model.part_time["PT1"]) >= 4)
  model.B_total = pyo.Constraint(expr = (model.full_time["FT1"] + model.full_time["FT2"] + model.part_time["PT2"]) >= 8)
  model.C_total = pyo.Constraint(expr = (model.full_time["FT2"] + model.full_time["FT3"] + model.part_time["PT3"]) >= 10)
  model.D_total = pyo.Constraint(expr = (model.full_time["FT3"] + model.part_time["PT4"]) >= 6)
  model.A_ratio = pyo.Constraint(expr = model.full_time["FT1"] >= 2*model.part_time["PT1"])
  model.B_ratio = pyo.Constraint(expr = (model.full_time["FT1"] + model.full_time["FT2"]) >= 2*model.part_time["PT2"])
  model.C_ratio = pyo.Constraint(expr = (model.full_time["FT2"] + model.full_time["FT3"]) >= 2*model.part_time["PT3"])
  model.D_ratio = pyo.Constraint(expr = model.full_time["FT3"] >= 2*model.part_time["PT4"])

  return model
model = model_larry_edison()
results = pyo.SolverFactory('cbc').solve(model)
print(model.wage())

The expected outcome is 4160, however I get 4106.6666666


Solution

  • You are on the right track.

    You should ask yourself: "How am I getting fractional values for the objective...?

    The answer is that you didn't constrain the variables to be integer!

    Try changing your variable declarations to:

      model.full_time = pyo.Var(model.salary_ft, domain=pyo.NonNegativeIntegers) #bounds=(0,None))
      model.part_time = pyo.Var(model.salary_pt, domain=pyo.NonNegativeIntegers) #bounds=(0,None))
    

    Also (and before/after you do that): Get in the habit of printing both the model (with variables) and the answer (with values plugged in). It is superbly valuable for troubleshooting:

    model.pprint()   # the model
    model.display()  # the model with the values plugged in