Bonjour à tous, Bonne et Heureuse Année 2011,
Je voudrais soumettre à votre sagacité un petit problème qui me turlupine.
Je suis en train de développer un module de géométrie affine. je dois pouvoir faire de la géométrie dans les espaces K^n où K peut-être soit le corps des réels, soit le corps des complexes, soit le corps des rationnels, soit un corps fini Z/pZ.
Il faut pour cela définir les vecteurs et les points, si possible avec une seule définition qui marche dans tous les cas de figure, c'est fait !
Voici le module 'de base' définissant les espaces vectoriels et les espaces affines associés.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# -*- coding: utf-8 -*-
 
from fractions import Fraction  #version python >= 2.6
 
class MyError(Exception):
    """Erreur custom"""
    def __init__(self,value):
        self.value=value
    def __str__(self):
        return repr(self.value)
 
######Modélisation des corps Z/pZ avec p premier#####
def GenZp(p):
    """génère la classe paramétrée Z/pZ"""
    class C(object):
        car=p
        def __init__(self,m):
            """initialisation à partir d'un entier"""
            self.m=m%self.car
        def __str__(self):
            """représentation externe pour print etc..."""
            return str(self.m)
        def __eq__(self,other):
            """test d'égalité"""
            return self.m==other.m
        def nul(self):
            """test de nullité"""
            return self.m==0
        ### les opérateurs unaires
        def __neg__(self):
            """opposé"""
            return C(-self.m)
        def __invert__(self):
            """inverse"""
            if not self.nul():
                y=1
                while (self.m*y)%self.car != 1:
                    y+=1
            return C(y)
        ### les opérations binaires
        def __add__(self,other):
            """addition"""
            return C(self.m+other.m)
        def __mul__(self,other):
            """multiplication"""
            if type(self)==type(other):#de deux éléments du même corps
                return C(self.m*other.m)
            else:#d'un scalaire par un vecteur
                return other.__rmul__(self)
        def __sub__(self,other):
            """soustraction"""
            return C(self.m-other.m)
        def __div__(self,other):
            return self*~(other)
        def __pow__(self,k):
            """puissances"""
            if k >=0:
                return C(self.m**k)
            else:
                return ~self**(-k)
    return C
#####fin de la modélisation des corps Z/pZ###############################
 
 
 
####Espaces vectoriels K^n K corps quelconque n entier quelconque##############
class VecteurKn:
    """modélisation des vecteurs à n dimensions corps quelconque"""
    def __init__(self,L):
        """constructeur"""
        self.V=L
        self.n=len(L)
    def __str__(self):
        """représentation externe print, etc..."""
        L=[str(x) for x in self.V]
        return '('+ ",".join(L)+')'
    def __eq__(self,other):
        """test d'égalité"""
        return self.n==other.n and all(self.L[i]==other.L[i] for i in xrange(0,self.n))
    def __add__(self,other):
        """addition des vecteurs"""
        if self.n!=other.n:
            raise MyError("Tailles non concordantes")
        else:
            L=[self.V[i]+other.V[i] for i in range(0,self.n)]
            return VecteurKn(L)
    def __neg__(self):
        """opposé d'un vecteur"""
        return VecteurKn([-x for x in self.V])
    def __sub__(self,other):
        """différence de deux vecteurs"""
        return self+(-other)
    def __rmul__(self,k):
        """ produit par un scalaire """
        L=[k*x for x in self.V]
        return VecteurKn(L)
####fin de la définition des espaces vectoriels###############
 
###########Modélisation des espaces affines###################
class PointKn:
    """modélisation des espaces affines K^n n et K quelconques"""
    def __init__(self,L):
        """constructeur"""
        self.C=L #coordonnées
        self.n=len(L)
    def __str__(self):
        """représentation externe print, etc..."""
        L=[str(x) for x in self.C]
        return '('+ ",".join(L)+')'
    def __eq__(self,other):
        """test d'égalité"""
        return self.n==other.n and all(self.C[i]==other.C[i] for i in xrange(0,self.n))
    def __add__(self,vecteur):
        """opération du groupe additif des vecteurs"""
        if self.n != vecteur.n:
            raise MyError("Tailles non concordantes")
        else:
            L=[self.C[i]+vecteur.V[i] for i in range(0,self.n)]
            return PointKn(L)
    def __sub__(self,other):
        """vecteur différence de deux points"""
        if self.n != other.n:
            raise MyError("Tailles non concordantes")
        else:
            L=[self.C[i]-other.C[i] for i in range(0,self.n)]
            return VecteurKn(L)
 
###fin de la modélisation des espaces affines######################
Vous remarquerez à ce niveau qu'il y a trois surcharges de l'opérateur __eq__ (pour les corps Z/pZ, pour les vecteurs et pour les points) Ces surcharges fonctionnent comme elles doivent dans des tests naturels.
Voici maintenant un petit programme d'application visant à déterminer l'intersection de deux plans non parallèles (soit une droite) dans l'espace (Z/3Z)^3.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding: utf-8 -*-
 
from affines import *
 
def main():
    CZ3=GenZp(3) #la classe modélisant les éléments Z/3Z
    Z3=[CZ3(x) for x in xrange(0,3)] #le corps Z/3Z entier
    P=PointKn([Z3[1],Z3[2],Z3[1]]) #le point P de l'énoncé
    U=VecteurKn ([Z3[2],Z3[1],Z3[1]]) # le vecteur U de l'énoncé
    V=VecteurKn ([Z3[1],Z3[0],Z3[1]]) # le vecteur V de l'énoncé
    F1=[k*U+h*V for k in Z3 for h in Z3] # le plan vectoriel engendré par U et V
    Q=PointKn([Z3[1],Z3[0],Z3[1]]) #le point Q de l'énoncé
    W=VecteurKn ([Z3[0],Z3[0],Z3[1]]) # le vecteur W de l'énoncé
    T=VecteurKn ([Z3[0],Z3[1],Z3[1]]) # le vecteur W de l'énoncé
    F2=[k*W+h*T for k in Z3 for h in Z3] # plan vectoriel engendré par W et T
    B1=[P+x for x in F1] # le premier plan affine
    B2=[Q+x for x in F2] # le second plan affine
    INTER=[M for M in B1 if any(M==N for N in B2)]
    for M in INTER:#liste les points de l'intersection
        print M
 
if __name__ == "__main__":
    main()
J'en viens au point qui me dérange.
J'avais tout naturellement et dans un premier temps défini INTER (ma droite intersection) par une 'list comprehension'
Code : Sélectionner tout - Visualiser dans une fenêtre à part
INTER=[M for M in B1 if M in B2]
en pensant que le filtre 'if M in B2' allait se baser sur ma surcharge de __equ__ .
Grosse déception !
Le filtre se base sur l'égalité des adresses (bref sur le '==' standard). Quelqu'un sait-il pourquoi ? J'ai trouvé une solution à ma façon qui force le test '==' surchargé (voir code) mais c'est inélégant. Quelqu'un a--t-il une solution meilleure ?
NB: Pour vous éviter de perdre du temps.
Passer par 'set' ne résout rien :
B1=set(B1) -->TypeError: unhashable instance
Il y a de très fortes restrictions sur les types des objets pouvant être mis en 'sets'.
,