Spaces:
Sleeping
Sleeping
import numpy as np | |
from scipy.integrate import odeint | |
import emcee | |
import panel as pn | |
import param | |
import plotly.graph_objects as go | |
from plotly.subplots import make_subplots | |
import plotly.io as pio | |
# Définition du thème sombre | |
pio.templates.default = "plotly_dark" | |
# Initialiser l'extension Panel | |
pn.extension('katex', 'mathjax', 'plotly') | |
# Informations personnelles | |
photo_url = "PPFB.png" | |
prenom = "Yaya" | |
nom = "Toure" | |
email = "[email protected]" | |
whatsapps_url = "https://wa.me/message/GW7RWRW3GR4WN1" | |
linkedin_url = "https://www.linkedin.com/in/yaya-toure-8251a4280/" | |
github_url = "https://github.com/CodingYayaToure" | |
universite = "UNCHK" | |
formation = "Licence Analyse Numerique et Modelisation | Master Calcul scientifique et Modelisation" | |
certificat = "Collecte et Fouille de Donnees (UADB-CNAM Paris)" | |
TP_Scilab = "https://codingyayatoure.github.io/Equation_de_Transport_en_1D_par_la_Methode_des_Differences_Finis/" | |
# Créer le template du tableau de bord | |
template = pn.template.ReactTemplate(title='Progiciel de Simulation des Interactions Microbiennes en Fermentation de Jus de Légumes. | Approche Modélisation mathématique ') | |
# Classe pour la dynamique Consumer-Resource | |
class ConsumerResourceApp(param.Parameterized): | |
# Paramètres du modèle | |
r = param.Number(10.0, bounds=(0.1, 20.0), step=0.1, doc="Taux d'approvisionnement en ressources") | |
d = param.Number(0.1, bounds=(0.01, 1.0), step=0.01, doc="Taux de mortalité des consommateurs") | |
c = param.Number(0.1, bounds=(0.01, 1.0), step=0.01, doc="Coefficient de consommation") | |
K = param.Number(1.0, bounds=(0.1, 10.0), step=0.1, doc="Concentration à laquelle la croissance est à moitié maximale") | |
X0 = param.Number(1.0, bounds=(0.1, 10.0), step=0.1, doc="Population initiale des consommateurs") | |
S0 = param.Number(5.0, bounds=(0.1, 20.0), step=0.1, doc="Concentration initiale de la ressource") | |
def view(self): | |
# Fonction de croissance dépendante de la ressource (Monod) | |
def mu(S): | |
return S / (self.K + S) | |
# Équations différentielles | |
def model(y, t): | |
X, S = y | |
dXdt = mu(S) * X - self.d * X # Dynamique des consommateurs | |
dSdt = self.r - self.c * mu(S) * X # Dynamique des ressources | |
return [dXdt, dSdt] | |
# Conditions initiales | |
y0 = [self.X0, self.S0] | |
# Temps pour la simulation (en heures) | |
t_hours = np.linspace(0, 100, 500) | |
# Résoudre le système d'équations différentielles | |
solution = odeint(model, y0, t_hours) | |
# Créer les graphiques avec Plotly en utilisant des sous-graphiques | |
fig = make_subplots( | |
rows=1, cols=3, | |
specs=[[{'type': 'scatter'}, {'type': 'scatter'}, {'type': 'scatter'}]], | |
subplot_titles=["Population des Consommateurs (X)", "Concentration de la Ressource (S)", "Population et Ressource sur le même repère"] | |
) | |
# Ajouter la trace pour les consommateurs | |
fig.add_trace(go.Scatter(x=t_hours, y=solution[:, 0], mode='lines', name='Population des Consommateurs (X)', line=dict(color='blue')), row=1, col=1) | |
# Ajouter la trace pour les ressources | |
fig.add_trace(go.Scatter(x=t_hours, y=solution[:, 1], mode='lines', name='Concentration de la Ressource (S)', line=dict(color='green')), row=1, col=2) | |
# Ajouter la trace pour les consommateurs et les ressources sur le même graphique | |
fig.add_trace(go.Scatter(x=t_hours, y=solution[:, 0], mode='lines', name='Population des Consommateurs (X)', line=dict(color='blue')), row=1, col=3) | |
fig.add_trace(go.Scatter(x=t_hours, y=solution[:, 1], mode='lines', name='Concentration de la Ressource (S)', line=dict(color='green')), row=1, col=3) | |
# Mettre à jour la mise en page du graphique | |
fig.update_layout( | |
title='Dynamique du Modèle Consumer-Resource', | |
xaxis_title='Temps', | |
yaxis_title='Population / Concentration', | |
legend_title='Légende', | |
height=400, | |
width=1500, | |
font_color="white", | |
template='plotly_dark' | |
) | |
return pn.pane.Plotly(fig) | |
def panel_view(self): | |
return pn.Column( | |
pn.Row(pn.Param(self, width=400), self.formulas()), # Aligner les widgets et les formules sur la même ligne | |
self.view # Appeler la vue pour qu'elle se mette à jour dynamiquement | |
) | |
def formulas(self): | |
return pn.Column( | |
pn.pane.LaTeX(r""" | |
$$ | |
\text{Fonction de croissance : } \mu(S) = \frac{S}{K + S} | |
$$ | |
""", styles={'font-size': '16pt'}), | |
pn.pane.LaTeX(r""" | |
$$ | |
\text{Dynamique des consommateurs : } \frac{dX}{dt} = \mu(S) \times X - d \times X | |
$$ | |
""", styles={'font-size': '16pt'}), | |
pn.pane.LaTeX(r""" | |
$$ | |
\text{Dynamique des ressources : } \frac{dS}{dt} = r - c \times \mu(S) \times X | |
$$ | |
""", styles={'font-size': '16pt'}) | |
) | |
# Classe pour la dynamique des populations microbiennes | |
class MicrobialDynamicsApp(param.Parameterized): | |
# Paramètres du modèle pour la dynamique des populations microbiennes | |
r1 = param.Number(0.5, bounds=(0.1, 1.0), step=0.01, doc="Taux de croissance espèce 1") | |
r2 = param.Number(0.3, bounds=(0.1, 1.0), step=0.01, doc="Taux de croissance espèce 2") | |
K1 = param.Number(100, bounds=(10, 200), step=1, doc="Capacité de charge espèce 1") | |
K2 = param.Number(80, bounds=(10, 200), step=1, doc="Capacité de charge espèce 2") | |
alpha = param.Number(0.01, bounds=(0.001, 0.1), step=0.001, doc="Compétition espèce 2 sur espèce 1") | |
beta = param.Number(0.01, bounds=(0.001, 0.1), step=0.001, doc="Compétition espèce 1 sur espèce 2") | |
def view(self): | |
# Équations différentielles pour les populations microbiennes | |
def model(y, t): | |
X1, X2 = y | |
dX1dt = self.r1 * X1 * (1 - (X1 + self.alpha * X2) / self.K1) | |
dX2dt = self.r2 * X2 * (1 - (X2 + self.beta * X1) / self.K2) | |
return [dX1dt, dX2dt] | |
# Conditions initiales | |
y0 = [10, 5] # Population initiale des deux espèces | |
# Temps pour la simulation | |
t = np.linspace(0, 50, 500) | |
# Résoudre le système d'équations différentielles | |
solution = odeint(model, y0, t) | |
# Créer le graphique avec Plotly | |
fig = go.Figure() | |
fig.add_trace(go.Scatter(x=t, y=solution[:, 0], mode='lines', name='Espèce 1', line=dict(color='red'))) | |
fig.add_trace(go.Scatter(x=t, y=solution[:, 1], mode='lines', name='Espèce 2', line=dict(color='purple'))) | |
# Mettre à jour la mise en page du graphique | |
fig.update_layout( | |
title='Dynamique des Populations Microbiennes', | |
xaxis_title='Temps', | |
yaxis_title='Population', | |
legend_title='Légende', | |
height=400, | |
width=1500, | |
font_color="white", | |
template='plotly_dark' | |
) | |
return pn.pane.Plotly(fig) | |
def panel_view(self): | |
return pn.Column( | |
pn.Row(pn.Param(self, width=400), self.formulas()), # Aligner les widgets et les formules sur la même ligne | |
self.view # Appeler la vue pour qu'elle se mette à jour dynamiquement | |
) | |
def formulas(self): | |
return pn.Column( | |
pn.pane.LaTeX(r""" | |
$$ | |
\frac{{dX_1}}{{dt}} = r_1 X_1 \left( 1 - \frac{{X_1 + \alpha X_2}}{{K_1}} \right) | |
$$ | |
""", styles={'font-size': '16pt'}), | |
pn.pane.LaTeX(r""" | |
$$ | |
\frac{{dX_2}}{{dt}} = r_2 X_2 \left( 1 - \frac{{X_2 + \beta X_1}}{{K_2}} \right) | |
$$ | |
""", styles={'font-size': '16pt'}) | |
) | |
# Classe pour la modélisation de la fermentation | |
class FermentationModelApp(param.Parameterized): | |
# Paramètres du modèle de fermentation | |
mu_max = param.Number(0.8, bounds=(0.1, 2.0), step=0.1, doc="Taux de croissance maximal") | |
Ks = param.Number(10.0, bounds=(1.0, 50.0), step=1.0, doc="Constante de demi-saturation") | |
Yxs = param.Number(0.5, bounds=(0.1, 1.0), step=0.1, doc="Rendement de croissance sur substrat") | |
Sin = param.Number(20.0, bounds=(10.0, 100.0), step=1.0, doc="Concentration initiale en substrat") | |
X0 = param.Number(0.1, bounds=(0.01, 5.0), step=0.01, doc="Concentration initiale en biomasse") | |
P0 = param.Number(0.0, bounds=(0.0, 5.0), step=0.1, doc="Concentration initiale en produit") | |
def view(self): | |
# Équations différentielles pour la fermentation | |
def model(y, t): | |
X, S, P = y | |
mu = self.mu_max * S / (self.Ks + S) | |
dXdt = mu * X | |
dSdt = -dXdt / self.Yxs | |
dPdt = 0.1 * dXdt # Production de produit proportionnelle à la croissance | |
return [dXdt, dSdt, dPdt] | |
# Conditions initiales | |
y0 = [self.X0, self.Sin, self.P0] | |
# Temps pour la simulation | |
t = np.linspace(0, 50, 500) | |
# Résoudre le système d'équations différentielles | |
solution = odeint(model, y0, t) | |
# Créer le graphique avec Plotly | |
fig = go.Figure() | |
fig.add_trace(go.Scatter(x=t, y=solution[:, 0], mode='lines', name='Biomasse (X)', line=dict(color='blue'))) | |
fig.add_trace(go.Scatter(x=t, y=solution[:, 1], mode='lines', name='Substrat (S)', line=dict(color='green'))) | |
fig.add_trace(go.Scatter(x=t, y=solution[:, 2], mode='lines', name='Produit (P)', line=dict(color='orange'))) | |
# Mettre à jour la mise en page du graphique | |
fig.update_layout( | |
title='Modélisation de la Fermentation', | |
xaxis_title='Temps', | |
yaxis_title='Concentration', | |
legend_title='Légende', | |
height=400, | |
width=1500, | |
font_color="white", | |
template='plotly_dark' | |
) | |
return pn.pane.Plotly(fig) | |
def formulas(self): | |
return pn.Column( | |
pn.pane.LaTeX(r""" | |
$$ | |
\mu = \frac{{\mu_{{\text{{max}}}} \cdot S}}{{K_s + S}} | |
$$ | |
""", styles={'font-size': '16pt'}), | |
pn.pane.LaTeX(r""" | |
$$ | |
\frac{{dX}}{{dt}} = \mu \cdot X | |
$$ | |
""", styles={'font-size': '16pt'}), | |
pn.pane.LaTeX(r""" | |
$$ | |
\frac{{dS}}{{dt}} = -\frac{1}{{Y_{{xs}}}} \cdot \frac{{dX}}{{dt}} | |
$$ | |
""", styles={'font-size': '16pt'}), | |
pn.pane.LaTeX(r""" | |
$$ | |
\frac{{dP}}{{dt}} = 0.1 \cdot \frac{{dX}}{{dt}} | |
$$ | |
""", styles={'font-size': '16pt'}) | |
) | |
def panel_view(self): | |
return pn.Column( | |
pn.Row(pn.Param(self, width=400), self.formulas()), # Aligner les widgets et les formules sur la même ligne | |
self.view # Appeler la vue pour qu'elle se mette à jour dynamiquement | |
) | |
# Classe pour l'estimation des paramètres par MCMC | |
class EstimationParametresFermentation(param.Parameterized): | |
# Paramètres pour l'estimation MCMC | |
n_marcheurs = param.Integer(32, bounds=(10, 100), doc="Nombre de marcheurs MCMC") | |
n_etapes = param.Integer(1000, bounds=(100, 5000), doc="Nombre d'étapes MCMC") | |
niveau_bruit = param.Number(0.1, bounds=(0.01, 0.5), doc="Niveau de bruit dans les données synthétiques") | |
def __init__(self, **params): | |
super().__init__(**params) | |
self.parametres_reels = { | |
'mu_max': 0.8, | |
'Ks': 10.0, | |
'Yxs': 0.5 | |
} | |
def generer_donnees_synthetiques(self, t): | |
def modele(y, t, mu_max, Ks, Yxs): | |
X, S, P = y | |
mu = mu_max * S / (Ks + S) | |
dXdt = mu * X | |
dSdt = -dXdt / Yxs | |
dPdt = 0.1 * dXdt | |
return [dXdt, dSdt, dPdt] | |
y0 = [0.1, 20.0, 0.0] | |
solution = odeint(modele, y0, t, args=(self.parametres_reels['mu_max'], self.parametres_reels['Ks'], self.parametres_reels['Yxs'])) | |
bruit = np.random.normal(0, self.niveau_bruit, solution.shape) | |
return solution + bruit | |
def log_vraisemblance(self, theta, t, donnees): | |
mu_max, Ks, Yxs = theta | |
def modele(y, t, mu_max, Ks, Yxs): | |
X, S, P = y | |
mu = mu_max * S / (Ks + S) | |
dXdt = mu * X | |
dSdt = -dXdt / Yxs | |
dPdt = 0.1 * dXdt | |
return [dXdt, dSdt, dPdt] | |
y0 = [0.1, 20.0, 0.0] | |
try: | |
solution = odeint(modele, y0, t, args=(mu_max, Ks, Yxs)) | |
sigma2 = self.niveau_bruit ** 2 | |
return -0.5 * np.sum((donnees - solution) ** 2 / sigma2 + np.log(2 * np.pi * sigma2)) | |
except: | |
return -np.inf | |
def log_apriori(self, theta): | |
mu_max, Ks, Yxs = theta | |
if 0.1 < mu_max < 2.0 and 1.0 < Ks < 50.0 and 0.1 < Yxs < 1.0: | |
return 0.0 | |
return -np.inf | |
def log_probabilite(self, theta, t, donnees): | |
lp = self.log_apriori(theta) | |
if not np.isfinite(lp): | |
return -np.inf | |
return lp + self.log_vraisemblance(theta, t, donnees) | |
def lancer_mcmc(self): | |
t = np.linspace(0, 50, 50) | |
donnees = self.generer_donnees_synthetiques(t) | |
ndim = 3 | |
pos = [ | |
[self.parametres_reels['mu_max'], self.parametres_reels['Ks'], self.parametres_reels['Yxs']] + 1e-4 * np.random.randn(ndim) | |
for i in range(self.n_marcheurs) | |
] | |
sampler = emcee.EnsembleSampler(self.n_marcheurs, ndim, self.log_probabilite, args=(t, donnees)) | |
sampler.run_mcmc(pos, self.n_etapes, progress=True) | |
return sampler, t, donnees | |
def vue(self): | |
sampler, t, donnees = self.lancer_mcmc() | |
fig = make_subplots(rows=2, cols=2, | |
subplot_titles=('Chaînes MCMC', 'Distribution postérieure', 'Ajustement du modèle', 'Corrélations des paramètres')) | |
echantillons = sampler.get_chain() | |
# Tracer les chaînes MCMC | |
for i in range(3): | |
fig.add_trace(go.Scatter(y=echantillons[:, 0, i], mode='lines', name=f'Paramètre {i+1}'), row=1, col=1) | |
echantillons_plats = sampler.get_chain(discard=100, thin=15, flat=True) | |
# Tracer les distributions postérieures | |
for i in range(3): | |
fig.add_trace(go.Histogram(x=echantillons_plats[:, i], name=f'Paramètre {i+1}', nbinsx=30), row=1, col=2) | |
params_med = np.median(echantillons_plats, axis=0) | |
y0 = [0.1, 20.0, 0.0] | |
# Définir une fonction de modèle pour odeint | |
def modele(y, t): | |
mu_max, Ks, Yxs = params_med | |
X, S, P = y | |
mu = mu_max * S / (Ks + S) | |
dXdt = mu * X | |
dSdt = -dXdt / Yxs | |
dPdt = 0.1 * dXdt | |
return [dXdt, dSdt, dPdt] | |
# Utiliser la fonction de modèle dans odeint | |
solution = odeint(modele, y0, t) | |
fig.add_trace(go.Scatter(x=t, y=donnees[:, 0], mode='markers', name='Données (X)', marker=dict(color='blue')), row=2, col=1) | |
fig.add_trace(go.Scatter(x=t, y=solution[:, 0], mode='lines', name='Modèle (X)', line=dict(color='red')), row=2, col=1) | |
# Tracer les corrélations des paramètres | |
fig.add_trace(go.Scatter(x=echantillons_plats[:, 0], y=echantillons_plats[:, 1], | |
mode='markers', marker=dict(size=2), | |
name='mu_max vs Ks'), row=2, col=2) | |
fig.update_layout(height=400, width=1200, | |
showlegend=True, | |
template='plotly_dark') | |
return pn.pane.Plotly(fig) | |
def vue_panneau(self): | |
return pn.Column( | |
pn.Row( | |
pn.Param(self.param, | |
widgets={ | |
'n_marcheurs': pn.widgets.IntSlider, | |
'n_etapes': pn.widgets.IntSlider, | |
'niveau_bruit': pn.widgets.FloatSlider}), | |
self.description_mcmc() | |
), | |
self.vue # Correction pour supprimer '()' et référencer dynamiquement | |
) | |
def description_mcmc(self): | |
return pn.pane.Markdown(""" | |
# Estimation des paramètres par MCMC | |
## Cette section permet d'estimer les paramètres du modèle de fermentation en utilisant l'algorithme MCMC (Markov Chain Monte Carlo). | |
- ## **n_marcheurs**: Nombre de chaînes MCMC parallèles. | |
- ## **n_etapes**: Nombre d'itérations pour chaque chaîne. | |
- ## **niveau_bruit**: Niveau de bruit dans les données synthétiques. | |
## Les graphiques montrent: | |
### 1. L'évolution des chaînes MCMC. | |
### 2. Les distributions postérieures des paramètres. | |
### 3. L'ajustement du modèle aux données. | |
### 4. Les corrélations entre les paramètres. | |
""") | |
# Informations personnelles et photo | |
photo = pn.pane.PNG(photo_url, width=200, height=200) | |
info = pn.Column( | |
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Prénom</strong>: {prenom}</div>"), | |
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Nom</strong>: {nom}</div>"), | |
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Email</strong>: <a href='mailto:{email}'>{email}</a></div>"), | |
#pn.pane.Markdown(f"<div style='font-size:16px'><strong>WhatsApp</strong>: <a href='{whatsapps_url}'>WhatsApp</a></div>"), | |
#pn.pane.Markdown(f"<div style='font-size:16px'><strong>LinkedIn</strong>: <a href='{linkedin_url}'>LinkedIn</a></div>"), | |
# pn.pane.Markdown(f"<div style='font-size:16px'><strong>GitHub</strong>: <a href='{github_url}'>GitHub</a></div>"), | |
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Université</strong>: {universite}</div>"), | |
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Formation</strong>: {formation}</div>"), | |
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Certificat</strong>: {certificat}</div>"), | |
pn.pane.Markdown(f"<div style='font-size:16px'><strong>Autre Projet </strong>: <a href='{TP_Scilab}'>Pour comprendre l'équation de Transport en 1D avec Scilab</a></div>"), | |
sizing_mode='stretch_width' | |
) | |
# Créer les applications | |
consumer_app = ConsumerResourceApp() | |
microbial_app = MicrobialDynamicsApp() | |
ferm_app = FermentationModelApp() | |
# Créer l'application | |
application_estimation_parametres = EstimationParametresFermentation() | |
# Afficher l'application Panel | |
#template = pn.template.ReactTemplate(title='Estimation des paramètres par MCMC') | |
#template.main[0, 0] = application_estimation_parametres.vue_panneau() | |
# Créer la mise en page | |
layout = pn.Row( | |
pn.Column(photo, info, width=300), # Ajouter la photo et les informations personnelles à gauche | |
pn.Column( | |
pn.Tabs( | |
('Consumer-Resource Dynamics', consumer_app.panel_view()), | |
('Microbial Dynamics', microbial_app.panel_view()), | |
(' Fermentation', ferm_app.panel_view()), | |
('Estimation Parametres', application_estimation_parametres.vue_panneau()) | |
) | |
) | |
) | |
# Ajouter le layout au template principal | |
template.main[0, 0] = layout | |
# Exécuter l'application | |
template.servable() | |