Quelques nouvelles

J’ai mis à jour les librairies SIunit et Vector2D.
Pas de changement fonctionnel, juste de l’optimisation et une simplification du code.

Je m’attelle maintenant à la mise au propre d’une version multi-processus (mais toujours pas multi-thread) de zz-chrono.

<36-15 my life> j’ai largement réduit ma cadence ces derniers temps, ayant rejoint les rangs du salariat à plein temps. M’enfin, tant qu’il y aura 24 heures dans une journée, il y aura du temps pour mes petits bouts de code égoïstes 😉

Le module siunit

C’est une classe numérique qui gére les grandeur physique avec les unités SI.
Par exemple 5 kilos + 3 g = 5,003 kg, mais 5 kilos + 3 volts n’a aucun sens.
Ca tourne donc autour de l’analyse dimensionnelle.

Les sources sont aux bout de ce lien

Exemple

Ci-dessous, on crée une hauteur, une masse et une gravitée.
Puis, on multiplie les 3 (formule de l’énergie potentielle)
Le résultat est une énergie exprimée en m2.kg.s-2. Cette unité correspond aux joules.
On essaie ensuite d’additionner la masse et la hauteur. Cela, évidemment, crée une exception.

>>>import siunit
>>> hauteur = 2*m
>>> masse = 3*kg
>>> print ("hauteur = {0}, masse = {1}".format(hauteur,masse))
hauteur = 2 m, masse = 3 kg
>>> g = 9.81*m/s/s
>>> energie_potentielle = hauteur*masse*g
>>> print(energie_potentielle)
58.86 m2.kg.s-2
>>> absurde = hauteur + masse   #TypeError exception
Traceback (most recent call last):
  File "", line 1, in 
    absurde = hauteur + masse
  File "D:\Code\libsPython\dimentional analysis\siunit.py", line 67, in __add__
    " and "+unit_to_str(other.unit))
TypeError: Cannot add units m and kg
>>> 

Plus en détail

Ce module contient la classe numérique immutable Dnumber (pour « denominated number », grandeur physique en Anglais) dérivé de la classe virtuelle Real.
Les valeur utilisées dans l’exemple ci-dessus sont toutes des instances de Dnumber. Y compris m, kg ou s qui sont prédéfinis dans le module.
Scrollez tout a la fin du code pour voir toutes les unités ainsi definies.

On peut faire l’essentiel des opérations courantes pourvues qu’elle aient du sens du point de vu de la physique.
Opérations définies: +, -, *, /, abs(),==, !=, //, %, <, <=, >, >=, float, math.trunc(), round(), math.floor(), math.ceil(), divmod() and a few others.

J’ai aussi defini beaucoup de sous unités. On peut donc faire ceci:

>>> longueur = 30 * cm
>>> longueur += 2*inch
>>> print (longueur)
0.3508 m
>>> print (3*longueur)
1.0524 m
>>>

Par contre longueur + 3 ne marche pas puisque le premier nombre a une unité et pas le second

Ce module fonctionne assez bien avec le module Vector2D

La librairie zzChrono

<3615 mylife> Bon… Je me suis un peu foiré sur ce coup
Déjà, je me suis trouvé un vrai boulot qui m’a bouffé du temps. Surtout j’ai sous estimé la complexité de gérer 2 notions (temps CPU et temps réel) dans 3 contextes (mono-thread, muti-thread, multi-process. Et meme multi-thread dans un multi-process)
Alors bon… j’ai revu mon ambitions à la baisse et je vous présente donc: </3615 mylife>

La librairie zzChrono

Les sources sont là

Le but

Cet outils répond à un problème simple: combien de temps mon programme a t-il
passé dans telle ou telle portion du code.

Il se veux facile d’utilisation et non-verbeux. Si vous cherchez le chrono le plus précis de l’univers, passez votre chemin (ou mieux: améliorez mon travail)

Il diffère de timeit qui est plutôt un benchmark pour un code isolé.
Il est un peu redondant avec les profiler mais en VACHEMENT plus simple

Limitation

Ce module donne des résultat souvent fantaisiste dans un scenario multi-thread.

Cela fonctionne dans un scenario multi-processus, mais avec une instance dans chaque processus.

Les temps totaux sont significatifs à environs 0.1s. Dans la mesure oũ cet outils sert à mesurer l’impact total d’une portion dans la durée de vie d’un programme, ce n’est pas gênant. Mais ça peut le devenir dans un autre contexte.

Le décorateur @zz_chrono.chronometer(nom)

C’est la façon la plus simple d’utiliser cette librairie

Il suffit d’ajouter ce décorateur à la fonction qu’on souhaite surveiller

Exemple simple:

from zz_chrono import *

@chronometer("initialisation")
def initialisation():
(...)
  
@chronometer("Faire un truc")
def do_something():
  (...)

@chronometer("un autre truc")
def do_something_else():
    (...)

initialisation()
for i in range (1000):
  if some_condition():
    do_something()
  else:
    do_something_else()
    
display_all()

La dernière ligne affiche le compte-rendu suivant

<Chrono "initialisation".elapsed time total:0.09115540896728355s >
<Chrono "initialisation".CPU time total:0.09375s >

<Chrono "Faire un truc".elapsed time total:1.947161044425888s >
<Chrono "Faire un truc".CPU time total:1.90625s >

<Chrono "un autre truc".elapsed time total:0.03172837835946546s >
<Chrono "un autre truc".CPU time total:0.046875s >

Ceci nous apprend donc que la fonction “initialisation” s’est exécutée en 0.098 seconde en
temps réel et pendant 0.09375 seconde en temps CPU
Sautez à ce chapitre pour mieux comprendre ces différents temps

On comprend donc que l’essentiel du temps d’exécution est concentré dans “do_something()”

Regrouper les chronos

Il se peut aussi qu’on veuillent regrouper plusieurs fonctions dans un chrono “fourre-tout”.

Pas de problème. Il suffit de leur donner le même nom de chrono.

Dans notre précédent exemple, mettons qu’on souhaite exploser l’initialisation en 3 parties

@zz_timer.timer("initialisation")
def init1()
  (...)
@zz_timer.timer("initialisation")
def init2()
  (...)
@zz_timer.timer("initialisation")
def init3()
  (...)
(...)

Le chrono nommé “initialisation” représentera donc le cumul des 3 fonctions

<Chrono "Faire un truc".elapsed time total:1.9543509192608595s >
<Chrono "Faire un truc".CPU time total:1.859375s >

<Chrono "initialisation".elapsed time total:0.14157410301506365s >
<Chrono "initialisation".CPU time total:0.140625s >

<Chrono "un autre truc".elapsed time total:0.031408351488404734s >
<Chrono "un autre truc".CPU time total:0.0625s >

On peut aussi récupérer le chrono par son nom

>>>mon_chrono = getChrono("initialisation")
>>>print (mon_chrono.chrono_list[REAL_TIME])
>>>elapsed time total:0.14157410301506365s

Chronométrer un bout de code

Il arrive aussi qu’on veuille chronométrer un bout de code flottant.

Cela se fait en utilisant la clause “with” sur un object chronometre

exemple

while some_condition():
  (...)
  with  getChrono("pygame"):
    pygame.manage_events()
    pygame.display(screen)
  
print (getChrono("pygame"))

On mesure quoi, au fait?

Mesurer le temps, c’est un problème tout bête dans les grandes ligne, mais super compliqué quand on s’inquiète des détails.

Ma librairie utilise 2 notions de temps:

  • le temps écoulé (alias « temps réel »)
  • le temps CPU

En revanche, il existe principalement 3 notions différentes de temps pour l’ordinateur:

L’horloge:

C’est celle que tout le monde connaît. Elle indique l’heure et on peut en déduire le temps écoulé.
Le problème est qu’on peut la modifier (à raison si on voyage ou passe à l’heure d’hiver). Elle n’est donc pas très fiable.

Si vous utilisez une version antérieure à Python 3.3, c’est cette horloge qui donne le “temps réel”

Le compteur interne:

C’est une horloge qui donne le temps écoulé depuis l’allumage de l’ordinateur.
Elle ne peut pas être modifiée est s’écoule régulièrement (horloge monotone).
Elle est donc plus fiable pour un intervalle de temps.

Si vous utilisez une version égale ou postérieure à Python 3.3, c’est cette horloge qui donne le “temps réel”

Le temps CPU

C’est le temps passée par un cœur du CPU à traiter un processus.


l’horloge plus en détail

Cette horloge indique l’heure. Intervalle entre l’heure T1 et l’heure T2 se calcule comme T2-T1.
Problème: elle peut être modifiée par l’utilisateur ou le système et s’écoule parfois selon des règle peu instinctive (rattrapage des secondes puisqu’une année ne dure pas exactement 365.25 jours)
T2-T1 ne donne donc pas toujours le résultat attendu

Si vous utilisez une vieille version de Python (<3.3), c’est cette horloge qui donne le “temps réel”
Elle s’obtient par la commande [link]time.time()

Le compteur interne:

C’est une horloge qui donne le temps écoulé depuis l’allumage de l’ordinateur.
Il ne donne donc pas l’heure mais peut calculer un intervalle de temps.
Il ne peut pas être modifié est s’écoule régulièrement (horloge monotone).
Son comportement pendant une mise en pause dépend de l’implémentation système et matérielle.
Elle a, a priori, une meilleur precision que l’horloge système.

Si vous utilisez une version récente de Python (3.3 ou plus), c’est cette horloge qui donne le “temps réel”
Il s’obtient par la commande [link]time.perf_counter()

Le temps CPU plus en détail

C’est le temps passée par un cœur du CPU à traiter un processus.
Attention: cela ne correspond pas au temps de traitement. Par exemple, les accès au disque ou à la carte graphique font partit du temps de traitement mais ne sont pas inclus dans le temps CPU

Plusieur threads peuvent exister dans un processus. L’horloge CPU donne l’integralité du temps CPU ecoulé dans un processus. Ce module donne donc des résultat parfois “bizarre” dans un scénario multithread. Je corigerais cela… un jour…

Ce chronomètre ne s’écoule pas quand l’ordinateur est en pause.
Il s’obtient par la commande [link]time.process_time()

le temps écoulé plus en détail

Ce temps mesure, à chaque fois, intervalle entre l’entrée dans la fonction et la sortie de la fonction (ou du bout de code).
En conséquence:

  • Si un autre programme/processus effectue un traitement lourd en tache de fond, ce temps sera plus long
  • Si 2 threads utilisant le meme chrono se chevauchent, on obtient n’importe quoi (je sais: c’est facile à régler mais comme je n’ai pas pus obtenir un module completement thread-friendly, j’ai fait un code simple et mono-thread)

Pour finir, voici les liens:
Le module zzChrono.py
La documentation (en anglais, aproximatif, pour l’instant)
Donation à votre bienfaiteur

La librairie Vector2D (classe Vector2)

Pour mon premier code publié, voici un objet émulant les vecteurs en 2 dimensions.
Le code est au bout de ce lien.
le repositary complet est au bout de celui-ci.
Je l’ai conçu pour être facile d’usage et flexible au dépend de la performance. On peut, inverser cela en commentant quelques lignes (tout ce qui commence par « @ »).
Cet objet permet du code tel que celui ci:

a = Vector2( 2, 2)
b = Vector2( 3, 3)
c = 2*a - b    # Les opérateurs courants marchent (+, -, *, +=, -=, ==) exceptés le point du produit scalaire et le ^ du produit vectoriel
c += (0,2)    # Le duck typing autorise un tuple comme opérande
x,y = c    # sémantiquement équivalent à "x = c[0]; y=c[1]"
toto == c.x    #les vecteurs ont des propriétés x et y
tata== c[1]    # les vecteurs sont des énumérés.

Utiliser un tuple (ou n’importe quel énuméré), c’est cool, mais le duck typing va plus loin.
Il est possible d’utiliser n’importe quel objet avec les propriétés x et y OU getX() et getY() OU get_x() et get_y() dans la plupart des calculs.
Mon point de départ était la librairie “euclid.py” de Alex Holkner que j’ai élaguée puis améliorée. L’original s’occupe de la géométrie euclidienne de manière bien plus large.

J’ai préféré me limiter à 2 dimensions et aux vecteurs pour fournir un code pas trop fouillis à ceux qui n’ont pas besoin de plus. En particulier pour le jeu vidéo.
Dans ce contexte, j’ai constaté qu’il est plus simple (et performant) d’utiliser un “vrai” vecteur 2D qu’un vecteur 3D (ou n dimensions) dont on ignore une dimension.
Je pense publier une librairie plus complète plus tard.

Je publie ce code dans le domaine public. La règle est simple: vous en faites ce que vous voulez (et je ne suis pas responsable du résultat).
Je vous invite chaleureusement à me faire une donation, à me créditer et à garder le code résultant aussi libre que possible, mais avant tout je ne veux pas vous bloquer avec des contraintes de droit.

Bref! Voici les liens vers:
Le module vector2d.py
La documentation (en anglais, aproximatif, pour l’instant)
La libraire originale “euclid.py”. Plus complète mais moins cool (en python2.x)
Donation à votre bienfaiteur

Bonjour tout le monde!

Bienvenu sur mon blog consacré à la programmation Python… pardon… consacré à MA programmation Python.

Je publierais donc mon code à moi en esperant qu’ils sera utile à d’autre gens. Je pense me limiter à 2 ou 3 post par mois.

Oui: c’est un blog complètement nombriliste ou je parlerais essentiellement tout seul de bouts de code qui, vraisemblablement n’intéresseront que moi.

Mais on ne sais jamais…

Qui suis-je?

Je suis un programmeur; un vieux de la vieille. Comme beaucoup d’individu de ce type, je n’ai pas de boulot, mais je le vis assez bien. Ça me permet, pour une fois, de coder ce qui m’amuse.

Seulement, je me heurte au problème récurant des passionnés procrastinateur: je commence des trucs super-intéressants, mais ne les fini jamais. Genre: Pas grave! C’est juste un truc perso

C’est donc un peu le but de ce blog. Étant donné un nombre suffisant de regards accusateurs, tous mes bouts de codes devrons être terminées.

Le calendrier de mes publications sera aussi tributaire de mon style de vie. Il se trouve que je vis et voyage dans les zones rurales ou maritimes de Sumatra. L’Internet et même l’électricité peuvent s’y faire rares.

Le programme

Je compte publier un truc utilisable tout les 1 ou 2 mois.

Ma production privilégie traditionnellement la facilité d’usage et la lisibilité.

Je publie des « vrai » version betha. C.a.d. que, dans mon esprit, ce sont des produits fini mais qui demande encore l’avis d’un œil externe.

Juin 2016

Vector2D

Un objet représentant un vecteur en 2 dimensions
Je sais, il y en déjà plein, mais le mien est meilleur.
Il permet notamment un bon niveau de duck typing (pour ceux qui ne connaissent pas, disons que c’est du polymorphisme) qui augmente largement la lisibilité.

Usage:

spam = Vector2(4,5)
x,y = spam  # Sémantiquement équivalent a « x=spam[0] ; y=spam[1] »
foo = 2 * spam + (x,y) # Le tuple (x,y) est compris comme un Vector2
# Les 2 lignes suivantes sont equivalentes
print ('foo = Vector2(%.2f, %.2f)' % (foo.x, foo.y))
print ('foo = Vector2(%.2f, %.2f)' % (foo[0], foo[1]))

Sources:
https://bitbucket.org/holypython/vector2d/src

Juillet 2016

zzChrono

Une bibliothéque avec un décorateur permetant de mesurer le temp d’exécution d’une fonction (ou d’une portion, mais sans le sucre syntaxique)

Usage:

@chronometer("render time")
def display():
   do_something()   

@chronometer("geometry time")
def compute_geometry():
   do_plenty_of_things()
 
display()
compute_geometry()
display()

>>> display_chrono("render time")
<Chrono "render time" total:0.03125286102294922 s >
>>> display_all()
<Chrono "render time" total:0.03125286102294922 s >
<Chrono "geometry time" total:0.04687809944152832 s >

Sources (work in progress. N’hésitez pas a proposer votre aide):
https://bitbucket.org/holypython/zzchrono/src

Aout 2016

denominate_numbers

Permet de faire des maths avec des nombres dimentionnés (ayant des unitées). De l‘analyse dimentionelle, si vous aimez les grands mots.

Usage :

>>> g = 9.8*m/s/s
>>> print(g)
9.8 m.s-2
>>> hauteur = 120*cm
>>> print (hauteur)
1.2 m
>>> energie_potentielle = hauteur*g
>>> print(energie_potentielle)
11.76 m2.s-2
>>> absurde = hauteur + g
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in 
    absurde = hauteur + g
TypeError: Can not add units "m" with "m.s-2"

Peut etre :
Afficher 11.76 m2.s-2 c’est exact, mais peut etre 11.76 J est-il mieux. Qu’en pensez-vous?
Sources (work in progress. N’hésitez pas a proposer votre aide):
https://bitbucket.org/holypython/dimentional-analysis/src

Octobre 2016

Géométrie euclydienne, le retour

Je ne sais pas encore ce que je vais faire.
Soit étendre ma librairie Vector2D à toute la géometrie euclidienne du plan, soit généraliser mon vecteur polymorphe a la 3D.
Peut etre meme les deux si j’ai le temps.