dirkschumacher / ompr

R package to model Mixed Integer Linear Programs
https://dirkschumacher.github.io/ompr/
Other
264 stars 35 forks source link

Feature Request: adding "macro" functionality to ompr #438

Open sbmack opened 1 year ago

sbmack commented 1 year ago

Reference here is Discussion item: https://github.com/dirkschumacher/ompr/discussions/412#discussion-3853109

Dirk provides an very nice solution for declaring a sparse data structure inside of a MIPModel:

mat4 <- cbind(mat3, cost)
MIPModel() |> 
  add_variable(x[i, j, k], list(i = mat4$plant, j = mat4$prod, k = mat4$ware))

mat4 is the dataframe representation of a sparse data set. The column names of mat[1:3] are the index names in the model. To clarify my idea, convert the index names of the variable x to the the real index names:

mat4 <- cbind(mat3, cost)
MIPModel() |> 
  add_variable(x[plant, prod, ware], list(i = mat4$plant, j = mat4$prod, k = mat4$ware))

The request is to allow the list object in the model to be declared outside the model and then used as a "macro" inside the model.

mat4 <- cbind(mat3, cost)
list_1 <- list(plant = mat4$plant, prod = mat4$prod, ware = mat4$ware)
list_2 <- as.list(mat4)
list_2b <- list_2[1:3]

MIPModel() |> 
  add_variable(x[plant, prod, ware], list_1))

The MIPModel fails when replacing the explicit list object in the model with the externally assigned list_1. Allowing external assignments to internal model structure elements is the request to simplify model syntax. Note that the list_2b assignment derived directly from the dataframe is identical to list_1. So ideally, an externally assigned list object in the model could be created directly from a sparse dataframe and then used inside the model.

Note that my reference paradigm in the is MPL algebraic model manager. A sample model below that demonstrates creating objects external to the model and then using those objects inside the model as a formulation ideal.

  {  Plandb3.mpl  }

TITLE  
    Production_Planning_Database3;

OPTIONS
    DatabaseType=Access
    DatabaseAccess="Plandb3.mdb"
    DatabaseExcel="Plandb3.xls"

INDEX
    product := DATABASE("Products","ProductID");

DATA
    Price[product]     := DATABASE("Products","Price");
    Demand[product]    := DATABASE("Products","Demand");
    ProdCost[product]  := DATABASE("Products","ProdCost");
    ProdRate[product]  := DATABASE("Products","ProdRate");
    ProdDaysAvail      := 22;

VARIABLES
    Produce[product] -> Prod
       EXPORT TO DATABASE("Products","Produce");

MACROS
    TotalRevenue  := SUM(product:  Price * Produce);
    TotalCost     := SUM(product:  ProdCost * Produce);

MODEL

    MAX Profit = TotalRevenue - TotalCost;

SUBJECT TO
    ProdCapacity -> PCap:
        SUM(product: Produce / ProdRate)  <=  ProdDaysAvail;

BOUNDS
    Produce <= Demand; 

END
dirkschumacher commented 10 months ago

Haven't tried it but couldn't you do:

MIPModel() |> 
  add_variable(x[plant, prod, ware], !!!list_1))