title: Du Vensim au Python
url: https://broco.ga/python.html
hash_url: ec4d74dd36e0dd7c730a567078e26e49

<h2 id="quelques-différences">Quelques différences…</h2>
<p>J’ai réussi aujourd’hui à porter une première version de World3 en Python depuis le modèle Vensim. J’ai noté deux différences techniques :</p>
<ul>
<li>Vensim est un <em>langage de programmation graphique</em>, comme la plupart des langages permettant de modéliser de la dynamique des systèmes (que je vais désormais abréger <em>SD</em> pour <em>System Dynamics</em>). Python, plus classique, a une syntaxe textuelle. Les fichiers Vensim peuvent cependant tout aussi bien être stockés dans un format binaire (.vdl) que dans un format textuel (.mdl). C’est ce dernier que PySD est capable de traduire en Python. Il est possible de convertir l’un en l’autre depuis le logiciel Vensim.</li>
</ul>
<p>Voici, par exemple, le code généré par PySD représentant le fameux <a href="deladds.html">modèle de la tasse de thé</a> :</p>
<div class="sourceCode"><pre class="sourceCode python"><code class="sourceCode python"><span class="co"># Le code généré par PySD est commenté </span>
<span class="co"># j&#39;ai enlevé les commentaires pour réduire la place que prend le code</span>
<span class="at">@cache</span>(<span class="st">&#39;run&#39;</span>)
<span class="kw">def</span> final_time():
    <span class="cf">return</span> <span class="dv">30</span>

<span class="at">@cache</span>(<span class="st">&#39;run&#39;</span>)
<span class="kw">def</span> room_temperature():
    <span class="cf">return</span> <span class="dv">70</span>

<span class="at">@cache</span>(<span class="st">&#39;run&#39;</span>)
<span class="kw">def</span> characteristic_time():
    <span class="cf">return</span> <span class="dv">10</span>

<span class="at">@cache</span>(<span class="st">&#39;run&#39;</span>)
<span class="kw">def</span> initial_time():
    <span class="cf">return</span> <span class="dv">0</span>

<span class="at">@cache</span>(<span class="st">&#39;run&#39;</span>)
<span class="kw">def</span> time_step():
    <span class="cf">return</span> <span class="fl">0.125</span>

<span class="at">@cache</span>(<span class="st">&#39;step&#39;</span>)
<span class="kw">def</span> teacup_temperature():
    <span class="cf">return</span> integ_teacup_temperature()

integ_teacup_temperature <span class="op">=</span> functions.Integ(<span class="kw">lambda</span>: <span class="op">-</span>heat_loss_to_room(), <span class="kw">lambda</span>: <span class="dv">180</span>)

<span class="at">@cache</span>(<span class="st">&#39;step&#39;</span>)
<span class="kw">def</span> saveper():
    <span class="cf">return</span> time_step()

<span class="at">@cache</span>(<span class="st">&#39;step&#39;</span>)
<span class="kw">def</span> heat_loss_to_room():
    <span class="cf">return</span> (teacup_temperature() <span class="op">-</span> room_temperature()) <span class="op">/</span> characteristic_time()</code></pre></div>
<p>En comparaison, voici le texte contenu dans un fichier .mdl (Vensim) :</p>
<pre><code>Characteristic Time=
    10
    ~   Minutes [0,?]
    ~   How long will it take the teacup to cool 1/e of the way to equilibrium?
    |

Heat Loss to Room=
    (Teacup Temperature - Room Temperature) / Characteristic Time
    ~   Degrees Fahrenheit/Minute
    ~   This is the rate at which heat flows from the cup into the room. We can \
        ignore it at this point.
    |

Room Temperature=
    70
    ~   Degrees Fahrenheit [-459.67,?]
    ~   Put in a check to ensure the room temperature is not driven below absolute \
        zero.
    |

Teacup Temperature= INTEG (
    -Heat Loss to Room,
        180)
    ~   Degrees Fahrenheit [32,212]
    ~   The model is only valid for the liquid phase of tea. While the tea could \
        theoretically freeze or boil off, we would want an error to be thrown in \
        these cases so that the modeler can identify the issue and decide whether \
        to expand the model.
        Of course, this refers to standard sea-level conditions...
    |</code></pre>
<p>Le passage de l’un à l’autre se fait à l’aide d’un <em>parser</em>, un programme qui, dans un texte, compare des syntaxes, repère les éléments clés puis les traduits. Le parser utilisé par PySD est la bibliothèque <a href="https://github.com/erikrose/parsimonious">parsimonious</a>.</p>
<p>Chaque variable est représentée par une fonction qui prend soit une valeur constante, soit une valeur dépendante d’autres variables. On peut remarquer les entêtes <code>@cache</code> au début avant chaque fonction : elle prend le paramètre <code>'run'</code> quand la variable est constante (la variable est conservée une bonne fois pour toute du début jusqu’à la fin d’une simulation), <code>'step'</code> quand la variable dépend des autres (la variable n’est conservée que la durée de l’état courant, pour calculer d’autres variables qui en ont besoin).</p>
<p>On peut aussi remarquer que les relations entre variables peuvent être simples et donc directement écrites au sein de la fonction représentant la variable (par exemple dans <code>heat_loss_to_room</code>), ou plus complexes et faisant appel à des implémentations propres à PySD (<code>integ_teacup_temperature</code>). Ces implémentations sont caractéristiques du logiciel de modélisation et peuvent différer d’un modèle à l’autre, au vu du caractère discret du calcul numérique et en fonction des besoins. Pour l’intégrale par exemple, il en existe au moins trois types, implémentés dans Vensim : l’intégrale d’Euler, l’intégrale par différence, l’intégrale de Runge-Kutta<a href="#fn1" class="footnoteRef" id="fnref1"><sup>1</sup></a>. Normalement, les résultats de chacunes de ces méthodes doivent être proches.</p>
<ul>
<li>Vensim dispose dans sa version gratuite d’une précision <em>single</em>. C’est-à-dire que seuls des nombres jusqu’à 6 chiffres significatifs peuvent être représentés. Python adopte par défaut la précision <em>double</em> (jusqu’à 17 chiffres significatifs). Ce défaut de précision pour l’instant négligeable dans la mesure où la dynamique des systèmes est contrainte à des modèles avec peu d’itérations. Cependant, l’incertitude peut devenir très grande sur des simulations plus longues, ou pour des applications la précision est de mise.</li>
</ul>
<h2 id="le-véritable-intérêt-du-python">Le véritable intérêt du Python</h2>
<p>Mis à part que Python est un langage libre, gratuit, accessible et très populaire (ce qui permet une diffusion rapide des implémentations dans ce langage), de nombreuses bibliothèques d’apprentissage, d’analyse et de fouille de données existent pour lui.</p>
<p>À terme et dans la pratique, une des solutions d’amélioration du modèle proposé par mon tuteur, Serge Fenet, est d’exécuter une <em>simulation inverse</em> depuis un état voulu dans le futur pour trouver des comportements à adopter dès aujourd’hui, ce qui se déroulerait en deux temps :</p>
<ol type="1">
<li>La simulation inverse à partir de l’état cherché, qui donnerait une multitude de points de départ en fonction des incertitudes</li>
<li>La simulation normale à partir de tous les points de départ trouvés précédemment. On trouverait ainsi le rayon d’action qu’on aurait, et dans quelle mesure on pourrait aboutir à un avenir souhaitable.</li>
</ol>
<section class="footnotes">
<hr />
<ol>
<li id="fn1"><p><a href="https://www.vensim.com/documentation/index.html?integration.htm" class="uri">https://www.vensim.com/documentation/index.html?integration.htm</a><a href="#fnref1">↩</a></p></li>
</ol>
</section>
