Unit Commitment MINLP with Knitro#
Description: Solving a nonlinear Unit Commitment problem with Knitro using MP features for logic and multi-objective optimization. The goal of this notebook is to show a straightforward and clear way of using nonlinear solvers for complex models with logical expressions and also hierarchical multi-objective optimization.
Tags: mp, knitro, mp2nl, nonlinear, quadratic, minlp, unit-commitment, electric-power-industry, energy, multi-objective, gurobi, xpress, mp2nl
Notebook author: Marcos Dominguez Velad <marcos@ampl.com>
# Install dependencies
%pip install -q amplpy pandas numpy
# Google Colab & Kaggle integration
from amplpy import AMPL, ampl_notebook
ampl = ampl_notebook(
modules=["mp2nl", "knitro", "xpress", "gurobi"], # modules to install
license_uuid="default", # license to use
) # instantiate AMPL object and register magics
The problem#
We are solving a version of Unit Commitment with generators with minimum and maximum outputs and ramp limits. There are linear and quadratic costs, so the problem becomes a MINLP, and we aim to minimize CO2 emissions while minimizing production costs, so it is a multi-objective problem.
We will focus on the nonlinear and multi-objective part of the problem for this case. The model is written in AMPL for it to keep easy to read.
Warning: this notebook uses commercial solvers, so when running on Cloud you may need a license for these solvers (Knitro, Gurobi, Xpress), or run locally.
MINLP#
One of the 2 objectives is minimizing total cost, which is a nonlinear expression.
Total cost: $$ \min ; \sum_{g \in \text{GEN}} \sum_{t \in \text{T}} \left( c_g^{\text{lin}} , p_{g,t}
c_g^{\text{quad}} , p_{g,t}^2 \right) ;+; \sum_{g \in \text{GEN}} \sum_{t \in \text{T}} c_g^{\text{start}} , y_{g,t}$$
Where \(p_{g,t}\) is the amount of energy produced by the generator \(g\) in time \(t\), and \(y_{g,t}\) is 1 if the generator started at period \(t\) (startup cost).
There are also some logical constraints to make the model clear. We have some binary variables to model easier if a generator is turned on or has just started:
Producing if and only if commited (binaries \(x_{g,t}\)): $\( p_{g,t} > 0 \;\Longleftrightarrow\; x_{g,t} = 1 \qquad \forall g \in \text{GEN},\; t \in \text{T} \)$
Commited if and only if producing: $\( y_{g,t} \;\Longleftrightarrow\; \big( x_{g,t} = 1 \;\land\; x_{g,t-1} = 0 \big) \qquad \forall g \in \text{GEN} \)$
Amount of energy produced is either 0 or at least over a threshold (minimum input). $\( p_{g,t} = 0 \;\;\text{or}\;\; p_{g,t} \ge \underline{P}_g \qquad \forall g \in \text{GEN},\; t \in \text{T} \)$
Ramp limits:
Solvers don’t necessarily handle logical expressions, but the AMPL/MP interface takes care of reformulating efficiently these expressions.
Multi-objective#
We have 2 objectives. We are going to do hierarchical optimization minimizing first the total cost minimization problem, and later the emissions, allowing a degradation of the total cost of 5%.
This will be handled by suffixes for the objectives, .objpriority and .objreltol.
MINLP Unit Commitment model#
%%writefile unit_commitment.mod
set GENERATORS;
set TIME ordered;
param demand {TIME} >= 0; # Power demand at each time
param min_output {GENERATORS} >= 0; # Minimum power output
param max_output {g in GENERATORS} >= min_output[g]; # Maximum power output
param ramp_up_limit {GENERATORS} >= 0;
param linear_cost {GENERATORS}; # Linear cost coefficient
param quadratic_cost {GENERATORS} >= 0; # Quadratic cost coefficient
param startup_cost {GENERATORS} >= 0; # Startup cost
param emission_rate {GENERATORS} >= 0; # Tons CO2 per MW produced
var is_committed {GENERATORS, TIME} binary; # 1 if generator is ON
var power_generated {gen in GENERATORS, TIME} >= 0 <= max_output[gen]; # MW produced
var is_startup {GENERATORS, TIME} binary; # 1 if generator starts up
# Meet demand in each period
subject to Demand_Satisfaction {t in TIME}:
sum {gen in GENERATORS} power_generated[gen,t] >= demand[t];
# Startup in first period
subject to Startup_First {gen in GENERATORS}:
is_startup[gen, first(TIME)] == is_committed[gen, first(TIME)];
# Startup logic in subsequent periods
subject to Startup_Transition {gen in GENERATORS, t in TIME: ord(t) > 1}:
is_startup[gen,t] <==> (is_committed[gen,t] and !is_committed[gen,prev(t)]);
subject to Min_Gen_If_On {gen in GENERATORS, t in TIME}:
(power_generated[gen,t] <= 0 && is_committed[gen,t] < 0.5)
or (power_generated[gen,t] >= min_output[gen] && is_committed[gen,t] >= 0.5);
# Ramp-up/down limits
subject to Ramp {gen in GENERATORS, t in TIME: ord(t) > 1}:
abs(power_generated[gen,t] - power_generated[gen,prev(t)]) <= ramp_up_limit[gen];
# Objective 1: Minimize total operating + startup cost
minimize Total_Cost:
sum {gen in GENERATORS, t in TIME}
(linear_cost[gen] * power_generated[gen,t] +
quadratic_cost[gen] * power_generated[gen,t]^2)
+ sum {gen in GENERATORS, t in TIME} startup_cost[gen] * is_startup[gen,t];
# Objective 2: Minimize total emissions
minimize Total_Emissions:
sum {gen in GENERATORS, t in TIME} emission_rate[gen] * power_generated[gen,t];
# Multi-objective support
suffix objpriority;
suffix objabstol;
suffix objreltol;
Writing unit_commitment.mod
Solving the model#
Generate the problem data#
The AMPL model is isolated from the input data for more readability and maintainance
import pandas as pd
import numpy as np
# Unit Commitment data
generators = ["G1", "G2", "G3", "G4", "G5", "G6", "G7"]
# Generators data
generators_data = pd.DataFrame(
{
"min_output": [20, 30, 25, 15, 10, 40, 0],
"max_output": [100, 120, 90, 60, 50, 150, 30],
"ramp_up_limit": [40, 50, 30, 25, 20, 60, 10],
"linear_cost": [20, 16, 18, 22, 24, 14, 12],
"quadratic_cost": [0.04, 0.05, 0.06, 0.03, 0.04, 0.036, 0.1],
"startup_cost": [400, 300, 360, 200, 160, 600, 160],
"emission_rate": [0.7, 0.5, 0.6, 0.4, 0.3, 0.8, 0.0],
},
index=generators,
)
# Generate random demand
num_time_periods = 24 * 3
time_periods = list(range(1, num_time_periods + 1))
np.random.seed(42)
base_demand = 150 + 40 * np.sin(np.linspace(0, 3 * np.pi, num_time_periods))
noise = np.random.normal(0, 10, num_time_periods)
demand = (base_demand + noise).clip(min=100).round().astype(int)
Assign the UC data#
# Optimization model and data
ampl = AMPL()
ampl.read("unit_commitment.mod")
ampl.set["TIME"] = time_periods
ampl.set["GENERATORS"] = generators
ampl.param["demand"] = demand
ampl.set_data(generators_data, "GENERATORS")
Running the solver#
For nonlinear solvers like Knitro, we need the mp2nl interface to make it handle logical constraints and multi-objective. See the following example for the knitro solver.
# Hierarchical Multi-objective configuration
# Higher priority first
ampl.eval("let Total_Cost.objpriority := 2;")
# Set 10% degradation tolerance for total cost
ampl.eval("let Total_Cost.objreltol := 0.10;")
# Second objective (less priority)
ampl.eval("let Total_Emissions.objpriority := 1;")
SOLVER = "knitro"
# SOLVER='gurobi'
# SOLVER='xpress'
# Time limit
max_seconds = 30
if SOLVER == "knitro":
ampl.solve(
solver="mp2nl",
knitro_options="numthreads=8 ms_enable=1 maxtime=" + str(max_seconds),
verbose=True,
mp2nl_options="solver=knitro obj:multi=2 outlev=1",
)
else:
ampl.solve(
solver=SOLVER,
mp_options="obj:multi=2 outlev=1 timelim=" + str(max_seconds),
verbose=True,
)
MP2NL 0.1: nl:solver = knitro
obj:multi = 2
tech:outlev = 1
AMPL MP initial flat model has 1512 variables (0 integer, 1008 binary);
Objectives: 2 linear;
Constraints: 1570 linear;
Algebraic expressions: 497 abs; 504 powconstexp;
Logical expressions: 936 conditional (in)equalitie(s); 1433 and; 504 not; 504 or;
==============================================================================
MULTI-OBJECTIVE MODE: starting with 2 objectives (2 combined) ...
==============================================================================
==============================================================================
AMPL MP final model has 6886 variables (1001 integer, 3881 binary);
Objectives: 2 linear;
Constraints: 6871 linear;
Algebraic expressions: 504 powconstexp;
MULTI-OBJECTIVE MODE: objective 1 (out of 2) ...
==============================================================================
Artelys Knitro 15.1.0: numthreads=8
ms_enable=1
maxtime=30
=======================================
Commercial License
Artelys Knitro 15.1.0
=======================================
Knitro changing mip_method from AUTO to 1.
No start point provided -- Knitro computing one.
concurrent_evals 0
datacheck 0
feastol 1e-06
feastol_abs 1e-06
findiff_numthreads 1
hessian_no_f 1
hessopt 1
maxtime 30
ms_enable 1
numthreads 8
opttol 1e-06
opttol_abs 0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 0.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_heuristic_fixpropagate from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.
Problem Characteristics | Presolved
-----------------------
Problem type: convex MIQP
Objective: minimize / quadratic
Number of variables: 6886 | 6886
bounds: lower upper range | lower upper range
0 0 4378 | 0 0 4378
free fixed | free fixed
0 2508 | 0 2508
cont. binary integer | cont. binary integer
3509 3377 0 | 3509 3377 0
Number of constraints: 6870 | 6870
eq. ineq. range | eq. ineq. range
linear: 504 6366 0 | 504 6366 0
quadratic: 0 0 0 | 0 0 0
Number of nonzeros:
objective Jacobian Hessian | objective Jacobian Hessian
linear: 1008 15598 | 1008 15598
quadratic: 504 0 504 | 504 0 504
total: 1008 15598 504 | 1008 15598 504
Knitro using Branch and Bound method with 8 threads.
Initial points
--------------
No initial point provided for the root node relaxation.
No primal point provided for the MIP.
Coefficient range:
linear objective: [1e+01, 6e+02] | [2e+00, 1e+02]
linear constraints: [1e+00, 2e+02] | [7e-01, 1e+02]
quadratic objective: [3e-02, 1e-01] | [5e-03, 2e-02]
quadratic constraints: [0e+00, 0e+00] | [0e+00, 0e+00]
variable bounds: [1e+00, 2e+02] | [1e+00, 2e+02]
constraint bounds: [1e+00, 2e+02] | [1e+00, 2e+02]
Root node relaxation
--------------------
Iter Objective Feasibility Optimality Time
error error (secs)
---- --------- ----------- ---------- ------
1 292009. 9.18623 37.6427 0.170
2 285619. 1.34977 17.3486 0.182
3 302103. 0.378027 17.3486 0.193
4 274191. 2.24937e-02 5.13851 0.201
5 251383. 7.92026e-03 3.28855 0.207
6 223548. 1.95982e-03 2.73753 0.213
7 202091. 1.63794e-04 1.27362 0.219
8 195453. 2.34342e-05 0.345642 0.226
9 193640. 2.59783e-06 0.111450 0.232
10 193264. 1.00037e-06 7.51847e-02 0.237
11 192927. 2.05976e-07 8.36857e-02 0.243
12 192780. 1.88764e-08 1.59895e-02 0.250
13 192771. 1.54518e-08 9.04205e-03 0.255
14 192755. 4.29941e-09 3.28825e-03 0.261
15 192746. 1.81713e-10 2.07496e-04 0.268
16 192746. 6.17359e-11 1.71659e-04 0.274
17 192746. 8.64879e-10 1.78123e-06 0.279
18 192746. 8.64879e-10 1.78123e-06 0.438
19 192746. 8.64879e-10 1.78123e-06 8.981
Root node cutting planes
------------------------
Iter Cuts Best solution Best bound Gap Time
value value (secs)
---- ---- ------------- ---------- --- ------
0 0 248954. 192746. 22.58% 9.083
Tree search
-----------
Nodes Best solution Best bound Gap Time
Expl | Unexpl value value (secs)
--------------- ------------- ---------- --- ------
1 0 248954. 192746. 22.58% 30.017
EXIT: Time limit reached. Integer feasible point found.
Final Statistics for MIP
------------------------
Final objective value = 2.48954353999999992e+05
Final bound value = 1.92745723301910068e+05
Final optimality gap (abs / rel) = 56208.6 / 0.225779 (22.58%)
# of root cutting plane rounds = 1
# of restarts = 0
# of nodes processed = 1 (29.775s)
# of strong branching evaluations = 0 (0.000s)
# of function evaluations = 0 (0.000s)
# of gradient evaluations = 0 (0.000s)
# of hessian evaluations = 0 (0.000s)
# of hessian-vector evaluations = 0
# of subproblems processed = 3 (29.775s)
Total program time (secs) = 30.01675 (182.981 CPU time)
Time spent in evaluations (secs) = 0.00000
Cuts statistics (gen / add)
---------------------------
Knapsack cuts = 15 / 5
Mixed-integer rounding cuts = 0 / 0
Gomory cuts = 1 / 1
Flow-cover cuts = 0 / 0
Probing cuts = 3141 / 1133
Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump = 0 / 0 / 0.000s
Rounding heuristic = 1 / 0 / 0.003s
MPEC heuristic = 0 / 0 / 0.000s
Local search heuristic = 4 / 1 / 0.131s
Fix-and-propagate heuristics = 0 / 0 / 0.000s
===========================================================================
MULTI-OBJECTIVE MODE: objective 2 (out of 2) ...
==============================================================================
Artelys Knitro 15.1.0: numthreads=8
ms_enable=1
maxtime=30
=======================================
Commercial License
Artelys Knitro 15.1.0
=======================================
Knitro changing mip_method from AUTO to 1.
concurrent_evals 0
datacheck 0
feastol 1e-06
feastol_abs 1e-06
findiff_numthreads 1
hessian_no_f 1
hessopt 1
maxtime 30
ms_enable 1
numthreads 8
opttol 1e-06
opttol_abs 0.001
Knitro changing mip_root_nlpalg from AUTO to 1.
Knitro changing mip_node_nlpalg from AUTO to 1.
Knitro changing mip_branchrule from AUTO to 2.
Knitro changing mip_selectrule from AUTO to 2.
Knitro changing mip_mir from AUTO to 2.
Knitro changing mip_clique from AUTO to 0.
Knitro changing mip_zerohalf from AUTO to 0.
Knitro changing mip_liftproject from AUTO to 0.
Knitro changing mip_knapsack from AUTO to 2.
Knitro changing mip_gomory from AUTO to 1.
Knitro changing mip_cut_flowcover from AUTO to 2.
Knitro changing mip_cut_probing from AUTO to 1.
Knitro changing mip_rounding from AUTO to 3.
Knitro changing mip_heuristic_strategy from AUTO to 1.
Knitro changing mip_heuristic_feaspump from AUTO to 1.
Knitro changing mip_heuristic_misqp from AUTO to 0.
Knitro changing mip_heuristic_mpec from AUTO to 1.
Knitro changing mip_heuristic_diving from AUTO to 0.
Knitro changing mip_heuristic_lns from AUTO to 0.
Knitro changing mip_heuristic_localsearch from AUTO to 1.
Knitro changing mip_heuristic_fixpropagate from AUTO to 1.
Knitro changing mip_pseudoinit from AUTO to 1.
Problem Characteristics | Presolved
-----------------------
Problem type: convex MIQCQP
Objective: minimize / linear
Number of variables: 6886 | 6886
bounds: lower upper range | lower upper range
0 0 4378 | 0 0 4378
free fixed | free fixed
0 2508 | 0 2508
cont. binary integer | cont. binary integer
3509 3377 0 | 3509 3377 0
Number of constraints: 6871 | 6871
eq. ineq. range | eq. ineq. range
linear: 504 6366 0 | 504 6366 0
quadratic: 0 1 0 | 0 1 0
Number of nonzeros:
objective Jacobian Hessian | objective Jacobian Hessian
linear: 432 16606 | 432 16606
quadratic: 0 504 504 | 0 504 504
total: 432 16606 504 | 432 16606 504
Knitro using Branch and Bound method with 8 threads.
Initial points
--------------
Read root node initial point:
Objective value: 6.732200e+03
Feasibility error: 0.000000e+00
Feasible: yes
Read MIP primal point:
Objective value: 6.732200e+03
Feasibility error: 0.000000e+00
Feasible: yes
Coefficient range:
linear objective: [3e-01, 8e-01] | [3e-01, 8e-01]
linear constraints: [1e+00, 6e+02] | [7e-01, 1e+02]
quadratic objective: [0e+00, 0e+00] | [0e+00, 0e+00]
quadratic constraints: [3e-02, 1e-01] | [5e-03, 2e-02]
variable bounds: [1e+00, 2e+02] | [1e+00, 2e+02]
constraint bounds: [1e+00, 3e+05] | [1e+00, 5e+04]
Root node relaxation
--------------------
Iter Objective Feasibility Optimality Time
error error (secs)
---- --------- ----------- ---------- ------
1 6730.63 9.76182e-02 0.800000 0.249
2 6724.66 8.57816e-02 0.800000 0.277
3 6718.46 5.78552e-02 0.800000 0.296
4 6682.75 3.20001e-02 0.800000 0.308
5 6451.29 1.81746e-02 0.800000 0.321
6 6104.81 7.36658e-03 0.733289 0.335
7 5587.70 2.66069e-03 0.664096 0.350
8 4771.72 3.05858e-04 0.497880 0.366
9 3983.94 3.00275e-05 0.232193 0.381
10 3522.78 8.55612e-07 5.30708e-02 0.397
11 3482.59 2.43304e-08 4.77726e-03 0.413
12 3478.81 1.75048e-10 1.67151e-04 0.428
13 3478.70 9.59386e-12 3.20852e-08 0.443
14 3478.70 8.93632e-10 7.28366e-09 0.467
15 3478.70 8.93632e-10 7.28366e-09 0.487
Root node cutting planes
------------------------
Iter Cuts Best solution Best bound Gap Time
value value (secs)
---- ---- ------------- ---------- --- ------
0 0 6575.30 3478.70 47.09% 0.636
1 852 6575.30 3481.17 47.06% 1.052
2 502 6575.30 3500.80 46.76% 1.697
Tree search
-----------
Nodes Best solution Best bound Gap Time
Expl | Unexpl value value (secs)
--------------- ------------- ---------- --- ------
1 2 6575.30 3500.80 46.76% 2.174
1 2 3500.80 FP 3500.80 -0.00% 2.200
EXIT: Optimal solution found.
Final Statistics for MIP
------------------------
Final objective value = 3.50079999999760639e+03
Final bound value = 3.50080000165960473e+03
Final optimality gap (abs / rel) = -1.66200e-06 / -4.74748e-10 (-0.00%)
# of root cutting plane rounds = 3
# of restarts = 0
# of nodes processed = 1 (1.523s)
# of strong branching evaluations = 0 (0.000s)
# of function evaluations = 0 (0.000s)
# of gradient evaluations = 0 (0.000s)
# of hessian evaluations = 0 (0.000s)
# of hessian-vector evaluations = 0
# of subproblems processed = 9 (1.547s)
Total program time (secs) = 2.20018 (18.933 CPU time)
Time spent in evaluations (secs) = 0.00000
Cuts statistics (gen / add)
---------------------------
Knapsack cuts = 72 / 21
Mixed-integer rounding cuts = 26 / 20
Gomory cuts = 0 / 0
Flow-cover cuts = 0 / 0
Probing cuts = 5990 / 1259
Heuristics statistics (calls / successes / time)
------------------------------------------------
Feasibility pump = 1 / 1 / 0.025s
Rounding heuristic = 4 / 0 / 0.017s
MPEC heuristic = 0 / 0 / 0.000s
Local search heuristic = 4 / 1 / 0.196s
Fix-and-propagate heuristics = 0 / 0 / 0.000s
===========================================================================
==============================================================================
MULTI-OBJECTIVE MODE: done.
MP2NL 0.1: Knitro 15.1.0: Locally optimal or satisfactory solution.
objective 3500.8; optimality gap -1.66e-06
1 nodes; 9 subproblem solves
suffix numiters OUT;
suffix opterror OUT;
suffix relaxbnd OUT;
suffix feaserror OUT;
suffix incumbent OUT;
suffix numfcevals OUT;
Objective = Total_Cost
print("Solution status:", ampl.solve_result, " solve code:", ampl.solve_result_num)
print("=== Objective Values ===")
total_cost = ampl.obj["Total_Cost"].value()
total_emissions = ampl.obj["Total_Emissions"].value()
print(f"Total cost: {total_cost:.2f}$")
print(f"Total emissions: {total_emissions:.2f} tons CO₂")
is_committed = ampl.get_data("is_committed").to_pandas()
display(is_committed.unstack().T)
produce = ampl.get_data("power_generated").to_pandas()
display(produce.unstack().T)
startup = ampl.get_data("is_startup").to_pandas()
display(startup.unstack().T)
Solution status: solved solve code: 0
=== Objective Values ===
Total cost: 248271.92$
Total emissions: 3500.80 tons CO₂
| index0 | G1 | G2 | G3 | G4 | G5 | G6 | G7 | |
|---|---|---|---|---|---|---|---|---|
| index1 | ||||||||
| is_committed | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
| 2 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | |
| 3 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | |
| 4 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | |
| 5 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | |
| ... | ... | ... | ... | ... | ... | ... | ... | |
| 68 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | |
| 69 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | |
| 70 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | |
| 71 | 0 | 1 | 0 | 1 | 1 | 0 | 1 | |
| 72 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
72 rows × 7 columns
| index0 | G1 | G2 | G3 | G4 | G5 | G6 | G7 | |
|---|---|---|---|---|---|---|---|---|
| index1 | ||||||||
| power_generated | 1 | 0.0 | 30.0 | 0.0 | 45.0 | 50.0 | 0.0 | 30.0 |
| 2 | 0.0 | 30.0 | 0.0 | 44.0 | 50.0 | 0.0 | 30.0 | |
| 3 | 0.0 | 30.0 | 0.0 | 57.0 | 50.0 | 0.0 | 30.0 | |
| 4 | 0.0 | 41.0 | 0.0 | 60.0 | 50.0 | 0.0 | 30.0 | |
| 5 | 0.0 | 30.0 | 0.0 | 58.0 | 50.0 | 0.0 | 30.0 | |
| ... | ... | ... | ... | ... | ... | ... | ... | |
| 68 | 0.0 | 40.0 | 0.0 | 60.0 | 50.0 | 0.0 | 30.0 | |
| 69 | 0.0 | 30.0 | 0.0 | 59.0 | 50.0 | 0.0 | 30.0 | |
| 70 | 0.0 | 30.0 | 0.0 | 44.0 | 50.0 | 0.0 | 30.0 | |
| 71 | 0.0 | 30.0 | 0.0 | 49.0 | 50.0 | 0.0 | 30.0 | |
| 72 | 0.0 | 30.0 | 0.0 | 55.0 | 50.0 | 0.0 | 30.0 |
72 rows × 7 columns
| index0 | G1 | G2 | G3 | G4 | G5 | G6 | G7 | |
|---|---|---|---|---|---|---|---|---|
| index1 | ||||||||
| is_startup | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 1 |
| 2 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| 4 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| 5 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| ... | ... | ... | ... | ... | ... | ... | ... | |
| 68 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| 69 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| 70 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| 71 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | |
| 72 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
72 rows × 7 columns
More documentation of the AMPL/MP interface (in particular, MP2NL).
More energy examples.