Classe Pygen (suite)

frederic han
Messages : 1137
Inscription : dim. mai 20, 2007 7:09 am
Localisation : Paris
Contact :

Classe Pygen (suite)

Message par frederic han » mar. nov. 22, 2016 3:11 pm

Pour faire suite à la discussion de ce topic:
http://xcas.e.ujf-grenoble.fr/XCAS/view ... f=4&t=1760

giacpy crée en Python des objets de classe Pygen.

Cette classe permet d'utiliser la librairie C++ giac. Plus précisément elle offre acces à un objet de type
giac::gen en C++.

Pour passer de giac vers python il faudra donc dans la majorité des cas passer par des str:
Exemple: Le cas des entiers

Code : Tout sélectionner

>>> from giacpy import giac
>>> a=giac(2)  # 2 dans giac
>>> b=a**100  # dans giac
>>> int(str(b))  # dans python  (conversion correcte)
1267650600228229401496703205376
>>> type(int(str(b)))
<class 'int'>
>>> b               # dans giac
1267650600228229401496703205376

Pour les entiers et les flottants, il existe une petite zone où l'on peut éviter le passage par les chaines: lorque l'objet giac représente un entier ou un flottant qui n'est pas stocké en multiprécision. C'est plus efficace, mais il faut être beaucoup plus prudent.

Avec les valeurs précédentes de a et b on a:

Code : Tout sélectionner

>>> a._type  # un entier en C peut etre converti directement vers python
0
>>> b._type  # un entier de type multiprecision (gmp)
2
>>> a._val    # acces direct au 2 de python.
2
>>> type(a._val)
<class 'int'>
>>> b._val    # pas de sens car b etait de type multiprecision.
653
De même pour les valeurs approchée qui ne sont pas en multiprécision:

Code : Tout sélectionner

>>> import giacpy
>>> c=giacpy.approx('pi',100) # dans giac on est en multiprecision 
>>> c
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117068
>>> d=giacpy.approx('pi',14)  
>>> d._type                      # on verifie que d n'est pas multiprecision.
1
>>> d._double                   # l'acces direct a bien un sens
3.1415926535897967
>>> type(d._double)
<class 'float'>
>>> c._type                      # flottant multiprecision
3
>>> c._double                   # acces direct n'a pas de sens
9.386415709344787e+303
>>> c._val                        # pas de sens non plus 
2131451904
La librairie C++ giac explique les valeurs retournées par l'attribu ._type.
giacpy pour python n'offre un acces direct que pour les cas 0 et 1 via les attribus: ._val et ._double

NB: giacpy_sage (la version de giacpy pour sage) peut en plus partager les entiers gmp.

Code : Tout sélectionner

    // from dispatch.h
    // immediate type (without mem allocation) should be < _ZINT
    _INT_= 0, // int val
    _DOUBLE_= 1, // double _DOUBLE_val
    // all type below or equal to _DOUBLE_ must be non pointers
    _ZINT= 2, // mpz_t * _ZINTptr
    _REAL= 3, // mpf_t * _REALptr
    // all type strictly below _CPLX must be real types
    _CPLX= 4, // gen * _CPLXptr
    _POLY= 5, // polynome * _POLYptr
    _IDNT= 6, // identificateur * _IDNTptr
    _VECT= 7, // vecteur * _VECTptr
    _SYMB= 8, // symbolic * _SYMBptr
    _SPOL1= 9, // sparse_poly1 * _SPOL1ptr
    _FRAC= 10, // fraction * _FRACptr
    _EXT= 11, // gen * _EXTptr
    _STRNG= 12, // string * _STRNGptr
    _FUNC= 13, // unary_fonction_ptr * _FUNCptr
    _ROOT= 14, // real_complex_rootof *_ROOTptr
    _MOD= 15, // gen * _MODptr
    _USER= 16, // gen_user * _USERptr
    _MAP=17, // map<gen.gen> * _MAPptr
    _EQW=18, // eqwdata * _EQWptr
    _GROB=19, // grob * _GROBptr
    _POINTER_=20, // void * _POINTER_val
    _FLOAT_=21 // immediate, _FLOAT_val


Les listes giac disposent d'une conversion vers une liste de Pygen naturelle:

Code : Tout sélectionner

>>> L=giac(range(10))  # une liste/vecteur dans giac
>>> L
[0,1,2,3,4,5,6,7,8,9]
>>> tuple(L)                # un tuple python contenant des Pygen
(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
>>> L2=list(L)             # une liste python contenant des Pygen
>>> 1/L2[5]                # L2[5] est un Pygen donc son inverse est fait dans giac.
1/5
>>> L+L                     # addition au sens de giac (donc des vecteurs)
[0,2,4,6,8,10,12,14,16,18]
>>> 5*L
[0,5,10,15,20,25,30,35,40,45]
>>> 1/2*L                  # ! le 1/2 est du python3 on a donc demande 0.5*L ! 
[0.0,0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5]
>>> '1/2'*L
[0,1/2,2*1/2,3*1/2,4*1/2,5*1/2,6*1/2,7*1/2,8*1/2,9*1/2] 
>>> L/2
[0,1/2,1,3/2,2,5/2,3,7/2,4,9/2]
>>> L2+L2                 # Mais si L2 est une liste Python contenant des Pygen (ou autre chose) alors + est celui de python, c'est donc la concatenation des listes.
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 


frederic han
Messages : 1137
Inscription : dim. mai 20, 2007 7:09 am
Localisation : Paris
Contact :

Re: Classe Pygen (suite)

Message par frederic han » mar. nov. 22, 2016 5:56 pm

Une petite astuce aussi avec les listes giac. Grace a cette conversion vers les listes python, on peut esperer que certaines fonctions python acceptent une liste giac.

Dans l'exemple suivant, L est une matrice giac c'est donc aussi une liste de liste pour giac. Si l'on souhaite avoir un entier python i qui bouge de 0 à la longueur de la liste L il faut transmettre à range un entier python, donc L.rowdim() ne marchera pas mais il suffit de faire ainsi:

Code : Tout sélectionner

>>> import giacpy
>>> L=giacpy.ranm(5,7)
>>> for i in range(len(L)):
...     print("la ligne %s i de L est %s"%(i,L[i]))
... 
la ligne 0 i de L est [94,-18,20,-26,-88,51,93]
la ligne 1 i de L est [25,15,-11,-69,-60,84,-71]
la ligne 2 i de L est [-88,33,72,1,19,0,12]
la ligne 3 i de L est [46,59,-90,-58,48,41,78]
la ligne 4 i de L est [-47,89,-60,6,-69,70,-13]
>>> 


frederic han
Messages : 1137
Inscription : dim. mai 20, 2007 7:09 am
Localisation : Paris
Contact :

Re: Classe Pygen (suite)

Message par frederic han » mar. nov. 22, 2016 6:15 pm

Pour les convertir une liste de flottants (._type 1) de giac vers python voici un peu ce que donne les deux méthodes comme temps:

Code : Tout sélectionner

# Avec python3.4
import giacpy
V=giacpy.randvector(10**5,'0..1')
#In [101]: %time x=eval(str(V))
#CPU times: user 355 ms, sys: 34.7 ms, total: 390 ms
#Wall time: 387 ms
#
#In [102]: %time x2=[ j._double for j in V ]
#CPU times: user 93.3 ms, sys: 0 ns, total: 93.3 ms
#Wall time: 89.6 ms

V6=giacpy.randvector(10**6,'0..1')
#In [114]: %time x2=[ j._double for j in V6 ]
#CPU times: user 1.08 s, sys: 0 ns, total: 1.08 s
#Wall time: 849 ms
#
#In [115]: %time x=eval(str(V6))
#CPU times: user 4.27 s, sys: 0 ns, total: 4.27 s
#Wall time: 4.25 s
#
# Remarque: l'arrondi peut etre different:
#In [117]: x[0]
#Out[117]: 0.565250649117
#
#In [118]: x2[0]                # ici on a utilise ._double
#Out[118]: 0.5652506491169333
#
#In [119]: V6[0]
#Out[119]: 0.565250649117

jpapot
Messages : 13
Inscription : mer. nov. 16, 2016 6:43 am

Re: Classe Pygen (suite)

Message par jpapot » sam. nov. 26, 2016 9:25 pm

Bonsoir.
Merci pour toutes ces infos Frédéric.

Pour en revenir à la conversion des pygen vers python, j'ai remarqué un curieux phénomène (qu'il m'a fallu du temps à comprendre). Avec la méthode approx(), on peut facilement transformer les racines exactes d'une équation en approximation décimale. Cependant :
-> un entier comme 5 devient 5.0
-> une fraction comme 5/2 devient 2,5
-> une racine comme racine de 5 devient 2,2360679775
Bizarrement, les calculs avec des fractions ou des racines donnent des nombres écrits avec une virgule et non un point ! Dans Xcas, je n'ai pas ce problème.

Il faut donc convertir les pygen en str, puis repérer les "," et les changer en "." avant de pouvoir changer ces str en float.

Si vous avez matplotlib et numpy (ou pylab), vous pourrez voir la sortie graphique du code suivant :

Code : Tout sélectionner

from pylab import arange, scatter, plot, subplot
from giacpy import giac, solve, equal, approx

def f(x) : # exemple de fonction
    return (x+3)*(x**2-5)*(2*x-1)
    
x=giac('x')    
a=equal(f(x),0).solve()
print(a,type(a))
b=a.approx()
c=list(b) 
print(c,type(c))
X=[] # comment faire la liste python des racines entières et fractionnaires
for i in c :    
        ch=str(i)
        cha=ch
        if "," in ch:
            cha=ch.replace(",",".")        
        X.append(float(cha))

print(X,type(X))    

Y=[0]*len(X)

A=arange(min(X)-1,max(X)+1,0.01)
B=f(A)

ax = subplot(111)
ax.grid(True)       # affichage de la grille
ax.spines['left'].set_position('zero')   # axe de gauche centré
ax.spines['right'].set_color('none')     # axe de droite invisible
ax.spines['bottom'].set_position('zero') # axe du bas centré
ax.spines['top'].set_color('none')       # axe du haut invisible

plot(A,B)
scatter(X,Y) 
A quoi est lié ce phénomène de virgule, apparemment inexistant dans Xcas ?

Merci,pour votre analyse ou celle de Bernard. ;-)
Bonne soirée.

JP

jpapot
Messages : 13
Inscription : mer. nov. 16, 2016 6:43 am

Re: Classe Pygen (suite)

Message par jpapot » dim. nov. 27, 2016 7:42 am

Bonjour.
Bon, la nuit portant conseil, j'ai pensé qu'il fallait juste écrire '0' dans l'équation à la place de 0 pour que giac "comprenne mieux".

A la première compilation, avec l'équation (x+3)*(x**2-5)*(2*x-1)='0'
j'ai obtenu ceci :
giacpy1.png
giacpy1.png (25.94 Kio) Consulté 7957 fois
La première ligne est le résultat de solve()
La seconde donne une liste Python avec le résultat de approx() -> disparition des "," super !
La troisième était la liste après conversion des "," en"."

Pensant que tout allait bien, j'ai oté donc mon test pour convertir les virgules, puis relancé, j'ai obtenu ceci :
giacpy1bis.png
giacpy1bis.png (7.78 Kio) Consulté 7957 fois
Les virgules, sorties par la porte, étaient entrées par la fenêtre !

Vérification après avoir remis le test.
J'obtiens bien à nouveau des virgules pour racine de 5 :
giacpy2.png
giacpy2.png (25.8 Kio) Consulté 7957 fois
En bref, le changement du 0 en '0' m'a permis de me passer de la conversion, mais ça n'a marché qu'à la première compilation. Comme on disait autrefois : Y'a un truc !

Je cale. Si pouvez éclairer ma lanterne ...

Merci pour vos lumières.
Bonne journée.

JP

frederic han
Messages : 1137
Inscription : dim. mai 20, 2007 7:09 am
Localisation : Paris
Contact :

Re: Classe Pygen (suite)

Message par frederic han » dim. nov. 27, 2016 11:56 am

Il y a des des fonctions qui tiennent compte de votre langue globale.

Ex si je fais: env LANG="en_US.UTF-8" pyzo je n'ai pas ce PB.


si la question est comment transformer votre liste L en une liste de type floats je ferai:

Code : Tout sélectionner

from giacpy import giac
x=giac('x')
L=giacpy.solve('(x+3)*(x**2-5)*(2*x-1)=0')
print(L.approx())
Lf=[ u.approx(14).eval()._double for u in L ]  # ne pas depasser 14 pour garder un ._type de 1
print(Lf)
le eval sert à forcer l'évaluation de la commande précédente pour être sur que le gen ne reste pas en type symbolique.

Si l'on est prudent on met un test pour n'utiliser ._double que si ._type vaut 1.


une autre méthode est d'utiliser locale.atof car les , viennent d'une étape de conversion qui a tenu compte de la langue.

Code : Tout sélectionner

A="1,234"
import locale
locale.atof(A)
ca peut etre utile si vous voulez un peu plus de 14 chiffres comme dans python3, mais c'est à priori plus lent.

Code : Tout sélectionner

import locale
print([ locale.atof(str(u.approx(16))) for u in L])

frederic han
Messages : 1137
Inscription : dim. mai 20, 2007 7:09 am
Localisation : Paris
Contact :

Re: Classe Pygen (suite)

Message par frederic han » dim. nov. 27, 2016 12:03 pm

J'ajoute un exemple de dessins avec mathplotlib ici:
http://xcas.e.ujf-grenoble.fr/XCAS/view ... =24&t=1764

Répondre