Production planning model (with non-linearities)#

This tutorial extends the Production planning model by illustrating how to handle a non-linearity in problems expressions explicitly. It is designed to be read after the base tutorial and focuses exclusively on the differences with respect to it: only the modified or new elements are described in detail, while unchanged steps are referenced to the base tutorial.

This short note highlights the minimal changes required when the base production planning model becomes non-linear but can still be solved by a non-linear solver without structural reformulation. If reformulation into coupled convex sub-problems is required, see the Production planning model (convex problems decomposition) tutorial.

Use this guide when the non-linearity is supported by a nonlinear-capable solver available to CVXlab and no decomposition is necessary. Refer to CVXPY Disciplined Non-Linear Programming (DNLP) documentation for understanding capabilities and supported solvers.

Problem statement

The production system is the same as in the base tutorial, with one key modification: the unit profit of each product is no longer a fixed constant but increases with the production level of that product (e.g., due to economies of scale):

\[c_j(x_j) = c_{0,j} + c_{s,j} \cdot x_j, \qquad j \in \{product_1,\, product_2\}\]

where \(c_{0,j}\) is the initial unit profit and \(c_{s,j}\) is the profit slope with respect to production. Numerical values are:

\[\begin{split}\begin{array}{c|cc} & product_1 & product_2 \\ \hline c_0 \; [\text{€/u}] & 1.0 & 2.2 \\ c_s \; [\text{€/u}^2] & 0 & 0.005 \\ e \; [\text{kWh/u}] & 1.7 & 3.5 \\ m \; [\text{kg/u}] & 0.5 & 1.2 \end{array}\end{split}\]

Positive slopes \(c_{s,j} > 0\) represent economies of scale: the more a product is sold, the higher its unit margin becomes (in the example, product 2 only exhibits this behavior). A linear relationship between profit and production level is assumed, with \(c_{0,j}\) representing the unit profit at zero production and \(c_{s,j}\) the increase in unit profit per additional unit produced. Finally, the energy and material endowments remain:

\(E = (250, 300)\) kWh, \(M = (60, 90)\) kg.

In the zip directory, the same materials as in the base tutorial are provided (notebook, concept workbook, and model directory), updated for the non-linear variant.

Conceptual model definition#

Related user guide step: Conceptual model definition.

This section focuses mainly on the changes with respect to the base tutorial.

Defining Sets

The Attributes set is updated to accommodate two separate profit-related parameters. The coordinate profit is replaced by profit_initial and profit_slope, bringing the cardinality from 3 to 4.

Sets of the tutorial model (modified entries in bold)#

Set name

Symbol

Coordinates

Cardinality

Set type

Products

\(p\)

\(product_1\), \(product_2\)

2

Dimension set

Attributes (modified)

\(a\)

\(profit\_initial\),\(profit\_slope\),\(energy\),\(material\)

4

Dimension set

Scenarios_energy

\(e\)

\(low\), \(high\)

2

Inter-problem set

Scenarios_material

\(m\)

\(low\), \(high\)

2

Inter-problem set

Defining Data Tables and related Variables

Main changes in data tables definition include: the introduction of a new endogenous profit table and the products_data domain grows by one attribute row, while all other tables are unchanged.

Data Tables of the tutorial model (modified/new entries in bold)#

Name (sets domain)

Type

Domain [cardinality]

Description

production

Endogenous

\(p \times e \times m \; [2 \times 2 \times 2 = 8]\)

Total products output by scenario

profit (new)

Endogenous

\(p \times e \times m \; [2 \times 2 \times 2 = 8]\)

Unit profit by production level and scenario

products_data (modified)

Exogenous

\(a \times p \; [4 \times 2 = 8]\)

Product attributes: initial profit, profit slope, energy use, material use

\(energy_{avail}(e)\)

Exogenous

\(e \; [2]\)

Energy availability scenarios (kWh)

\(material_{avail}(m)\)

Exogenous

\(m \; [2]\)

Material availability scenarios (kg)

Variable \(c\) migrates from products_data (exogenous, constant across scenarios) to the new profit table (endogenous, scenario-dependent). Two new exogenous variables, \(c_0\) and \(c_s\), replace it in products_data.

Variables of the tutorial model (modified/new entries in bold)#

Source Data Table

Symbol

Shape [rows, cols]

Intra-problem sets

Inter-problem sets

Description

profit (new)

\(c\)

\(1,\, p \; [1,2]\)

\(-\)

\(e \times m \; [4]\)

Unit profit per product (€/unit)

products_data (new)

\(c_0\)

\(1,\, p \; [1,2]\)

\(-\)

\(-\)

Initial unit profit per product (€/unit)

products_data (new)

\(c_s\)

\(1,\, p \; [1,2]\)

\(-\)

\(-\)

Profit slope per unit of production (€/unit²)

production

\(x\)

\(1,\, p \; [1,2]\)

\(-\)

\(e \times m \; [4]\)

Products output (unit)

products_data

\(e\)

\(1,\, p \; [1,2]\)

\(-\)

\(-\)

Specific energy use per product (kWh/unit)

products_data

\(m\)

\(1,\, p \; [1,2]\)

\(-\)

\(-\)

Specific material use per product (kg/unit)

\(energy_{avail}\)

\(E\)

\(1,\, 1\)

\(-\)

\(e \; [2]\)

Energy endowment (kWh)

\(material_{avail}\)

\(M\)

\(1,\, 1\)

\(-\)

\(m \; [2]\)

Material endowment (kg)

Defining Problems and related Expressions

The mathematical formulation of the problem is defined below.

\[\begin{split}\begin{aligned} \max \quad & (c \cdot x') \\ \text{s.t.} \quad & e \cdot x' - E \leq 0 \\ & m \cdot x' - M \leq 0 \\ & c - c_s \cdot \mathrm{diag}(x) - c_0 = 0 \\ & x \geq 0 \end{aligned}\end{split}\]

Unit profit \(c_j\) becomes a function of the production level \(x_j\): since unit profit and production level are multiplied in the objective function, the latter becomes non-linear (non-convex).

Notice that:

  • The diag() built-in operator constructs a diagonal matrix from a row vector, so that \(c_s \cdot \mathrm{diag}(x)\) evaluates to the element-wise product \(c_s \odot x\) (see Symbolic operators).

Generation of model directory#

Related user guide step: Generation of model directory

This step is identical to the base tutorial. Refer to the corresponding section of Production planning model for details.

Fill model setup file(s)#

Related user guide step: Fill model setup file(s)

Only the modified or new parts of the setup files are shown below.

Modified sets definition

The only change in structure_sets.yml (or the structure_sets tab) is the updated filter list of the Attributes set.

File: structure_sets.yml

Attributes:
    description: attributes of the products | dimension set
    filters:
        type: [profit_initial, profit_slope, energy, material]

All other set definitions are unchanged.

Modified and new Data Tables and Variables

The full updated structure_variables.yml is shown below. The two hybrid tables and the new variables are annotated with inline comments.

File: structure_variables.yml

production:
    description: products supply
    type: endogenous
    coordinates: [Products, Scenarios_energy, Scenarios_material]
    variables_info:
        x:
            Products:
                dim: cols

# NEW: hybrid profit data table
profit:
    description: unit profit by production
    type: endogenous
    coordinates: [Products, Scenarios_energy, Scenarios_material]
    variables_info:
        c:
            Products:
                dim: cols

# MODIFIED: variable 'c' removed; 'c_0' and 'c_s' added
products_data:
    description: attributes of products | profits, energy use, material use
    type: exogenous
    coordinates: [Products, Attributes]
    variables_info:
        c_0:
            Products:
                dim: cols
            Attributes:
                dim: rows
                filters: {type: profit_initial}
        c_s:
            Products:
                dim: cols
            Attributes:
                dim: rows
                filters: {type: profit_slope}
        e:
            Products:
                dim: cols
            Attributes:
                dim: rows
                filters: {type: energy}
        m:
            Products:
                dim: cols
            Attributes:
                dim: rows
                filters: {type: material}

energy_avail:
    description: availability scenarios for energy
    type: exogenous
    coordinates: [Scenarios_energy]
    variables_info:
        E:

material_avail:
    description: availability scenarios for material
    type: exogenous
    coordinates: [Scenarios_material]
    variables_info:
        M:

Modified symbolic problem definition

In the problem formulation, an expression is added representing the dependancy of profit on production level, linking \(c\) to \(c_0\), \(c_s\), and \(x\).

File: problem.yml

objective:
  - Maximize(c @ tran(x))

expressions:
  - e @ tran(x) - E <= 0
  - m @ tran(x) - M <= 0
  - c - c_s @ diag(x) - c_0 == 0
  - x >= 0

description:
  - maximization of total profit
  - energy use less than endowment
  - material use less than endowment
  - unit profit as a function of product demand
  - positive products supply

Notice that:

  • Since one unique problem is defined, the problem key could even not be specified.

  • The diag() built-in operator constructs a diagonal matrix from a row vector (see Symbolic operators).

Model class instance generation#

Related user guide step: Model class instance generation
Related class constructor: cvxlab.Model

This step is identical to the base tutorial. Refer to the corresponding section of Production planning model for details.

Fill sets data (model coordinates)#

Related user guide step: Fill sets data (model coordinates)

All set tabs in sets.xlsx are identical to the base tutorial with one exception: the _set_ATTRIBUTES tab now contains four rows instead of three, reflecting the updated filter structure.

sets.xlsx file | tab _set_ATTRIBUTES#

Attributes_Name

Attributes_type

profit_initial

profit_initial

profit_slope

profit_slope

energy

energy

material

material

Initialization of data structures#

This step is identical to the base tutorial. Refer to the corresponding section of Production planning model for details.

The products_data input data file tab will now contain eight rows (four attributes × two products) instead of six, reflecting the new attribute structure. Fill the additional profit_slope rows with the \(c_s\) coefficient values.

Fill exogenous model data#

Related user guide step: Fill exogenous model data

The energy_avail and material_avail tabs are unchanged. The products_data tab now has eight rows due to the additional profit_initial and profit_slope attributes.

input_data.xlsx file | tab products_data#

id

Products_Name

Attributes_Name

values

1

product_1

profit_initial

1.0

2

product_1

profit_slope

0

3

product_1

energy

1.7

4

product_1

material

0.5

5

product_2

profit_initial

2.2

6

product_2

profit_slope

0.005

7

product_2

energy

3.5

8

product_2

material

1.2

Note that the profit data table is endogenous and therefore does not appear in the input data file: its values are computed by CVXlab during the solution process.

Initialization of numerical problem(s)#

This step is identical to the base tutorial. Refer to the corresponding section of Production planning model for details.

CVXlab generates eight CVXPY problem instances in total: four scenario instances for sub-problem production and four for sub-problem profit, corresponding to the Cartesian product of the energy and material sets (\(2 \times 2 = 4\) per sub-problem).

Solution of numerical problem(s)#

Related user guide step: Solution of numerical problem(s)
Related API: run_model()

Because the two sub-problems are coupled (i.e., the output of production feeds into profit and vice versa) they cannot be solved independently. The integrated_problems argument must be set to True to activate the iterative block Gauss–Seidel solver.

model.run_model(
  integrated_problems=False, # could be avoided since it is the default value
  solver='IPOPT', # or any other nonlinear-capable solver available to CVXPY backend
  solver_settings={'nlp': True}, # must be specified for nonlinear problems
)

Notice that:

  • the nlp argument must be set to True in the solver_settings dictionary to activate the non-linear solver mode;

  • a non-linear capable solver must be specified in the solver argument: check CVXPY-compatible solvers for non-linear problems here ;

Export endogenous model data#

Related user guide step: Export endogenous model data

The export step is identical to the base tutorial:

model.load_results_to_database()

After this step, the SQLite database database.db contains solved values in two endogenous tables: production (values of \(x\)) and profit (converged values of \(c\)).

Inspecting the exported results

After export, the SQLite database contains two endogenous tables: production (optimal output levels \(x\)) and profit (converged unit profits \(c\)). Both tables are indexed over the four scenario combinations.