Home > Software engineering >  Define sets and parameters from csv file to be used in pyomo optimization max quantity waste collect
Define sets and parameters from csv file to be used in pyomo optimization max quantity waste collect

Time:03-27

First time Pyomo user here.

I am trying to build an optimization model that will maximize the quantity of waste collected in a waste recycling network consisting of customers i and recycling centres j. (i.e. maximize quantity waste Qij flowing from i to j). Here is the mathematical model:

Mathematical model

I have coded a function in jupyter notebook that reads customer and recycling centre latitude and longitude coordinates from two seperate csv file using the read_csv function. The function called distance_from calculates the haversine distance between coordinates and runs a loop which will parse customers location one by one to distance_from function. This generates a dataframe of 80x16 RowsxColumns. Here is the code for this bit:

#create data file of customers i and recycling centres j
df_cent = pd.read_csv("recycling centres.csv",index_col=[0], header=0)
df_cust = pd.read_csv("customers.csv",index_col=[0], header=0)

# concatenating lat and long to create a consolidated location as accepted by haversine function
df_cent['coordinates'] = list(zip(df_cent.Latitude, df_cent.Longitude))
df_cust['coordinates'] = list(zip(df_cust.Latitude, df_cust.Longitude))

# defining a  function to calculate distance between two locations 
# loc1= location of an existing recycling centre
# loc2= location of customer

def distance_from(loc1,loc2): 
    dist=hs.haversine(loc1,loc2)
    return round(dist,2)

# running a loop which will parse customers location one by one to distance from function 
for _,row in df_cent.iterrows():
    df_cust[row.Name]=df_cust['coordinates'].apply(lambda d:
                                                 distance_from(row.coordinates,d))

My problem is that I haven't performed any optimization in this function. I want to use the data from the csv files to create sets and parameters for my Pyomo model using AbstractModel(). I'm not sure if this method or ConcreteModel() is the best way to do this.

One constraint on the optimization problem determines the maximum distance a customer i will travel to recycling centre j, with this max distance set at b=5km. I need this data to be read from a csv file or data frame, but I'm not sure if this is the best method as I could also perform this check when looping location coordinates to the distance_from function.

The second constraint ensures the total amount of waste Qij delivered to pharmacy j does not exceed capacity at j, which is also read from a csv file.

The binary decision variable Xij=1 if customer i delivers waste to recycling centre j, Xij=0 otherwise

Here is the code I have attempted to write which will define sets to be read from csv file, define objective function and apply constraints. It appears after the code above. I'm not sure how far away this code is from where I need to be. All the code has been written in Jupyter Notebook, but I don't think I cana taach the file here, or the csv datafiles for that matter.

from pyomo.environ import *
import pandas as pd
import haversine as hs

solver = SolverFactory('glpk') #GNU Linear Programming Kit

model = AbstractModel()

#set of customers
model.I = pd.read_csv("customers.csv", index_col="Number")
#set of recycling centres
model.J = pd.read_csv("recycling centres.csv", index_col="Name")
#waste generation quantity unit=kg/month
model.Q = pd.read_csv("customers.csv", index_col="Waste Generation (kg/month)")
#Capacity at recycling centre j unit=kg/month
model.Cj = pd.read_csv("recycling centres.csv", index_col="Capacity (kg/month)")

#Binary decision Variable X=1 if customer i served by recycling centre j, X=0 otherwise
model.X = Var(model.I, model.J, domain=Binary)
#Maximum distance customer i will travel to recycling facility j unit=km
model.b = 5 

# Objective is to maximise waste collected within network
def waste_(model):
    return sum(model.Q[i,j]*model.X[i,j] for i in model.I for j in model.J)
model.waste = Objective(rule=waste_, sense=maximize)

# Distance from i to j constraint
def distance_(model, i, j):
    return sum(model.d[i,j]*model.X[i,j] for i in model.I for j in model.J) <= model.b
model.distance = Constraint(rule=distance_)

#Capacity constraint
def capacity_(model, i, j):
    return sum(model.d[i,j]*model.X[i,j] for i in model.I for j in model.J) <= model.Cj
model.capacity = Constraint(rule=capacity_)


Thanks in advance!

CodePudding user response:

Welcome to the site.

You are off to an "OK" start. Your model has quite a few errors in it.... did you look at the examples in the pyomo documentation?

A few suggestions:

  1. Start with ConcreteModel and manually initialize the data. I think it is easier to do, esp. with python's ability to handle .csv files either manually (as I show below) or with pandas or csv_reader.

  2. Throw out pandas for now. Use it from a separate file if need be to create the .csv files if you are comfortable with that, or just manually write them, or use csv_reader, but don't commingle pandas and pyomo till you get your feet on the ground. Same advice for numpy.

  3. Use "flat file" format for your data, not tabular. It is easier to ingest. So, for example, create your distance table in a csv that has 3 columns as mine does and it is easier to read into a dictionary or, if you go to an AbstractModel it is in an easy format.

  4. Use a small slice of your data and pprint() your model to make sure it makes sense and complies with your math model.

distances.csv (Other data files can be inferred from output)

Bob,Main St.,2.1
Cindy,Main St.,3.4
Bob,3rd Ave.,4.9
Cindy,3rd Ave.,0.5 

Code

# pyomo model for customers and distances
import pyomo.environ as pyo

customers = []
centers = []
distances = {}
with open('customers.csv', 'r') as src:
    for line in src:
        customers.append(line.strip())
with open('centers.csv', 'r') as src:
    for line in src:
        centers.append(line.strip())
with open('distances.csv', 'r') as src:
    for line in src:
        cust, center, dist = line.strip().split(',')
        distances[cust, center] = float(dist)

print(customers)
print(centers)
print(distances)

model = pyo.ConcreteModel()

# SETS
model.customers = pyo.Set(initialize=customers)
model.centers = pyo.Set(initialize=centers)

# PARAMETERS
model.distances = pyo.Param(model.customers, model.centers, initialize=distances)

# check it...
model.pprint()

Output

['Bob', 'Cindy']
['Main St.', '3rd Ave.']
{('Bob', 'Main St.'): 2.1, ('Cindy', 'Main St.'): 3.4, ('Bob', '3rd Ave.'): 4.9, ('Cindy', '3rd Ave.'): 0.5}
3 Set Declarations
    centers : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    2 : {'Main St.', '3rd Ave.'}
    customers : Size=1, Index=None, Ordered=Insertion
        Key  : Dimen : Domain : Size : Members
        None :     1 :    Any :    2 : {'Bob', 'Cindy'}
    distances_index : Size=1, Index=None, Ordered=True
        Key  : Dimen : Domain            : Size : Members
        None :     2 : customers*centers :    4 : {('Bob', 'Main St.'), ('Bob', '3rd Ave.'), ('Cindy', 'Main St.'), ('Cindy', '3rd Ave.')}

1 Param Declarations
    distances : Size=4, Index=distances_index, Domain=Any, Default=None, Mutable=False
        Key                   : Value
          ('Bob', '3rd Ave.') :   4.9
          ('Bob', 'Main St.') :   2.1
        ('Cindy', '3rd Ave.') :   0.5
        ('Cindy', 'Main St.') :   3.4

4 Declarations: customers centers distances_index distances
  • Related