Added 21/07/2025
Energy
electricity_market_monopoly
Dimension
{
"x": 25,
"y": 168,
"F": 1,
"G": 72,
"H": 0,
"f": 1,
"g": 343,
"h": 0
}
Solution
{
"optimality": "infeasible",
"x": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"y": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
"F": 0,
"G": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],
"H": [],
"f": 0,
"g": [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,-0.1,0.87536857,0.87329652,0.52418612,0.70880937,0.58396042,0.42134981,0.13151717],
"h": []
}
\subsection{electricity\_market\_monopoly}
\label{subsec:electricity_market_monopoly}
% Description
This model, proposed in \cite{electricity}, considers an electricity market where a single supplier sets the price of electricity which a set of $N$ customers use to power their appliances. The problem is defined across a set of time slots $H$. The electricity supplier, taking the role of the leader in the upper-level, sets the price for each time slot $p_h$ for all $h \in H$, bounded above by the maximum allowed price $p_h^\text{max}$. Each customer $n \in \N$ then sets the power level $x_{n,a}^h$ of each of their appliances $a \in A_n$ at time slot $h \in T_{n,a}$, where $T_{n,a} := [TW_{n,a}^b, TW_{n,a}^e]$ is the time window of appliance $a$ owned by customer $n$. The supplier is concerned with a trade-off between the revenue and the peak load $\Gamma$ caused by the power levels generated by the customers in the lower-level. A penalty is imposed proportionally to the peak load, controlled by a penalty parameter $\kappa$.
The objective of the customers in the lower-level is measured by two terms, the electricity bill, measured by the price of electricity multiplied by the amount used, and an inconvenience cost. It assumed that a customer $n$ would prefer to run appliance $a$ in the initial time slot of the time window $T_{n,a}$. Postponing this would come with a cost, namely the inconvenience cost, defined as
\[C_{n,a}(h) := \lambda_n E_{n,a} \frac{h - TW^b_{n,a}}{TW^e_{n,a} - TW^b_{n,a}}, \quad \forall \, n \in N, a \in A_n, h \in H,\]
where $\lambda_n$ is the inconvenience coefficient of customer $n$.
Let $E_{n,a}$ be the demand of appliance $a$ owned by customer $n$ and let $\beta_{n,a}^{\text{max}}$ be its power limit. The bilevel optimisation program is then given as
% Equation
\begin{flalign*}
\maximise_{p, \Gamma} \quad
& \sum_{n \in N} \sum_{a \in A_n} \sum_{h \in H} p^h x^h_{n,a} - \kappa \Gamma \\
\subjectto \quad
& \Gamma \geq \sum_{n \in N, a \in A_n} x^h_{n,a} \quad \forall h \in H \\
& 0 \leq p^h \leq p^h_{\text{max}} \quad \forall h \in H \\
& x \in \argmin_{y}
\left\{
\begin{array}{l}
\sum_{n \in N} \sum_{a \in A_n} \sum_{h \in H} \left ( p^h + C_{n,a}(h) \right ) x^h_{n,a} \\
\text{s.t.} \quad 0 \leq x^h_{n,a} \leq \beta^{\text{max}}_{n,a}, \quad \forall \, n \in N, a \in A_n, h \in H \\
\sum_{h \in H} x^h_{n,a} \geq E_{n,a}, \quad \forall \, n \in N, a \in A_n
\end{array}
\right.
\end{flalign*}
classdef electricity_market_monopoly
%{
Comming soon
%}
properties(Constant)
name = 'electricity_market_monopoly';
category = 'energy';
subcategory = '';
end
end
import numpy as np
import json
import os
"""
Sezin Af¸sar, Luce Brotcorne, Patrice Marcotte, and Gilles Savard.
"Achieving an optimal trade-off between revenue and energy peak within a smart grid environment."
Renewable Energy, 91:293–301, 2016.
"""
# Properties
name: str = "electricity_market_monopoly"
category: str = "energy"
subcategory: str = ""
datasets: list = ["electricity_data_toy_ex.json"]
paths: list = [os.path.join('bolib3', 'data', 'electricity_data_toy_ex.json')]
# Feasible point
x0 = np.zeros(24+1)
y0 = np.zeros(4*24 + 2*24 + 1*24)
kappa = 0.1
p_max = np.ones(24)
beta_max = [np.ones(4)*0.1, np.ones(2)*0.1, np.ones(1)*0.1]
# Methods
def F(x, y, data):
"""
Upper-level objective function
"""
hours = data["H"]
N = data["N"]
A = data["A"]
p, gamma = x[:hours], x[-1]
pl = []
for i in range(len(A)):
pl.append(y[sum(A[:i])*hours : sum(A[:i+1])*hours].reshape(A[i], hours))
return sum([p[h]*pl[n][a,h] for n in range(N) for a in range(A[n]) for h in range(hours)]) - kappa*gamma
def G(x, y, data):
"""
Upper-level inequality constraints
"""
hours = data["H"]
N = data["N"]
A = data["A"]
p, gamma = x[:hours], x[-1]
pl = []
for i in range(len(A)):
pl.append(y[sum(A[:i])*hours : sum(A[:i+1])*hours].reshape(A[i], hours))
g_1 = [sum([pl[n][a][h] for n in range(N) for a in range(A[n])]) for h in range(hours)] - gamma
g_2 = -p
g_3 = p - p_max
return np.concatenate((g_1, g_2, g_3))
def H(x, y, data=None):
"""
Upper-level equality constraints
"""
return np.empty(0)
def f(x, y, data):
"""
Lower-level objective function
"""
hours = data["H"]
N = data["N"]
A = data["A"]
p, gamma = x[:hours], x[-1]
pl = []
for i in range(len(A)):
pl.append(y[sum(A[:i])*hours : sum(A[:i+1])*hours].reshape(A[i], hours))
return sum([(p[h] + C(n,a,h,data))*pl[n][a,h] for n in range(N) for a in range(A[n]) for h in range(hours)])
def g(x, y, data):
"""
Lower-level inequality constraints
"""
hours = data["H"]
N = data["N"]
A = data["A"]
E = data["E"]
p, gamma = x[:hours], x[-1]
pl = []
for i in range(len(A)):
pl.append(y[sum(A[:i])*hours : sum(A[:i+1])*hours].reshape(A[i], hours))
g_1 = - y
g_2 = y - np.array([[beta_max[n][a]]*hours for n in range(N) for a in range(A[n])]).flatten()
g_3 = np.array([E[n][a] - sum([pl[n][a][h] for h in range(hours)]) for n in range(N) for a in range(A[n])])
return np.concatenate((g_1, g_2, g_3))
def h(x, y, data=None):
"""
Lower-level equality constraints
"""
return np.empty(0)
def read_data(filepath=datasets[0]):
"""
If the bilevel program is parameterized by data, this function should
provide code to read data file and return an appropriate python structure.
"""
with open(filepath, 'r') as file:
json_str = file.read()
data = json.loads(json_str)
return data
def dimension(key='', data=None):
"""
If the argument 'key' is not specified, then:
- a dictionary mapping variable/function names (str) to the corresponding dimension (int) is returned.
If the first argument 'key' is specified, then:
- a single integer representing the dimension of the variable/function with the name {key} is returned.
"""
hours = data["H"]
N = data["N"]
A = data["A"]
n = {
"x": hours + 1, # Upper-level variables
"y": sum([a*hours for a in A]), # Lower-level variables
"F": 1, # Upper-level objective functions
"G": hours*3, # Upper-level inequality constraints
"H": 0, # Upper-level equality constraints
"f": 1, # Lower-level objective functions
"g": 2*sum([a*hours for a in A]) + sum(A), # Lower-level inequality constraints
"h": 0, # Lower-level equality constraints
}
if key in n:
return n[key]
return n
# Extra Functions
def C(n,a,h, data):
lam = data["lam"]
E = data["E"]
T = data["T"]
return lam[h]*E[n][a]*((h - T[n][a][0]) / (T[n][a][1] - T[n][a][0]))