Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7515e46135 |
No files matched your search
@@ -1,3 +1,10 @@
|
|||||||
|
# Scripts
|
||||||
|
main.py
|
||||||
|
|
||||||
# UV Environment
|
# UV Environment
|
||||||
.python-version
|
.python-version
|
||||||
.venv
|
.venv
|
||||||
|
|
||||||
|
# Datasets
|
||||||
|
dataset.py
|
||||||
|
data/capacity.csv
|
||||||
@@ -1,18 +1,15 @@
|
|||||||
# Mini Projet - Optimisation Métaheuristique
|
# Mini Projet - Optimisation Métaheuristique
|
||||||
|
|
||||||
Ceci est le répertoire Git du projet d'optimisation métaheuristique du groupe 9 dont les membres sont **AIT MOUSSA Amine, DAANOUNI Siham et DELAMOTTE Clément**.
|
Ceci est le répertoire Git du projet d'optimisation métaheuristique du groupe 9 dont les membres sont AIT MOUSSA Amine, DAANOUNI Siham et DELAMOTTE Clément.
|
||||||
|
|
||||||
Le sujet choisi est **l'optimisation du chargement des véhicules électriques** et l'algorithme mis en place est **Multiple Objectives Particle Swarm Optimization (MOPSO) + Surrogate**. La modélisation du problème se trouvera dans le rapport et les slides de présentation.
|
Le sujet choisi est **l'optimisation du chargement des véhicules électriques** et l'algorithme mis en place est **Multiple Objectives Particle Swarm Optimization (MOPSO) + Surrogate**. La modélisation du problème se trouvera dans le rapport.
|
||||||
|
|
||||||
Pour les datasets, nous avons pris diverses sources réalistes pour concevoir nos propres jeux de données afin de pouvoir récupérer des paramètres cruciaux:
|
Pour les datasets, nous avons pris diverses sources pour concevoir notre propre jeu de données:
|
||||||
- data/vehicle_capacity.csv: [Car Dataset (2025)](https://www.kaggle.com/datasets/abdulmalik1518/cars-datasets-2025/data).
|
- data/vehicle_capacity.csv: [Car Dataset (2025)](https://www.kaggle.com/datasets/abdulmalik1518/cars-datasets-2025/data)
|
||||||
- data/elec_prices.csv: [RTE France (éco2mix)](https://www.rte-france.com/donnees-publications/eco2mix-donnees-temps-reel/donnees-marche), les données ont été récupérées manuellement sur l'hivers 2025 (S2-S5) et l'été 2025 (S29-S32).
|
- data/elec_prices.csv: [RTE France (éco2mix)](https://www.rte-france.com/donnees-publications/eco2mix-donnees-temps-reel/donnees-marche), les données ont été récupérées manuellement sur l'hivers 2025 (S2-S5) et l'été 2025 (S29-S32)
|
||||||
- data/grid_capacity.txt: [RTE France (éco2mix)](https://www.rte-france.com/donnees-publications/eco2mix-donnees-temps-reel/donnees-marche), même procédé qu'au dessus.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Pour télécharger le projet vous pouvez simplement utiliser la commande `git clone https://gitea.galaxynoliro.fr/KuMiShi/Optim_Metaheuristique.git` ou récupérer le fichier `.zip` du projet et l'extraire.
|
Le projet a été concu à l'aide du *Python packet manager* ***UV***, il est préférable d'utiliser celui-ci pour ca facilité d'utilisation. **UV** peut être installé via le [site internet officiel](https://docs.astral.sh/uv/getting-started/installation/#installing-uv).
|
||||||
|
|
||||||
Le projet a été concu à l'aide du ***Python packet manager UV***, il est préférable d'utiliser celui-ci pour sa facilité d'utilisation **sauf si vous vous contentez de regarder les résultats de notre notebook**. **UV** peut être installé via le [site internet officiel](https://docs.astral.sh/uv/getting-started/installation/#installing-uv) sur tout système d'exploitation.
|
|
||||||
|
|
||||||
**Linux:**
|
**Linux:**
|
||||||
```bash
|
```bash
|
||||||
@@ -30,11 +27,6 @@ winget install --id=astral-sh.uv -e
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Utilisation
|
## Utilisation
|
||||||
Vous pouvez utiliser le projet de deux manières:
|
|
||||||
|
|
||||||
1. Récupérer le notebook et suivre les cellules une à une avec les résultats pré-compiler dans le fichier.
|
|
||||||
2. Exécuter le projet complet à l'aide du code source et de **UV**
|
|
||||||
|
|
||||||
Pour charger le projet et l'executer sans problème, il faut d'abord configurer notre environnement d'execution de la manière suivante:
|
Pour charger le projet et l'executer sans problème, il faut d'abord configurer notre environnement d'execution de la manière suivante:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -44,8 +36,8 @@ uv venv
|
|||||||
# Téléchargement des requirements du projet
|
# Téléchargement des requirements du projet
|
||||||
uv pip sync uv.lock
|
uv pip sync uv.lock
|
||||||
|
|
||||||
# Si uv.lock ne fonctionne pas correctement ou n'existe pas, vous pouvez le générer avec la commande suivante à partir du .toml:
|
# Si uv.lock n'existe pas, vous pouvez le générer avec la commande suivante:
|
||||||
uv pip compile --upgrade pyproject.toml -o uv.lock
|
uv pip compile --upgrade pyproject.toml -o uv.lock
|
||||||
```
|
```
|
||||||
|
|
||||||
Enfin, vous pouvez executer n'importe quel script avec la commande `uv run main.py`, sachant que `main.py` peut être remplacé par n'importe quel autre script python executable.
|
Enfin, vous pouvez executer n'importe quel script avec la commande `uv run main.py` (main.py pouvant etre remplacé par n'importe quel autre script python executable).
|
||||||
Binary file not shown.
Binary file not shown.
@@ -14,7 +14,7 @@ Winter 2025; Summer 2025
|
|||||||
12.54; 76.77
|
12.54; 76.77
|
||||||
0.4; 63.01
|
0.4; 63.01
|
||||||
60.01; 54.1
|
60.01; 54.1
|
||||||
115.8; 69.52
|
1158; 69.52
|
||||||
93.49; 94.16
|
93.49; 94.16
|
||||||
71.25; 30.5
|
71.25; 30.5
|
||||||
79.76; 46.2
|
79.76; 46.2
|
||||||
|
|||||||
|
@@ -1,9 +0,0 @@
|
|||||||
| Maximum | Minimum
|
|
||||||
---------------------------------------------------
|
|
||||||
Consumption (Winter)| 87 028 Mwh | 46 847 Mwh
|
|
||||||
(Summer)| 52 374 Mwh | 29 819 Mwh
|
|
||||||
---------------------------------------------------
|
|
||||||
Production (Winter)| 91 341 Mwh | 72 926 Mwh
|
|
||||||
(Summer)| 86 579 Mwh | 49 127 Mwh
|
|
||||||
|
|
||||||
Winter correspond to S2-S5 and Summer correspond to S29-S32 (same as prices)
|
|
||||||
@@ -1,283 +1,6 @@
|
|||||||
import time
|
def main():
|
||||||
import numpy as np
|
print("Hello from optim-meta!")
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import copy
|
|
||||||
from mopso import MOPSO
|
|
||||||
from surrogate_handler import SurrogateHandler
|
|
||||||
import pandas as pd
|
|
||||||
|
|
||||||
class SmartMOPSO(MOPSO):
|
|
||||||
def __init__(self, model_type=None, **kwargs):
|
|
||||||
super().__init__(**kwargs)
|
|
||||||
|
|
||||||
# Initialize Surrogate Handler if model_type is provided
|
if __name__ == "__main__":
|
||||||
self.use_surrogate = (model_type is not None)
|
main()
|
||||||
if self.use_surrogate:
|
|
||||||
self.surrogate_handler = SurrogateHandler(model_type)
|
|
||||||
|
|
||||||
# Pre-fill with initial particle data
|
|
||||||
for p in self.particles:
|
|
||||||
self.surrogate_handler.add_data(p.x, p.f_current[1])
|
|
||||||
|
|
||||||
def iterate(self, prediction_freq:int=10):
|
|
||||||
# Main loop (overriding original logic to manage control flow)
|
|
||||||
for t in range(self.t):
|
|
||||||
self.select_leader()
|
|
||||||
|
|
||||||
for i in range(self.n):
|
|
||||||
# Movement
|
|
||||||
self.particles[i].update_velocity(self.leader.x, self.c1, self.c2, self.w)
|
|
||||||
self.particles[i].update_position()
|
|
||||||
self.particles[i].keep_boudaries(self.A_max)
|
|
||||||
|
|
||||||
if (t % (prediction_freq) != 0) and self.use_surrogate:
|
|
||||||
# Fast exact calculation (f1, f3)
|
|
||||||
f1 = self.particles[i].f1(self.prices)
|
|
||||||
f3 = self.particles[i].f3()
|
|
||||||
|
|
||||||
# Slow prediction (f2) by using Surrogate
|
|
||||||
f2_pred = self.surrogate_handler.predict(self.particles[i].x)
|
|
||||||
|
|
||||||
# Inject scores without running the expensive 'updating_socs'
|
|
||||||
self.particles[i].f_current = [f1, f2_pred, f3]
|
|
||||||
|
|
||||||
else:
|
|
||||||
# Standard Calculation (Slow and Exact)
|
|
||||||
self.particles[i].updating_socs(self.socs, self.capacities)
|
|
||||||
self.particles[i].evaluate(self.prices, self.socs, self.socs_req, self.times)
|
|
||||||
|
|
||||||
self.particles[i].update_best()
|
|
||||||
|
|
||||||
self.update_archive()
|
|
||||||
|
|
||||||
|
|
||||||
# Run Classic MOPSO, collect data and run training for the model
|
|
||||||
def train_surrogate_model(self):
|
|
||||||
# Generation of data
|
|
||||||
for t in range(self.t):
|
|
||||||
self.select_leader()
|
|
||||||
|
|
||||||
for i in range(self.n):
|
|
||||||
# Movement
|
|
||||||
self.particles[i].update_velocity(self.leader.x, self.c1, self.c2, self.w)
|
|
||||||
self.particles[i].update_position()
|
|
||||||
self.particles[i].keep_boudaries(self.A_max)
|
|
||||||
# Standard Calculation (Slow and Exact)
|
|
||||||
self.particles[i].updating_socs(self.socs, self.capacities)
|
|
||||||
self.particles[i].evaluate(self.prices, self.socs, self.socs_req, self.times)
|
|
||||||
|
|
||||||
# Capture data for AI training
|
|
||||||
self.surrogate_handler.add_data(self.particles[i].x, self.particles[i].f_current[1])
|
|
||||||
|
|
||||||
# End of dataset generation (based on classic MOPSO)
|
|
||||||
self.surrogate_handler.train()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_elec_prices(csv_file:str, sep:str=';'):
|
|
||||||
elec_df = pd.read_csv(filepath_or_buffer=csv_file, sep=sep, skipinitialspace=True)
|
|
||||||
|
|
||||||
# Mean of Winter and Summer of 2025 electric prices (Euros/MWh)
|
|
||||||
elec_mean = (elec_df['Winter 2025'].mean() + elec_df['Summer 2025'].mean())/2
|
|
||||||
|
|
||||||
# Standard variation of Winter and Summer of 2025 electric prices (Euros/MWh)
|
|
||||||
elec_std = (elec_df['Winter 2025'].std() + elec_df['Summer 2025'].std())/2
|
|
||||||
|
|
||||||
elec_mean = elec_mean / 1000
|
|
||||||
elec_std = elec_std / 1000
|
|
||||||
|
|
||||||
|
|
||||||
print(f'Electricity prices:\n - Mean: ${elec_mean}€/Mwh\n - Std: ${elec_std}€/Mwh')
|
|
||||||
return elec_mean, elec_std
|
|
||||||
|
|
||||||
def generate_capacities(csv_file:str, nb_vehicles:int, seed:int=42, sep:str=';'):
|
|
||||||
cap_df = pd.read_csv(filepath_or_buffer=csv_file, sep=sep)
|
|
||||||
|
|
||||||
# Getting back all kind of battery capacities with unique values
|
|
||||||
all_capacities = cap_df['Battery Capacity kwh'].dropna().unique()
|
|
||||||
|
|
||||||
# Extracting random values for generating the array of capacities
|
|
||||||
capacities = pd.Series(all_capacities).sample(n=nb_vehicles, random_state=seed)
|
|
||||||
|
|
||||||
print(f'Capacities of vehicles (kwh): ${capacities}')
|
|
||||||
return capacities.tolist()
|
|
||||||
|
|
||||||
def get_power_constants(nb_vehicles:int, nb_consumers:int=67000000):
|
|
||||||
mean_consumption = (87028 + 46847 + 52374 + 29819)/4 # Mean of consumption in France in 2025 (estimate according to data/grid_capacity.txt)
|
|
||||||
sim_ratio = nb_vehicles / nb_consumers # Ratio to reduce A_max of simulation to realistic restrictions
|
|
||||||
|
|
||||||
a_max = sim_ratio * mean_consumption
|
|
||||||
x_max = a_max / nb_vehicles # For init, uniform charging/discharging for every vehicle
|
|
||||||
x_min = -x_max
|
|
||||||
return a_max, x_max, x_min
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def run_scenario(scenario_name, capacities:list, price_mean:float, price_std:float, model_type=None, n:int=20, t:int=30, w:float=0.4, c1:float=0.3, c2:float=0.2, archive_size:int=10, nb_vehicles:int=10, delta_t:int=60, nb_of_ticks:int=48):
|
|
||||||
A_MAX, X_MAX, X_MIN = get_power_constants(nb_vehicles=nb_vehicles)
|
|
||||||
|
|
||||||
|
|
||||||
print(f"\n--- Launching Scenario: {scenario_name} ---")
|
|
||||||
# Simulation parameters
|
|
||||||
params = {
|
|
||||||
'A_max': A_MAX, 'price_mean': price_mean, 'price_std': price_std,
|
|
||||||
'capacities': capacities, 'n': n, 't': t,
|
|
||||||
'w': w, 'c1': c1, 'c2': c2,
|
|
||||||
'nb_vehicles': nb_vehicles, 'delta_t': delta_t, 'nb_of_ticks': nb_of_ticks,
|
|
||||||
'x_min':X_MIN, 'x_max':X_MAX
|
|
||||||
}
|
|
||||||
|
|
||||||
# Instantiate extended class
|
|
||||||
optimizer = SmartMOPSO(model_type=model_type, **params)
|
|
||||||
|
|
||||||
if(model_type is not None):
|
|
||||||
optimizer.train_surrogate_model()
|
|
||||||
|
|
||||||
start_time = time.time()
|
|
||||||
|
|
||||||
# Run simulation
|
|
||||||
optimizer.iterate()
|
|
||||||
|
|
||||||
end_time = time.time()
|
|
||||||
duration = end_time - start_time
|
|
||||||
|
|
||||||
# Retrieve best f2 (e.g. from archive)
|
|
||||||
best_f2 = min([p.f_best[1] for p in optimizer.archive]) if optimizer.archive else 0
|
|
||||||
|
|
||||||
print(f"Finished in {duration:.2f} seconds.")
|
|
||||||
print(f"Best f2 found: {best_f2:.4f}")
|
|
||||||
|
|
||||||
return duration, best_f2, optimizer.archive
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# CSV files
|
|
||||||
elec_price_csv = 'data/elec_prices.csv'
|
|
||||||
capacity_csv = 'data/vehicle_capacity.csv'
|
|
||||||
|
|
||||||
# Global Simulation parameters
|
|
||||||
T = 30 # Number of iterations (for the particles)
|
|
||||||
W = 0.4 # Inertia (for exploration)
|
|
||||||
C1 = 0.3 # Individual trust
|
|
||||||
C2 = 0.2 # Social trust
|
|
||||||
ARC_SIZE = 10 # Archive size
|
|
||||||
nb_vehicle = 20
|
|
||||||
|
|
||||||
P_MEAN, P_STD = calculate_elec_prices(elec_price_csv)
|
|
||||||
CAPACITIES = generate_capacities(capacity_csv, nb_vehicles=nb_vehicle)
|
|
||||||
|
|
||||||
NB_TICKS = 48
|
|
||||||
DELTA = 60
|
|
||||||
|
|
||||||
results = {
|
|
||||||
'MOPSO':[],
|
|
||||||
'MLP': [],
|
|
||||||
'RF': []
|
|
||||||
}
|
|
||||||
nb_particles = [20,50,100,500]
|
|
||||||
|
|
||||||
for k in range(len(nb_particles)):
|
|
||||||
# 1. Without Surrogate (Baseline)
|
|
||||||
d1, f1_score, _ = run_scenario(
|
|
||||||
"Only MOPSO",
|
|
||||||
capacities=CAPACITIES,
|
|
||||||
price_mean=P_MEAN,
|
|
||||||
price_std=P_STD,
|
|
||||||
nb_vehicles=nb_vehicle, # Important pour la cohérence
|
|
||||||
model_type=None,
|
|
||||||
n=nb_particles[k]
|
|
||||||
)
|
|
||||||
results['MOPSO'].append((d1, f1_score))
|
|
||||||
|
|
||||||
# 2. With MLP
|
|
||||||
d2, f2_score, _ = run_scenario(
|
|
||||||
"With MLP",
|
|
||||||
capacities=CAPACITIES,
|
|
||||||
price_mean=P_MEAN,
|
|
||||||
price_std=P_STD,
|
|
||||||
nb_vehicles=nb_vehicle,
|
|
||||||
model_type='mlp',
|
|
||||||
n=nb_particles[k]
|
|
||||||
)
|
|
||||||
results['MLP'].append((d2, f2_score))
|
|
||||||
|
|
||||||
# 3. With Random Forest
|
|
||||||
d3, f3_score, _ = run_scenario(
|
|
||||||
"With Random Forest",
|
|
||||||
capacities=CAPACITIES,
|
|
||||||
price_mean=P_MEAN,
|
|
||||||
price_std=P_STD,
|
|
||||||
nb_vehicles=nb_vehicle,
|
|
||||||
model_type='rf',
|
|
||||||
n=nb_particles[k]
|
|
||||||
)
|
|
||||||
results['RF'].append((d3, f3_score))
|
|
||||||
|
|
||||||
|
|
||||||
# --- DISPLAY RESULTS ---
|
|
||||||
print("\n=== SUMMARY ===")
|
|
||||||
print(f"{'Mode':<15} | {'Time (s)':<10} | {'Best f2':<10}")
|
|
||||||
print("-" * 45)
|
|
||||||
for k, v in results.items():
|
|
||||||
for i in range(len(nb_particles)):
|
|
||||||
print(f"{k:<15}_{nb_particles[i]:<15} | {v[i][0]:<10.2f} | {v[i][1]:<10.4f}")
|
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
def plot_time_benchmark(nb_particles_list, results_dict):
|
|
||||||
|
|
||||||
t_mopso = [item[0] for item in results_dict['MOPSO']]
|
|
||||||
t_mlp = [item[0] for item in results_dict['MLP']]
|
|
||||||
t_rf = [item[0] for item in results_dict['RF']]
|
|
||||||
|
|
||||||
plt.figure(figsize=(10, 6))
|
|
||||||
|
|
||||||
plt.plot(nb_particles_list, t_mopso, 'o-', label='Sans IA (MOPSO)', color='#1f77b4', linewidth=2)
|
|
||||||
plt.plot(nb_particles_list, t_mlp, 's--', label='Avec MLP', color='#ff7f0e', linewidth=2)
|
|
||||||
plt.plot(nb_particles_list, t_rf, '^-.', label='Avec Random Forest', color='#2ca02c', linewidth=2)
|
|
||||||
|
|
||||||
plt.title("Temps d'exécution selon le nombre de particules", fontsize=14, fontweight='bold')
|
|
||||||
plt.xlabel("Nombre de Particules", fontsize=12)
|
|
||||||
plt.ylabel("Temps (s)", fontsize=12)
|
|
||||||
plt.grid(True, linestyle=':', alpha=0.7)
|
|
||||||
plt.legend(fontsize=11)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
|
|
||||||
plot_time_benchmark(nb_particles, results)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
|
|
||||||
def plot_f2_benchmark(nb_particles_list, results_dict):
|
|
||||||
s_mopso = [item[1] for item in results_dict['MOPSO']]
|
|
||||||
s_mlp = [item[1] for item in results_dict['MLP']]
|
|
||||||
s_rf = [item[1] for item in results_dict['RF']]
|
|
||||||
|
|
||||||
|
|
||||||
plt.figure(figsize=(10, 6))
|
|
||||||
|
|
||||||
plt.plot(nb_particles_list, s_mopso, 'o-', label='Sans IA (MOPSO)', color='#1f77b4', linewidth=2)
|
|
||||||
plt.plot(nb_particles_list, s_mlp, 's--', label='Avec MLP', color='#ff7f0e', linewidth=2)
|
|
||||||
plt.plot(nb_particles_list, s_rf, '^-.', label='Avec Random Forest', color='#2ca02c', linewidth=2)
|
|
||||||
|
|
||||||
plt.title("Meilleur Score F2 (Convergence) selon le nombre de particules", fontsize=14, fontweight='bold')
|
|
||||||
plt.xlabel("Nombre de Particules (log scale)", fontsize=12)
|
|
||||||
plt.ylabel("Meilleur F2 Score", fontsize=12)
|
|
||||||
plt.grid(True, linestyle=':', alpha=0.7)
|
|
||||||
plt.legend(fontsize=11)
|
|
||||||
|
|
||||||
plt.xscale('log')
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
plot_f2_benchmark(nb_particles, results)
|
|
||||||
File renamed without changes.
@@ -1,9 +1,8 @@
|
|||||||
import random as rd
|
import random as rd
|
||||||
from particle import Particle
|
from .particle import Particle
|
||||||
import copy
|
|
||||||
|
|
||||||
class MOPSO():
|
class MOPSO():
|
||||||
def __init__(self, A_max:float, price_mean:float, price_std:float, capacities:list, n:int, t:int, w:float, c1:float, c2:float, archive_size:int=10, nb_vehicles:int=10, delta_t:int=60, nb_of_ticks:int=72, x_min=-100, x_max=100, v_alpha=0.1, surrogate=False):
|
def __init__(self, f_weights:list, A_max:float, price_mean:float, price_std:float, capacities:list, n:int, t:int, w:float, c1:float, c2:float, archive_size:int=10, nb_vehicles:int=10, delta_t:int=60, nb_of_ticks:int=72, x_min=-100, x_max=100, v_alpha=0.1, surrogate=False):
|
||||||
# Constants
|
# Constants
|
||||||
self.n = n # Number of particles
|
self.n = n # Number of particles
|
||||||
self.t = t # Number of simulation iterations
|
self.t = t # Number of simulation iterations
|
||||||
@@ -11,34 +10,24 @@ class MOPSO():
|
|||||||
self.c1 = c1 # Individual trust
|
self.c1 = c1 # Individual trust
|
||||||
self.c2 = c2 # Social trust
|
self.c2 = c2 # Social trust
|
||||||
self.archive_size = archive_size # Archive size
|
self.archive_size = archive_size # Archive size
|
||||||
|
self.f_weights = f_weights # Weigths for aggregation of all function objective
|
||||||
|
|
||||||
self.surrogate = surrogate # Using AI calculation
|
self.surrogate = surrogate # Using AI calculation
|
||||||
|
|
||||||
# Initialisation of particle's global parameters
|
# Initialisation of particle's global parameters
|
||||||
self.A_max = A_max # Network's power limit
|
self.A_max = A_max # Network's power limit
|
||||||
self.socs, self.socs_req = self.generate_state_of_charges(nb_vehicles,nb_of_ticks)
|
self.socs, self.socs_req = self.generate_state_of_charges(nb_vehicles,nb_of_ticks)
|
||||||
self.times = self.generate_times(nb_vehicles, nb_of_ticks)
|
self.times = self.generate_times(nb_vehicles, nb_of_ticks, delta_t)
|
||||||
self.prices = self.generates_prices(nb_of_ticks,price_mean,price_std) #TODO: Use RTE France prices for random prices generation according to number of ticks
|
self.prices = self.generates_prices(price_mean,price_std) #TODO: Use RTE France prices for random prices generation according to number of ticks
|
||||||
self.capacities = capacities
|
self.capacities = capacities
|
||||||
|
|
||||||
# Particles of the simulation
|
# Particles of the simulation
|
||||||
self.particles = [
|
self.particles = [Particle(nb_vehicles=nb_vehicles, nb_of_ticks=nb_of_ticks, delta_t=delta_t, x_min=x_min, x_max=x_max, alpha=v_alpha) for _ in range(self.n)]
|
||||||
Particle(
|
|
||||||
socs=copy.deepcopy(self.socs),
|
|
||||||
times=self.times, # Ajouté ici
|
|
||||||
nb_vehicles=nb_vehicles,
|
|
||||||
nb_of_ticks=nb_of_ticks,
|
|
||||||
delta_t=delta_t,
|
|
||||||
x_min=x_min,
|
|
||||||
x_max=x_max,
|
|
||||||
alpha=v_alpha
|
|
||||||
) for _ in range(self.n)
|
|
||||||
]
|
|
||||||
self.archive = []
|
self.archive = []
|
||||||
self.leader = self.particles[0] # it doesnt matter as the first thing done is choosing a new leader
|
self.leader = self.particles[0] # it doesnt matter as the first thing done is choosing a new leader
|
||||||
|
|
||||||
for i in range(self.n):
|
for i in range(self.n):
|
||||||
self.particles[i].evaluate(self.prices, self.socs, self.socs_req, self.times)
|
self.particles[i].evaluate(self.f_weights, self.prices, self.socs, self.socs_req, self.times)
|
||||||
self.update_archive()
|
self.update_archive()
|
||||||
|
|
||||||
def iterate(self):
|
def iterate(self):
|
||||||
@@ -70,7 +59,7 @@ class MOPSO():
|
|||||||
# Checking for best positions
|
# Checking for best positions
|
||||||
|
|
||||||
# Generation of arriving and leaving times for every vehicle
|
# Generation of arriving and leaving times for every vehicle
|
||||||
def generate_times(self, nb_vehicles, nb_of_ticks):
|
def generate_times(self, nb_vehicles, nb_of_ticks, delta_t):
|
||||||
times = []
|
times = []
|
||||||
for _ in range(nb_vehicles):
|
for _ in range(nb_vehicles):
|
||||||
# Minumun, we have one tick of charging/discharging during simulation
|
# Minumun, we have one tick of charging/discharging during simulation
|
||||||
@@ -83,54 +72,52 @@ class MOPSO():
|
|||||||
def generates_prices(self,nb_of_ticks:int, mean:float, std:float):
|
def generates_prices(self,nb_of_ticks:int, mean:float, std:float):
|
||||||
prices = []
|
prices = []
|
||||||
for _ in range(nb_of_ticks):
|
for _ in range(nb_of_ticks):
|
||||||
variation = rd.uniform(-std, std) # Random float variation
|
variation = rd.randrange(-(std*10), (std * 10) +1, 1) / 10 # Random float variation
|
||||||
prices.append(mean + variation)
|
prices.append(mean + variation)
|
||||||
return prices
|
return prices
|
||||||
|
|
||||||
# Genrates the coordinated states of charges requested and initial (duplicated initially for other ticks)
|
# Genrates the coordinated states of charges requested and initial (duplicated initially for other ticks)
|
||||||
def generate_state_of_charges(self, nb_vehicles:int, nb_of_ticks:int):
|
def generate_state_of_charges(self, nb_vehicles:int, nb_of_ticks:int):
|
||||||
# Structure souhaitée : socs[tick][vehicle] pour être cohérent avec self.x[tick][vehicle]
|
socs = []
|
||||||
socs = [[0.0 for _ in range(nb_vehicles)] for _ in range(nb_of_ticks)]
|
|
||||||
socs_req = []
|
socs_req = []
|
||||||
|
# We ensure soc_req is greater than what the soc_init is (percentage transformed into floats)
|
||||||
|
for _ in range(nb_vehicles):
|
||||||
|
soc_init = rd.randrange(0,100,1)
|
||||||
|
soc_req = rd.randrange(soc_init+1, 101,1)
|
||||||
|
|
||||||
for i in range(nb_vehicles):
|
# Creating states of charges for each tick in time
|
||||||
soc_init = rd.randrange(0, 100, 1)
|
for _ in range(nb_of_ticks):
|
||||||
soc_req = rd.randrange(soc_init + 1, 101, 1)
|
socs.append(soc_init/100)
|
||||||
|
|
||||||
# Remplissage de la matrice 2D
|
|
||||||
for tick in range(nb_of_ticks):
|
|
||||||
socs[tick][i] = soc_init / 100.0
|
|
||||||
|
|
||||||
socs_req.append(soc_req / 100.0)
|
|
||||||
|
|
||||||
|
# Adding the requested state of charge
|
||||||
|
socs_req.append(soc_req/100)
|
||||||
return socs, socs_req
|
return socs, socs_req
|
||||||
|
|
||||||
# True if a dominates b, else false
|
# True if a dominates b, else false
|
||||||
def dominates(self, a:Particle, b:Particle):
|
def dominates(a:Particle, b:Particle):
|
||||||
dominates = (a.f_current[0] <= b.f_current[0]) and (a.f_current[1] <= b.f_current[1]) and (a.f_current[2] <= b.f_current[2])
|
dominates = (a.f_current[0] >= b.f_current[0]) and (a.f_current[1] >= b.f_current[1]) and (a.f_current[2] >= b.f_current[2])
|
||||||
if dominates:
|
if dominates:
|
||||||
# Not strict superiority yet
|
# Not strict superiority yet
|
||||||
dominates = (a.f_current[0] < b.f_current[0]) or (a.f_current[1] < b.f_current[1]) or (a.f_current[2] < b.f_current[2])
|
dominates = (a.f_current[0] > b.f_current[0]) or (a.f_current[1] > b.f_current[1]) or (a.f_current[2] > b.f_current[2])
|
||||||
return dominates
|
return dominates
|
||||||
|
|
||||||
|
|
||||||
def update_archive(self):
|
def update_archive(self):
|
||||||
candidates = self.archive + self.particles
|
candidates = self.archive + self.particles
|
||||||
length = len(candidates)
|
length = len(candidates)
|
||||||
|
|
||||||
non_dominated = []
|
non_dominated = []
|
||||||
|
|
||||||
for i in range(length):
|
for i in range(length):
|
||||||
candidate_i = candidates[i]
|
candidate_i = candidates[i]
|
||||||
is_dominated = False
|
dominates = True
|
||||||
|
|
||||||
for j in range(length):
|
for j in range(length):
|
||||||
if i != j:
|
if i!=j:
|
||||||
candidate_j = candidates[j]
|
candidate_j = candidates[j]
|
||||||
if self.dominates(candidate_j, candidate_i):
|
dominates = dominates and self.dominates(candidate_i, candidate_j)
|
||||||
is_dominated = True
|
if dominates:
|
||||||
break
|
|
||||||
if not is_dominated:
|
|
||||||
non_dominated.append(candidate_i)
|
non_dominated.append(candidate_i)
|
||||||
|
|
||||||
|
# Keeping only a certain number of solutions depending on archive_size (to avoid overloading the number of potential directions for particles)
|
||||||
if len(non_dominated) > self.archive_size:
|
if len(non_dominated) > self.archive_size:
|
||||||
final_non_dominated = []
|
final_non_dominated = []
|
||||||
while len(final_non_dominated) < self.archive_size:
|
while len(final_non_dominated) < self.archive_size:
|
||||||
|
|||||||
@@ -1,554 +0,0 @@
|
|||||||
{
|
|
||||||
"cells": [
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "dcf378e6",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"# Optimisation de charge de véhicules électriques par MOPSO assisté par IA\n",
|
|
||||||
"### Par Amine AIT MOUSSA, Siham DAANOUNI, et Clément DELAMOTTE\n",
|
|
||||||
"\n",
|
|
||||||
"---\n",
|
|
||||||
"\n",
|
|
||||||
"**Objectif :** Minimiser le coût de charge, l'insatisfaction utilisateur et la tension sur le réseau.\n",
|
|
||||||
"\n",
|
|
||||||
"**Méthode :** Utilisation d'un algorithme MOPSO (Multi-Objective Particle Swarm Optimization), couplé à un modèle de régression (Surrogate Model) pour accélérer l'évaluation des particules."
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "ea73e020",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"## 1. Imports"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 1,
|
|
||||||
"id": "5cf6f9f8",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"import time\n",
|
|
||||||
"import numpy as np\n",
|
|
||||||
"import matplotlib.pyplot as plt\n",
|
|
||||||
"import copy\n",
|
|
||||||
"from mopso import MOPSO \n",
|
|
||||||
"from surrogate_handler import SurrogateHandler\n",
|
|
||||||
"import pandas as pd"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "ee8b04e4",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"## 2. SmartMOPSO class\n",
|
|
||||||
"We created a new class that heritate the classic MOPSO, but we added :\n",
|
|
||||||
"- in the initialization the surrogate type\n",
|
|
||||||
"- a new implementation to the iteration function, to use the surrogate for predictions"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 2,
|
|
||||||
"id": "f783ba32",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"class SmartMOPSO(MOPSO):\n",
|
|
||||||
" def __init__(self, model_type=None, **kwargs):\n",
|
|
||||||
" super().__init__(**kwargs)\n",
|
|
||||||
" \n",
|
|
||||||
" # Initialize Surrogate Handler if model_type is provided\n",
|
|
||||||
" self.use_surrogate = (model_type is not None)\n",
|
|
||||||
" if self.use_surrogate:\n",
|
|
||||||
" self.surrogate_handler = SurrogateHandler(model_type)\n",
|
|
||||||
"\n",
|
|
||||||
" # Pre-fill with initial particle data\n",
|
|
||||||
" for p in self.particles:\n",
|
|
||||||
" self.surrogate_handler.add_data(p.x, p.f_current[1])\n",
|
|
||||||
"\n",
|
|
||||||
" def iterate(self, prediction_freq:int=10):\n",
|
|
||||||
" # Main loop (overriding original logic to manage control flow)\n",
|
|
||||||
" for t in range(self.t): \n",
|
|
||||||
" self.select_leader()\n",
|
|
||||||
" \n",
|
|
||||||
" for i in range(self.n):\n",
|
|
||||||
" # Movement \n",
|
|
||||||
" self.particles[i].update_velocity(self.leader.x, self.c1, self.c2, self.w)\n",
|
|
||||||
" self.particles[i].update_position()\n",
|
|
||||||
" self.particles[i].keep_boudaries(self.A_max)\n",
|
|
||||||
"\n",
|
|
||||||
" if (t % (prediction_freq) != 0) and self.use_surrogate:\n",
|
|
||||||
" # Fast exact calculation (f1, f3)\n",
|
|
||||||
" f1 = self.particles[i].f1(self.prices)\n",
|
|
||||||
" f3 = self.particles[i].f3()\n",
|
|
||||||
" \n",
|
|
||||||
" # Slow prediction (f2) by using Surrogate\n",
|
|
||||||
" f2_pred = self.surrogate_handler.predict(self.particles[i].x)\n",
|
|
||||||
" \n",
|
|
||||||
" # Inject scores without running the expensive 'updating_socs'\n",
|
|
||||||
" self.particles[i].f_current = [f1, f2_pred, f3]\n",
|
|
||||||
" \n",
|
|
||||||
" else:\n",
|
|
||||||
" # Standard Calculation (Slow and Exact)\n",
|
|
||||||
" self.particles[i].updating_socs(self.socs, self.capacities)\n",
|
|
||||||
" self.particles[i].evaluate(self.prices, self.socs, self.socs_req, self.times)\n",
|
|
||||||
"\n",
|
|
||||||
" self.particles[i].update_best()\n",
|
|
||||||
" \n",
|
|
||||||
" self.update_archive()\n",
|
|
||||||
"\n",
|
|
||||||
"\n",
|
|
||||||
" # Run Classic MOPSO, collect data and run training for the model\n",
|
|
||||||
" def train_surrogate_model(self):\n",
|
|
||||||
" # Generation of data\n",
|
|
||||||
" for t in range(self.t): \n",
|
|
||||||
" self.select_leader()\n",
|
|
||||||
" \n",
|
|
||||||
" for i in range(self.n):\n",
|
|
||||||
" # Movement \n",
|
|
||||||
" self.particles[i].update_velocity(self.leader.x, self.c1, self.c2, self.w)\n",
|
|
||||||
" self.particles[i].update_position()\n",
|
|
||||||
" self.particles[i].keep_boudaries(self.A_max)\n",
|
|
||||||
" # Standard Calculation (Slow and Exact)\n",
|
|
||||||
" self.particles[i].updating_socs(self.socs, self.capacities)\n",
|
|
||||||
" self.particles[i].evaluate(self.prices, self.socs, self.socs_req, self.times)\n",
|
|
||||||
" \n",
|
|
||||||
" # Capture data for AI training\n",
|
|
||||||
" self.surrogate_handler.add_data(self.particles[i].x, self.particles[i].f_current[1])\n",
|
|
||||||
" \n",
|
|
||||||
" # End of dataset generation (based on classic MOPSO)\n",
|
|
||||||
" self.surrogate_handler.train()"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "83075d35",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"## 3. Utility functions\n",
|
|
||||||
"Manage CSV files reading and calculate physical constants"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 3,
|
|
||||||
"id": "a7789c8c",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"def calculate_elec_prices(csv_file:str, sep:str=';'):\n",
|
|
||||||
" elec_df = pd.read_csv(filepath_or_buffer=csv_file, sep=sep, skipinitialspace=True)\n",
|
|
||||||
"\n",
|
|
||||||
" # Mean of Winter and Summer of 2025 electric prices (Euros/MWh)\n",
|
|
||||||
" elec_mean = (elec_df['Winter 2025'].mean() + elec_df['Summer 2025'].mean())/2\n",
|
|
||||||
"\n",
|
|
||||||
" # Standard variation of Winter and Summer of 2025 electric prices (Euros/MWh)\n",
|
|
||||||
" elec_std = (elec_df['Winter 2025'].std() + elec_df['Summer 2025'].std())/2\n",
|
|
||||||
"\n",
|
|
||||||
" elec_mean = elec_mean / 1000\n",
|
|
||||||
" elec_std = elec_std / 1000\n",
|
|
||||||
"\n",
|
|
||||||
"\n",
|
|
||||||
" print(f'Electricity prices:\\n - Mean: ${elec_mean}€/Mwh\\n - Std: ${elec_std}€/Mwh')\n",
|
|
||||||
" return elec_mean, elec_std\n",
|
|
||||||
"\n",
|
|
||||||
"def generate_capacities(csv_file:str, nb_vehicles:int, seed:int=42, sep:str=';'):\n",
|
|
||||||
" cap_df = pd.read_csv(filepath_or_buffer=csv_file, sep=sep)\n",
|
|
||||||
"\n",
|
|
||||||
" # Getting back all kind of battery capacities with unique values\n",
|
|
||||||
" all_capacities = cap_df['Battery Capacity kwh'].dropna().unique()\n",
|
|
||||||
"\n",
|
|
||||||
" # Extracting random values for generating the array of capacities\n",
|
|
||||||
" capacities = pd.Series(all_capacities).sample(n=nb_vehicles, random_state=seed)\n",
|
|
||||||
"\n",
|
|
||||||
" print(f'Capacities of vehicles (kwh): ${capacities}')\n",
|
|
||||||
" return capacities.tolist()\n",
|
|
||||||
"\n",
|
|
||||||
"def get_power_constants(nb_vehicles:int, nb_consumers:int=67000000):\n",
|
|
||||||
" mean_consumption = (87028 + 46847 + 52374 + 29819)/4 # Mean of consumption in France in 2025 (estimate according to data/grid_capacity.txt)\n",
|
|
||||||
" sim_ratio = nb_vehicles / nb_consumers # Ratio to reduce A_max of simulation to realistic restrictions\n",
|
|
||||||
"\n",
|
|
||||||
" a_max = sim_ratio * mean_consumption\n",
|
|
||||||
" x_max = a_max / nb_vehicles # For init, uniform charging/discharging for every vehicle\n",
|
|
||||||
" x_min = -x_max\n",
|
|
||||||
" return a_max, x_max, x_min"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "a2b510ed",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"## 4. Execution Function\n",
|
|
||||||
"Execute the pipeline on a scenario with simulation parameters by using the SmartMOPSO class for the classic MOPSO or for MOPSO and Surrogate. It does the calculation for the functions f1 and f3, and calculate or predict by AI the f2 function"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 4,
|
|
||||||
"id": "a8797755",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"def run_scenario(scenario_name, capacities:list, price_mean:float, price_std:float, model_type=None, n:int=20, t:int=30, w:float=0.4, c1:float=0.3, c2:float=0.2, archive_size:int=10, nb_vehicles:int=10, delta_t:int=60, nb_of_ticks:int=48):\n",
|
|
||||||
" A_MAX, X_MAX, X_MIN = get_power_constants(nb_vehicles=nb_vehicles)\n",
|
|
||||||
"\n",
|
|
||||||
"\n",
|
|
||||||
" print(f\"\\n--- Launching Scenario: {scenario_name} ---\")\n",
|
|
||||||
" # Simulation parameters\n",
|
|
||||||
" params = {\n",
|
|
||||||
" 'A_max': A_MAX, 'price_mean': price_mean, 'price_std': price_std,\n",
|
|
||||||
" 'capacities': capacities, 'n': n, 't': t, \n",
|
|
||||||
" 'w': w, 'c1': c1, 'c2': c2,\n",
|
|
||||||
" 'nb_vehicles': nb_vehicles, 'delta_t': delta_t, 'nb_of_ticks': nb_of_ticks,\n",
|
|
||||||
" 'x_min':X_MIN, 'x_max':X_MAX\n",
|
|
||||||
" }\n",
|
|
||||||
" \n",
|
|
||||||
"# Instantiate extended class\n",
|
|
||||||
" optimizer = SmartMOPSO(model_type=model_type, **params)\n",
|
|
||||||
"\n",
|
|
||||||
" if(model_type is not None):\n",
|
|
||||||
" optimizer.train_surrogate_model()\n",
|
|
||||||
" \n",
|
|
||||||
" start_time = time.time()\n",
|
|
||||||
" \n",
|
|
||||||
" # Run simulation\n",
|
|
||||||
" optimizer.iterate()\n",
|
|
||||||
" \n",
|
|
||||||
" end_time = time.time()\n",
|
|
||||||
" duration = end_time - start_time\n",
|
|
||||||
" \n",
|
|
||||||
" # Retrieve best f2 (e.g. from archive)\n",
|
|
||||||
" best_f2 = min([p.f_best[1] for p in optimizer.archive]) if optimizer.archive else 0\n",
|
|
||||||
" \n",
|
|
||||||
" print(f\"Finished in {duration:.2f} seconds.\")\n",
|
|
||||||
" print(f\"Best f2 found: {best_f2:.4f}\")\n",
|
|
||||||
" \n",
|
|
||||||
" return duration, best_f2, optimizer.archive"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "cdbd5f06",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"## 5. Scenario comparison\n",
|
|
||||||
"\n",
|
|
||||||
"We use here 3 configurations to evaluate our surrogate models :\n",
|
|
||||||
"1. **No AI (Baseline) :** Exact calculation, without any approximation.\n",
|
|
||||||
"2. **MLP (Multi-Layer Perceptron) :** Using a neural network to approximate $f_2$.\n",
|
|
||||||
"3. **RF (Random Forest) :** Using random forest to approximate $f_2$.\n",
|
|
||||||
"\n",
|
|
||||||
"The goal is to compare the **execution time** and **solutions quality**"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 10,
|
|
||||||
"id": "6cdd4953",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"Electricity prices:\n",
|
|
||||||
" - Mean: $0.08469423999999999€/Mwh\n",
|
|
||||||
" - Std: $0.043112482841363334€/Mwh\n",
|
|
||||||
"Capacities of vehicles (kwh): $33 83.0\n",
|
|
||||||
"36 20.0\n",
|
|
||||||
"4 72.8\n",
|
|
||||||
"13 35.8\n",
|
|
||||||
"30 77.4\n",
|
|
||||||
"26 17.3\n",
|
|
||||||
"6 65.0\n",
|
|
||||||
"27 11.4\n",
|
|
||||||
"24 82.0\n",
|
|
||||||
"15 87.0\n",
|
|
||||||
"17 93.4\n",
|
|
||||||
"8 65.5\n",
|
|
||||||
"16 79.2\n",
|
|
||||||
"12 58.0\n",
|
|
||||||
"19 26.0\n",
|
|
||||||
"9 39.2\n",
|
|
||||||
"32 90.0\n",
|
|
||||||
"0 95.0\n",
|
|
||||||
"25 200.0\n",
|
|
||||||
"5 75.0\n",
|
|
||||||
"dtype: float64\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: Only MOPSO ---\n",
|
|
||||||
"Finished in 0.39 seconds.\n",
|
|
||||||
"Best f2 found: 5.6198\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: With MLP ---\n",
|
|
||||||
"Finished in 0.32 seconds.\n",
|
|
||||||
"Best f2 found: 6.7202\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: With Random Forest ---\n",
|
|
||||||
"Finished in 2.77 seconds.\n",
|
|
||||||
"Best f2 found: 5.7399\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: Only MOPSO ---\n",
|
|
||||||
"Finished in 0.92 seconds.\n",
|
|
||||||
"Best f2 found: 4.0497\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: With MLP ---\n",
|
|
||||||
"Finished in 0.78 seconds.\n",
|
|
||||||
"Best f2 found: 4.2297\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: With Random Forest ---\n",
|
|
||||||
"Finished in 6.91 seconds.\n",
|
|
||||||
"Best f2 found: 5.2098\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: Only MOPSO ---\n",
|
|
||||||
"Finished in 1.90 seconds.\n",
|
|
||||||
"Best f2 found: 6.0193\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: With MLP ---\n",
|
|
||||||
"Finished in 1.80 seconds.\n",
|
|
||||||
"Best f2 found: 3.2313\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: With Random Forest ---\n",
|
|
||||||
"Finished in 13.86 seconds.\n",
|
|
||||||
"Best f2 found: 5.1697\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: Only MOPSO ---\n",
|
|
||||||
"Finished in 9.62 seconds.\n",
|
|
||||||
"Best f2 found: 5.3396\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: With MLP ---\n",
|
|
||||||
"Finished in 8.16 seconds.\n",
|
|
||||||
"Best f2 found: 6.0898\n",
|
|
||||||
"\n",
|
|
||||||
"--- Launching Scenario: With Random Forest ---\n",
|
|
||||||
"Finished in 69.95 seconds.\n",
|
|
||||||
"Best f2 found: 7.1100\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"\n",
|
|
||||||
"# CSV files\n",
|
|
||||||
"elec_price_csv = 'data/elec_prices.csv'\n",
|
|
||||||
"capacity_csv = 'data/vehicle_capacity.csv'\n",
|
|
||||||
"\n",
|
|
||||||
"# Global Simulation parameters\n",
|
|
||||||
"T = 30 # Number of iterations (for the particles)\n",
|
|
||||||
"W = 0.4 # Inertia (for exploration)\n",
|
|
||||||
"C1 = 0.3 # Individual trust\n",
|
|
||||||
"C2 = 0.2 # Social trust\n",
|
|
||||||
"ARC_SIZE = 10 # Archive size\n",
|
|
||||||
"nb_vehicle = 20\n",
|
|
||||||
"\n",
|
|
||||||
"P_MEAN, P_STD = calculate_elec_prices(elec_price_csv)\n",
|
|
||||||
"CAPACITIES = generate_capacities(capacity_csv, nb_vehicles=nb_vehicle)\n",
|
|
||||||
"\n",
|
|
||||||
"NB_TICKS = 48\n",
|
|
||||||
"DELTA = 60\n",
|
|
||||||
"\n",
|
|
||||||
"results = {\n",
|
|
||||||
" 'MOPSO':[],\n",
|
|
||||||
" 'MLP': [],\n",
|
|
||||||
" 'RF': []\n",
|
|
||||||
"}\n",
|
|
||||||
"nb_particles = [20,50,100,500]\n",
|
|
||||||
"\n",
|
|
||||||
"for k in range(len(nb_particles)):\n",
|
|
||||||
" # 1. Without Surrogate (Baseline)\n",
|
|
||||||
" d1, f1_score, _ = run_scenario(\n",
|
|
||||||
" \"Only MOPSO\", \n",
|
|
||||||
" capacities=CAPACITIES, \n",
|
|
||||||
" price_mean=P_MEAN, \n",
|
|
||||||
" price_std=P_STD, \n",
|
|
||||||
" nb_vehicles=nb_vehicle, # Important pour la cohérence\n",
|
|
||||||
" model_type=None,\n",
|
|
||||||
" n=nb_particles[k]\n",
|
|
||||||
" )\n",
|
|
||||||
" results['MOPSO'].append((d1, f1_score))\n",
|
|
||||||
"\n",
|
|
||||||
" # 2. With MLP\n",
|
|
||||||
" d2, f2_score, _ = run_scenario(\n",
|
|
||||||
" \"With MLP\", \n",
|
|
||||||
" capacities=CAPACITIES, \n",
|
|
||||||
" price_mean=P_MEAN, \n",
|
|
||||||
" price_std=P_STD, \n",
|
|
||||||
" nb_vehicles=nb_vehicle,\n",
|
|
||||||
" model_type='mlp',\n",
|
|
||||||
" n=nb_particles[k]\n",
|
|
||||||
" )\n",
|
|
||||||
" results['MLP'].append((d2, f2_score))\n",
|
|
||||||
"\n",
|
|
||||||
" # 3. With Random Forest\n",
|
|
||||||
" d3, f3_score, _ = run_scenario(\n",
|
|
||||||
" \"With Random Forest\", \n",
|
|
||||||
" capacities=CAPACITIES, \n",
|
|
||||||
" price_mean=P_MEAN, \n",
|
|
||||||
" price_std=P_STD, \n",
|
|
||||||
" nb_vehicles=nb_vehicle,\n",
|
|
||||||
" model_type='rf',\n",
|
|
||||||
" n=nb_particles[k]\n",
|
|
||||||
" )\n",
|
|
||||||
" results['RF'].append((d3, f3_score))"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 11,
|
|
||||||
"id": "165b48cd",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"\n",
|
|
||||||
"=== SUMMARY ===\n",
|
|
||||||
"Mode | Time (s) | Best f2 \n",
|
|
||||||
"---------------------------------------------\n",
|
|
||||||
"MOPSO _20 | 0.39 | 5.6198 \n",
|
|
||||||
"MOPSO _50 | 0.92 | 4.0497 \n",
|
|
||||||
"MOPSO _100 | 1.90 | 6.0193 \n",
|
|
||||||
"MOPSO _500 | 9.62 | 5.3396 \n",
|
|
||||||
"MLP _20 | 0.32 | 6.7202 \n",
|
|
||||||
"MLP _50 | 0.78 | 4.2297 \n",
|
|
||||||
"MLP _100 | 1.80 | 3.2313 \n",
|
|
||||||
"MLP _500 | 8.16 | 6.0898 \n",
|
|
||||||
"RF _20 | 2.77 | 5.7399 \n",
|
|
||||||
"RF _50 | 6.91 | 5.2098 \n",
|
|
||||||
"RF _100 | 13.86 | 5.1697 \n",
|
|
||||||
"RF _500 | 69.95 | 7.1100 \n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"# --- DISPLAY RESULTS ---\n",
|
|
||||||
"print(\"\\n=== SUMMARY ===\")\n",
|
|
||||||
"print(f\"{'Mode':<15} | {'Time (s)':<10} | {'Best f2':<10}\")\n",
|
|
||||||
"print(\"-\" * 45)\n",
|
|
||||||
"for k, v in results.items():\n",
|
|
||||||
" for i in range(len(nb_particles)):\n",
|
|
||||||
" print(f\"{k:<15}_{nb_particles[i]:<15} | {v[i][0]:<10.2f} | {v[i][1]:<10.4f}\")"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 12,
|
|
||||||
"id": "314f8d5a",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"image/png": "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 truncated
|
|
||||||
"text/plain": [
|
|
||||||
"<Figure size 1000x600 with 1 Axes>"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"metadata": {},
|
|
||||||
"output_type": "display_data"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"import matplotlib.pyplot as plt\n",
|
|
||||||
"import numpy as np\n",
|
|
||||||
"\n",
|
|
||||||
"def plot_time_benchmark(nb_particles_list, results_dict):\n",
|
|
||||||
" \n",
|
|
||||||
" t_mopso = [item[0] for item in results_dict['MOPSO']]\n",
|
|
||||||
" t_mlp = [item[0] for item in results_dict['MLP']]\n",
|
|
||||||
" t_rf = [item[0] for item in results_dict['RF']]\n",
|
|
||||||
"\n",
|
|
||||||
" plt.figure(figsize=(10, 6))\n",
|
|
||||||
" \n",
|
|
||||||
" plt.plot(nb_particles_list, t_mopso, 'o-', label='Sans IA (MOPSO)', color='#1f77b4', linewidth=2)\n",
|
|
||||||
" plt.plot(nb_particles_list, t_mlp, 's--', label='Avec MLP', color='#ff7f0e', linewidth=2)\n",
|
|
||||||
" plt.plot(nb_particles_list, t_rf, '^-.', label='Avec Random Forest', color='#2ca02c', linewidth=2)\n",
|
|
||||||
" \n",
|
|
||||||
" plt.title(\"Temps d'exécution selon le nombre de particules\", fontsize=14, fontweight='bold')\n",
|
|
||||||
" plt.xlabel(\"Nombre de Particules\", fontsize=12)\n",
|
|
||||||
" plt.ylabel(\"Temps (s)\", fontsize=12)\n",
|
|
||||||
" plt.grid(True, linestyle=':', alpha=0.7)\n",
|
|
||||||
" plt.legend(fontsize=11)\n",
|
|
||||||
" \n",
|
|
||||||
"\n",
|
|
||||||
" \n",
|
|
||||||
" plt.tight_layout()\n",
|
|
||||||
" plt.show()\n",
|
|
||||||
"\n",
|
|
||||||
" \n",
|
|
||||||
"plot_time_benchmark(nb_particles, results)"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 13,
|
|
||||||
"id": "1e3fa2ff",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAJOCAYAAABBfN/cAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd4U2X7wPFvku5dugullLJKy2pZAoooQ0UEQTaIgqAiIuB63b6+jp++KqIoiPiCioCiDFFwA7JnWaVAgVJKF7R07ybn90fooekG2qbj/lwXF8lzzknuJzlJc59naRRFURBCCCGEEEIIIUSt0Jo7ACGEEEIIIYQQojGTxFsIIYQQQgghhKhFkngLIYQQQgghhBC1SBJvIYQQQgghhBCiFkniLYQQQgghhBBC1CJJvIUQQgghhBBCiFokibcQQgghhBBCCFGLJPEWQgghhBBCCCFqkSTeQgghhBBCCCFELZLEW4ga8NBDD6HRaNBoNNx+++0m24rLNRoNy5cvV8uXL19usk2I2nL58mWcnJzQaDR06tQJRVHMHZJoBHJzc/Hw8ECj0dCqVSvy8/PNHVIZlX031xetWrVSY3z99dfNHU699frrr6uvU6tWrcwdzk1pCOeluZn7c3H77berz//QQw/V+fOLxkkSb9Hgbd261SSB1Wg03HfffeXu+9tvv5XZV75Qa8aePXuYMGECrVq1wsbGBnt7e/z8/OjRowfTpk3j888/N3eIdeb8+fNlzrPy/pX8wVVUVMS6det46qmn6N27Ny1btsTGxgYHBwc6d+7M888/z6VLl24onjfffJPMzEwAnnnmmQov9OzcuZMZM2YQEhKCi4sLlpaWuLu7c+utt/Laa69x9uzZG3p+0TjZ2tryxBNPABATE8OiRYvMHJEQoiEwd1IthLlYmDsAIWrDL7/8wrlz52jdurVJ+YIFC2rl+caNG0dISAgAfn5+tfIc9dnSpUuZMWNGmZbUnJwcLl68yIEDB/jxxx959NFHzRRh/ZecnMzIkSPLlOfn53Ps2DGOHTvGsmXL2LJlC8HBwdV+3ISEBBYvXgyAu7s7EyZMKLNPamoqU6dOZf369WW2paSksGPHDnbs2MG2bdvYunVrtZ9bNH5PPPEEb731FkVFRbz99ts8/vjjWFtbmzssIUQD99JLL5Geng5Anz59zByNEDVDEm/RKBkMBhYuXMiHH36olp0+fZpff/21Vp7vrrvu4q677qqVxza3zMxMHB0dK9x+5coVZs+erSbdLVq04IEHHsDT05PMzEyOHz/OP//8U1fhXpeMjAycnJxq/XkGDRrE4MGDy5SXd5HG0tKSgQMH0qNHDwoLC1mzZg1nzpwBjF3GH330UXbs2FHt5162bBkFBQUAjBo1CktLS5Pt2dnZDB48mAMHDqhl3t7ejBgxgpYtW5KZmcmhQ4f466+/qv2cjVV2dja2trZotdJZrJiHhwd33HEHv//+O5cvX2bt2rWMHz/e3GEJUS16vZ78/Hzs7OzMHUqjd73fn9OnT6/liIQwA0WIBm7Lli0KoP7TarUKoDg7OytZWVnqfrNmzVL30el06u0pU6aUeczExETlhRdeULp06aI4ODgo1tbWSmBgoDJz5kwlJiamzP5TpkxRH69///4m20rGtmzZMrV82bJlJttKy8vLUz755BPl1ltvVVxdXRVLS0vF29tbeeCBB5Rdu3aV2f+1115TH8vf399kW3R0tMlzbdmypcLjkpOTlZkzZyrNmzdXtFqtMn/+/HJf92IbNmwweezz58+X2aewsFD57bffyj0+NjZWee6555SuXbsqjo6OirW1teLn56cMHz5c+f3338vs/8MPPyj33HOP4uXlpVhaWiouLi7KLbfcorz//vtKdnZ2mf1Lv/7r169XbrnlFsXe3l5xdnY22feff/5Rxo4dq/j5+SlWVlaKo6Oj0rt3b2XhwoVKQUFBpa9DSaVf79dee63KYy5fvqzMmTNHSUhIMCnPyclROnToYPJ46enp1Y6lTZs26nHlvZ7/+te/TB57+PDh5b6OcXFxyuLFi8uU//nnn8qoUaOU5s2bq69Zt27dlFdffVVJSUkps7+/v7/J63LgwAFl6NChirOzs2Jra6v069dP2b59u7p/VFRUhedusZ49e6rbH3nkEZNthw8fVh5++GGldevWio2NjWJvb6907dpVeeutt0y+HyqKb/v27cqdd96pODk5KYCSmpqqKIrxnH733XeVNm3aKFZWVkrr1q2Vt956SykoKKjwM1/ses+z0o/3+++/K7fffrtib2+vODg4KHfddZdy/PjxMscpyvV/vn766SflvvvuU7y9vdXP14ABA5QVK1YoBoOh3OdYsmSJGt/AgQPL3ac8hYWFyvz585XevXsrzs7Oik6nU5o1a6Z07NhRmTx5srJq1aoyx9Tkd7OiKMrFixeVZ555RgkJCVHs7e0Va2trxd/fX5k4caKyd+/eMvuX/r5MS0tTnnnmGaVly5aKpaWlEhAQoLz11lsVvlblKX3OlXa953BFyvs7sGrVKqVnz56Kra2t4uLiojzwwAPKhQsXyj3+wIEDyuTJk5VWrVop1tbWir29vRIcHKzMmzdPiY2NLbN///79Tf7O7t27V7nzzjsVe3t7xdPTU5k5c6aSmZmpKIqifPfdd0poaKhiY2Oj+Pr6KvPmzVPy8vJMHq/0a5+RkaHMmzdPadGihWJtba0EBQUpn3zySZnXvvQ5EBMTo0yaNEnx9PRUNBqNsm7dOnXf6z2/qrJt2zalf//+ip2dneLq6qo88MADypkzZ6o8L2syjtLn1549e5RBgwYpTk5OioODgzJ48GDlwIEDZY778ssvldGjRysdOnRQ3NzcFAsLC8XR0VHp0qWL8txzzymXL1+u8rlKf38OHz7c5Bws719Fj1VaZGSkMnPmTCUoKEixt7dXbG1tlYCAAGXs2LHK/v371f1Kn4clVfY7rLLjFEVRzp49qzz55JNKhw4dFDs7O8XGxkYJCgpSnn/++XJfm8uXLytPP/200rFjR8XOzk6xtLRUvLy8lB49eihPPPGEsnv37jLHiMZHEm/R4JVOvEeMGKHe/vTTTxVFUZT09HTF0dFRAZRu3bqZfKGX/kLdtWuX4u7uXuEfBWdnZ+Wff/4xOaamE+9Lly4pXbt2rTAGrVarfPTRRybH1ETi7e7uXibJqyrx/vHHH03237BhQ6X7l/TLL7+o70t5/5566il136KiImXMmDGV/sEOCgpS4uPjTZ6j5PZbb721zHtZ7MUXX6z0sW+99dZq/8i9kcS7Mk8//bTJ4yUnJ1fruHPnzpmcMxkZGSbbCwoKTF5/b2/v6/ohP2/evEpfs+bNm5dJCEt+9nr27KlYWlqWOc7a2lo5ceKEekzJ923GjBkmj3fmzBmTY0telPrss88UCwuLCuPr2LFjmQsdJeO75ZZbTC7SwbXEe9y4ceU+5rBhwyr8zCvKjZ1nJbf37dtX0Wg0ZY5zc3NTLl26ZHLc9Xy+9Hq9Mnny5EpjGz16tFJUVFTmPDh27JjJe1c6YapIye/N8v716tXLZP+a/m7etm2b4urqWuHjabVa5YMPPjA5puT3pZubmxIUFFTusa+88kq1XgNFqTzBuJFzuCKlv5f69etX7mO2bdtWyc3NNTl2/vz56kXtil770hfFSiYuwcHBirW1dZnjbr/9duX9998v9zEnT55c4Wvv5eWldO/evdzjnnzySZPjSp4Dbdu2Vby9vU32L068b+T8qszGjRvLfe+aNWum3HLLLRWelzUdR8nzq1+/fuV+59ra2ppc8FQURQkLC6v089m8eXMlLi6uwucq7/uzphLvpUuXKlZWVhU+RsnfLbWReK9fv16xs7Or9LUp+TcsNzdXad++faX1fv7556v5joqGTLqai0Zn4sSJ7Nixg+TkZBYuXMjMmTNZtmyZOrnU7NmzK5zMIyMjgxEjRpCcnAyAv78/Y8eOxdbWlh9++IGIiAjS09MZNWoUUVFRODs710odJk+ezOHDhwFwdHRkwoQJtGjRgp07d/Lrr79iMBiYO3cu3bt3p2/fvjX2vMnJySQnJzNw4ED69u3L5cuX8fLyqvSYrl27otFo1K7mw4cPp3Xr1vTu3ZvQ0FBuvfVWevToUWZCr5iYGEaPHk1OTg6AOile165duXz5Mn///bfJ/m+//Tbff/+9er93794MHjyYyMhI1qxZA0BkZCQTJ04sc2yx7du34+7uzrhx43BzcyMiIgKA1atX8/bbb6v7DRkyhL59+5KUlMRXX31FVlYW27dvZ+7cuSxZsqQ6L6WJXbt28f7775cpv/vuu6s1XvvkyZPq7datW+Pm5lat592+fbt6u127dmWGDOzfv1/9XACMHTsWe3v7aj32N998YzKUIzg4mPvvv5/4+Hi++uor9Ho9cXFxjBw5koiICCwsyv652bdvHy1atGDixInExsaycuVKwDiufcGCBerY9Icfflityw8//MDChQvVLvOrVq1SH69Dhw7ccsstgPE1nzVrFgaDATCeL3fddReZmZl89dVXJCcnc+LECR588EF+//33cuu4e/du7OzsmDRpEs2bNyc8PBydTscPP/zA6tWr1f1at27NuHHjuHDhAt9++22Fr1lNnGc7d+6kQ4cOjBw5ksOHD7Np0ybAOBb/yy+/5F//+hdw/Z+v9957j2+++Ubdd9SoUXTp0oXo6Gi++eYbddhD165defHFF02ODQoKwt7enuzsbPLz89m3bx+33nprha8DQFZWFitWrFDvjxo1itDQUNLT04mJiWHbtm0m+9f0d3NaWhojR44kNTUVME4U9/DDD+Pk5MSqVauIiYnBYDDwzDPPEBYWRv/+/cs8RkpKCqmpqTz44IP4+vqydOlSNb4FCxbw8ssvY2VlVWkclamJc7gyO3bsoEePHgwZMoQtW7awc+dOAKKioli/fj3jxo0D4J9//mHevHnqd3zLine truncated
|
|
||||||
"text/plain": [
|
|
||||||
"<Figure size 1000x600 with 1 Axes>"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"metadata": {},
|
|
||||||
"output_type": "display_data"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"import matplotlib.pyplot as plt\n",
|
|
||||||
"\n",
|
|
||||||
"def plot_f2_benchmark(nb_particles_list, results_dict):\n",
|
|
||||||
" s_mopso = [item[1] for item in results_dict['MOPSO']]\n",
|
|
||||||
" s_mlp = [item[1] for item in results_dict['MLP']]\n",
|
|
||||||
" s_rf = [item[1] for item in results_dict['RF']]\n",
|
|
||||||
"\n",
|
|
||||||
"\n",
|
|
||||||
" plt.figure(figsize=(10, 6))\n",
|
|
||||||
" \n",
|
|
||||||
" plt.plot(nb_particles_list, s_mopso, 'o-', label='Sans IA (MOPSO)', color='#1f77b4', linewidth=2)\n",
|
|
||||||
" plt.plot(nb_particles_list, s_mlp, 's--', label='Avec MLP', color='#ff7f0e', linewidth=2)\n",
|
|
||||||
" plt.plot(nb_particles_list, s_rf, '^-.', label='Avec Random Forest', color='#2ca02c', linewidth=2)\n",
|
|
||||||
" \n",
|
|
||||||
" plt.title(\"Meilleur Score F2 (Convergence) selon le nombre de particules\", fontsize=14, fontweight='bold')\n",
|
|
||||||
" plt.xlabel(\"Nombre de Particules (log scale)\", fontsize=12)\n",
|
|
||||||
" plt.ylabel(\"Meilleur F2 Score\", fontsize=12)\n",
|
|
||||||
" plt.grid(True, linestyle=':', alpha=0.7)\n",
|
|
||||||
" plt.legend(fontsize=11)\n",
|
|
||||||
" \n",
|
|
||||||
" plt.xscale('log') \n",
|
|
||||||
" \n",
|
|
||||||
" plt.tight_layout()\n",
|
|
||||||
" plt.show()\n",
|
|
||||||
"\n",
|
|
||||||
"plot_f2_benchmark(nb_particles, results)"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"kernelspec": {
|
|
||||||
"display_name": "Optim_Metaheuristique (3.11.14)",
|
|
||||||
"language": "python",
|
|
||||||
"name": "python3"
|
|
||||||
},
|
|
||||||
"language_info": {
|
|
||||||
"codemirror_mode": {
|
|
||||||
"name": "ipython",
|
|
||||||
"version": 3
|
|
||||||
},
|
|
||||||
"file_extension": ".py",
|
|
||||||
"mimetype": "text/x-python",
|
|
||||||
"name": "python",
|
|
||||||
"nbconvert_exporter": "python",
|
|
||||||
"pygments_lexer": "ipython3",
|
|
||||||
"version": "3.11.14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nbformat": 4,
|
|
||||||
"nbformat_minor": 5
|
|
||||||
}
|
|
||||||
+15
-26
@@ -1,14 +1,12 @@
|
|||||||
import random as rd
|
import random as rd
|
||||||
import copy
|
|
||||||
|
|
||||||
class Particle():
|
class Particle():
|
||||||
def __init__(self, socs:list, times:list, nb_vehicles:int=10, delta_t:int=60, nb_of_ticks:int=72, x_min=-100, x_max=100, alpha=0.1):
|
def __init__(self,socs:list, nb_vehicles:int=10, delta_t:int=60, nb_of_ticks:int=72, x_min=-100, x_max=100, alpha=0.1):
|
||||||
# Problem specific attributes
|
# Problem specific attributes
|
||||||
self.nb_vehicles = nb_vehicles # Number of vehicles handles for the generations of position x
|
self.nb_vehicles = nb_vehicles # Number of vehicles handles for the generations of position x
|
||||||
self.delta_t = delta_t # delta_t for update purposes
|
self.delta_t = delta_t # delta_t for update purposes
|
||||||
self.nb_of_ticks = nb_of_ticks # Accounting for time evolution of the solution (multiplied by delta_t)
|
self.nb_of_ticks = nb_of_ticks # Accounting for time evolution of the solution (multiplied by delta_t)
|
||||||
self.socs = socs # States of charges for the particle current position (self.x)
|
self.socs = socs # States of charges for the particle current position (self.x)
|
||||||
self.times = times
|
|
||||||
|
|
||||||
# Minima and maxima of a position value
|
# Minima and maxima of a position value
|
||||||
self.x_min = x_min
|
self.x_min = x_min
|
||||||
@@ -65,12 +63,17 @@ class Particle():
|
|||||||
self.x[tick][i] = self.x[tick][i] * 0.9
|
self.x[tick][i] = self.x[tick][i] * 0.9
|
||||||
current_power = self.get_current_grid_stress(tick)
|
current_power = self.get_current_grid_stress(tick)
|
||||||
|
|
||||||
|
def update_socs(self, capacities):
|
||||||
|
for tick in range(self.nb_of_ticks):
|
||||||
|
for i in range(self.nb_vehicles-1):
|
||||||
|
self.socs[tick][i+1] = self.socs[tick][i] + (self.x[tick][i] / capacities[i])
|
||||||
|
|
||||||
def generate_position(self):
|
def generate_position(self):
|
||||||
pos = []
|
pos = []
|
||||||
for _ in range(self.nb_of_ticks):
|
for _ in range(self.nb_of_ticks):
|
||||||
x_tick = []
|
x_tick = []
|
||||||
for _ in range(self.nb_vehicles):
|
for _ in range(self.nb_vehicles):
|
||||||
x_tick.append(rd.uniform(self.x_min, self.x_max))
|
x_tick.append(rd.randrange(self.x_min, self.x_max +1, 1))
|
||||||
pos.append(x_tick)
|
pos.append(x_tick)
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
@@ -81,15 +84,14 @@ class Particle():
|
|||||||
for _ in range(self.nb_of_ticks):
|
for _ in range(self.nb_of_ticks):
|
||||||
v_tick = []
|
v_tick = []
|
||||||
for _ in range(self.nb_vehicles):
|
for _ in range(self.nb_vehicles):
|
||||||
# v_tick.append(rd.randrange(-vel_coeff, vel_coeff +1, 1) * self.alpha)
|
v_tick.append(rd.randrange(-vel_coeff, vel_coeff +1, 1) * self.alpha)
|
||||||
v_tick.append(rd.uniform(-vel_coeff, vel_coeff) * self.alpha)
|
|
||||||
vel.append(v_tick)
|
vel.append(v_tick)
|
||||||
return vel
|
return vel
|
||||||
|
|
||||||
# Function objective
|
# Function objective
|
||||||
def evaluate(self,elec_prices,socs,socs_req,times):
|
def evaluate(self,elec_prices,socs,socs_req,times):
|
||||||
f1 = self.f1(elec_prices)
|
f1 = self.f1(elec_prices)
|
||||||
f2 = self.f2(self.socs,socs_req,times)
|
f2 = self.f2(socs,socs_req,times)
|
||||||
f3 = self.f3()
|
f3 = self.f3()
|
||||||
|
|
||||||
# Keeping in memory evaluation of each objective for domination evaluation
|
# Keeping in memory evaluation of each objective for domination evaluation
|
||||||
@@ -101,13 +103,13 @@ class Particle():
|
|||||||
self.f_current = f_current
|
self.f_current = f_current
|
||||||
|
|
||||||
def update_best(self):
|
def update_best(self):
|
||||||
current_better = (self.f_current[0] <= self.f_best[0]) and (self.f_current[1] <= self.f_best[1]) and (self.f_current[2] <= self.f_best[2])
|
current_better = (self.f_current[0] >= self.f_best[0]) and (self.f_current[1] >= self.f_best[1]) and (self.f_current[2] >= self.f_best[2])
|
||||||
if current_better:
|
if current_better:
|
||||||
# Not strict superiority yet
|
# Not strict superiority yet
|
||||||
current_dominates = (self.f_current[0] < self.f_best[0]) or (self.f_current[1] < self.f_best[1]) or (self.f_current[2] < self.f_best[2])
|
current_dominates = (self.f_current[0] > self.f_best[0]) or (self.f_current[1] > self.f_best[1]) or (self.f_current[2] > self.f_best[2])
|
||||||
if current_dominates:
|
if current_dominates:
|
||||||
self.p_best = copy.deepcopy(self.x)
|
self.p_best = self.x
|
||||||
self.f_best = self.f_current[:]
|
self.f_best = self.f_current
|
||||||
|
|
||||||
# Calculate the price of the electricity consumption in the grid SUM(1_to_T)(Epsilon_t * A_t * delta_t)
|
# Calculate the price of the electricity consumption in the grid SUM(1_to_T)(Epsilon_t * A_t * delta_t)
|
||||||
def f1(self,elec_prices):
|
def f1(self,elec_prices):
|
||||||
@@ -140,18 +142,5 @@ class Particle():
|
|||||||
current_grid_stress += self.x[tick][i]
|
current_grid_stress += self.x[tick][i]
|
||||||
return current_grid_stress
|
return current_grid_stress
|
||||||
|
|
||||||
def updating_socs(self, initial_socs, capacities):
|
def updating_socs(self, socs, capacities):
|
||||||
|
pass
|
||||||
# Calcul de l'évolution temporelle
|
|
||||||
for tick in range(self.nb_of_ticks - 1): # On s'arrête à l'avant-dernier pour calculer le suivant
|
|
||||||
for i in range(self.nb_vehicles):
|
|
||||||
# SoC(t+1) = SoC(t) + (Puissance(t) * delta_t / Capacité)
|
|
||||||
# Attention: x est en kW, delta_t en minutes -> conversion en heures (/60) si capacité en kWh
|
|
||||||
energy_added = (self.x[tick][i] * (self.delta_t / 60))
|
|
||||||
|
|
||||||
# Mise à jour du tick suivant basé sur le tick actuel
|
|
||||||
# On utilise initial_socs comme base si c'est une liste de listes [tick][vehicule]
|
|
||||||
self.socs[tick+1][i] = self.socs[tick][i] + (energy_added / capacities[i])
|
|
||||||
|
|
||||||
# Bornage entre 0 et 1 (0% et 100%)
|
|
||||||
self.socs[tick+1][i] = max(0.0, min(1.0, self.socs[tick+1][i]))
|
|
||||||
@@ -5,8 +5,5 @@ description = "Metaheuristic Optimization Project"
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"matplotlib>=3.10.8",
|
|
||||||
"numpy>=2.4.1",
|
|
||||||
"pandas>=2.3.3",
|
"pandas>=2.3.3",
|
||||||
"scikit-learn>=1.8.0",
|
|
||||||
]
|
]
|
||||||
@@ -1,46 +1,14 @@
|
|||||||
# This file was autogenerated by uv via the following command:
|
# This file was autogenerated by uv via the following command:
|
||||||
# uv pip compile pyproject.toml -o uv.lock
|
# uv pip compile pyproject.toml -o uv.lock
|
||||||
contourpy==1.3.3
|
|
||||||
# via matplotlib
|
|
||||||
cycler==0.12.1
|
|
||||||
# via matplotlib
|
|
||||||
fonttools==4.61.1
|
|
||||||
# via matplotlib
|
|
||||||
joblib==1.5.3
|
|
||||||
# via scikit-learn
|
|
||||||
kiwisolver==1.4.9
|
|
||||||
# via matplotlib
|
|
||||||
matplotlib==3.10.8
|
|
||||||
# via optim-meta (pyproject.toml)
|
|
||||||
numpy==2.4.1
|
numpy==2.4.1
|
||||||
# via
|
# via pandas
|
||||||
# optim-meta (pyproject.toml)
|
|
||||||
# contourpy
|
|
||||||
# matplotlib
|
|
||||||
# pandas
|
|
||||||
# scikit-learn
|
|
||||||
# scipy
|
|
||||||
packaging==25.0
|
|
||||||
# via matplotlib
|
|
||||||
pandas==2.3.3
|
pandas==2.3.3
|
||||||
# via optim-meta (pyproject.toml)
|
# via optim-meta (pyproject.toml)
|
||||||
pillow==12.1.0
|
|
||||||
# via matplotlib
|
|
||||||
pyparsing==3.3.1
|
|
||||||
# via matplotlib
|
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
# via
|
# via pandas
|
||||||
# matplotlib
|
|
||||||
# pandas
|
|
||||||
pytz==2025.2
|
pytz==2025.2
|
||||||
# via pandas
|
# via pandas
|
||||||
scikit-learn==1.8.0
|
|
||||||
# via optim-meta (pyproject.toml)
|
|
||||||
scipy==1.17.0
|
|
||||||
# via scikit-learn
|
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
# via python-dateutil
|
# via python-dateutil
|
||||||
threadpoolctl==3.6.0
|
|
||||||
# via scikit-learn
|
|
||||||
tzdata==2025.3
|
tzdata==2025.3
|
||||||
# via pandas
|
# via pandas
|
||||||
Reference in new issue
Block a user