Tutorial 9: Working with JSON file formats (Diet problem)

In this tutorial, we aim to achieve two objectives. First, we will explore the use of JSON file formats for reading problem instances and writing results in the context of the elytica service. Secondly, we will establish an efficient data management process that simplifies the integration of the elytica service into other programming stacks.

In the next tutorial, we will dive deeper into the technical details of how to call elytica services from within C# applications, building upon the foundational knowledge established in this tutorial. By the end of both tutorials, you will have a comprehensive understanding of how to leverage the elytica service to optimize a range of real-world problems.

Formulating the Diet Problem

The Diet Problem is a well-known optimization challenge that requires finding the most cost-effective combination of foods to meet specific nutritional requirements. In this tutorial, the classic diet problem is formulated as an integer linear programming problem to accommodate indivisible food portions as part of the diet plan.

Let F be the set of available food types and x_j be the number of portions of food j \in F to be included in the proposed diet. Since the cost of each unit of food c_j is given by EUR/g, the unit portion (g) of each food is given by p_j.

Let N be the set of nutrients with v_{i,j} the nutritional value for a nutrient i \in N provided by a food j \in F. The minimum nutritional requirement for a nutrient i \in N is given by L_i, and the maximum nutritional requirement for a nutrient i \in N is given by U_i.

The resulting integer linear programming formulation of the diet problem is

\begin{array}{c}\begin{array}{cl} \text{Minimize}~ \sum\limits_{j \in F}{c_{j} p_{j} x_{j}}\\ \sum\limits_{j \in F}{v_{i,j} x_{j}} \leq U_{i},\qquad\forall i \in N\\ \sum\limits_{j \in F}{v_{i,j} x_{j}} \geq L_{i},\qquad\forall i \in N\\ \\ \\ \end{array} \\ \end{array}

Reading from a JSON file

For convenience, we have created a JSON file that contains the input data to a diet problem. In the subsequent section of this tutorial, we will show you how the elytica Excel add-in can be used to automatically create a JSON file from an Excel spread sheet for consumption by an optimization model on the elytica platform.

Consider the following input table as an example of the diet problem.

Any proposed diet should satisfy the following nutritional requirements:

The corresponding JSON file that contains the above information may be downloaded from here. After downloading and extracting the file “diet_problem.json”, you have to create a new Interpreter project and upload the JSON input file by clicking on in the editor window of the elytica Interpreter.

The JSON structure of the file is the following:

The content of the first record under the “Food” section is:

The content of the first record under the Nutrients section is:

The following code shows the mathematical formulation of the diet problem as well as the Python code required to read the file diet_problem.json.

model diet_problem
set N = load_nutrients()
set F = load_food()
const U = load_nutrient_max(), forall i in N
const L = load_nutrient_min(), forall i in N
const c = load_food_cost(), forall j in F
const p = load_food_portion(), forall j in F
const v = load_food_nutrients(), forall i in N, forall j in F
int 0 <= x, forall j in F
min sum_{j in F}{ c_{j}*p_{j}*x_{j} }
constr sum_{j in F}{ v_{i,j}*x_{j} } <= U_{i}, forall i in N
constr sum_{j in F}{ v_{i,j}*x_{j} } >= L_{i}, forall i in N
end

import elytica
import json

with open('diet_problem.json', 'r') as f:
  data = json.load(f)
    
def load_nutrients():
  return [id for id, row in enumerate(data["Nutrients"])]

def load_nutrient_max():
  return {id : row["Maximum"] for id, row in enumerate(data["Nutrients"])}

def load_nutrient_min():
  return {id : row["Minimum"] for id, row in enumerate(data["Nutrients"])}

def load_food():
  return [id for id, row in enumerate(data["Food"])]

def load_food_cost():
  return {row: col["Unit Cost (EUR/g)"] for row, col in enumerate(data["Food"])}

def load_food_portion():
  return {row: col["Unit Portion (g)"] for row, col in enumerate(data["Food"])}

def load_food_nutrients():
  nutrient_ids = [id for id, row in enumerate(data["Nutrients"])]
  nutrient_name = {id : row["Name"] for id, row in enumerate(data["Nutrients"])}
  return {i: {row: col[nutrient_name[i]] for row, col in enumerate(data["Food"])} for i in nutrient_ids}

def main():      
  elytica.init_model("diet_problem")
  status = elytica.run_model("diet_problem")      
  if status == "FEASIBLE" or status == "OPTIMAL":
    print("Solution")            
    food_name = {id : row["Name"] for id, row in enumerate(data["Food"])}
    solution = []
    F = elytica.get_model_set("diet_problem", "F")    
    for j in F:
      val = elytica.get_variable_value("diet_problem", f"x{j}")
      if val > 0 :
        row = {}
        row["Food"] = food_name[j]
        row["Portions"] = val
        solution.append(row)                   
    result={'Diet': solution }
    elytica.write_results(json.dumps(result))             
  else:
    print("No feasible solution")  
  return 0

It is worth noting that, since the elytica Interpreter only accommodates integer-valued index sets, the row indices of the JSON records are used to uniquely identify the different food and nutrient objects. Furthermore, the optimization output is formatted as a Python dictionary, which is transformed to a proper JSON structure with the json.dumps() programming statement.

Uploading JSON using the Excel add-in

Instead of manually creating a JSON input file, the elytica Excel add-in can be used to convert an Excel spread sheet to a JSON format. This provides a way to verify the correct use of JSON as input to the elytica optimization model. Download the elytica Excel add-in from here.

Download the file diet_problem.xlsx which contains the following input data to the diet problem.

Login to the elytica service with your access token and select the application and project that you used for creating your diet optimization model. For example, in our case we created our Interpreter model with the following settings:

To upload the two data sheets in “diet_problem.xlsx” as a single JSON file, select “Json upload” instead of the default “Excel upload” option on the elytica add-in:

The transformation of the JSON output file to an Excel sheet is also managed by the elytica add-in to produce the following output sheet: