Si vous pouvez vous permettre de symétriser la matrice juste avant de faire des calculs, ce qui suit devrait être raisonnablement rapide :
def symmetrize(a):
"""
Retourne une version symétrisée du tableau NumPy a.
Les valeurs 0 sont remplacées par la valeur du tableau à la position symétrique
(par rapport à la diagonale), c'est-à-dire que si a_ij = 0,
alors le tableau retourné a' est tel que a'_ij = a_ji.
Les valeurs diagonales restent inchangées.
a -- tableau NumPy carré, tel que a_ij = 0 ou a_ji = 0,
pour i != j.
"""
return a + a.T - numpy.diag(a.diagonal())
Cela fonctionne sous des hypothèses raisonnables (telles que ne pas faire à la fois a[0, 1] = 42
et le contradictoire a[1, 0] = 123
avant d'exécuter symmetrize
).
Si vous avez vraiment besoin d'une symétrisation transparente, vous pouvez envisager de créer une sous-classe de numpy.ndarray et de simplement redéfinir __setitem__
:
class SymNDArray(numpy.ndarray):
"""
Sous-classe de tableau NumPy pour les matrices symétriques.
Un SymNDArray arr est tel que faire arr[i,j] = valeur
fait automatiquement arr[j,i] = valeur, de sorte que les mises à jour de tableau restent symétriques.
"""
def __setitem__(self, (i, j), valeur):
super(SymNDArray, self).__setitem__((i, j), valeur)
super(SymNDArray, self).__setitem__((j, i), valeur)
def symarray(tableau_d_entree):
"""
Retourne une version symétrisée du tableau d'entrée de type tableau-like.
Le tableau retourné a la classe SymNDArray. Les affectations ultérieures au tableau
sont donc automatiquement symétrisées.
"""
return symmetrize(numpy.asarray(tableau_d_entree)).view(SymNDArray)
# Exemple :
a = symarray(numpy.zeros((3, 3)))
a[0, 1] = 42
print a # a[1, 0] == 42 aussi !
(ou l'équivalent avec des matrices au lieu de tableaux, en fonction de vos besoins). Cette approche gère même des affectations plus compliquées, comme a[:, 1] = -1
, qui définit correctement les éléments de a[1, :]
.
Notez que Python 3 a supprimé la possibilité d'écrire def …(…, (i, j),…)
, donc le code doit être légèrement adapté avant de l'exécuter avec Python 3 : def __setitem__(self, indexes, valeur): (i, j) = indexes
…
1 votes
Vous pourriez envisager de marquer la réponse comme acceptée, si elle résout votre problème. :)
2 votes
Je voulais attendre une meilleure réponse (c'est-à-dire intégrée et efficace en mémoire) à venir. Il n'y a rien de mal avec votre réponse, bien sûr, alors je vais l'accepter de toute façon.
0 votes
Je pense qu'à ce jour, vous ne pouvez que sous-classer (non merci) ou envelopper numpy, par exemple en enveloppant numpy en modifiant la façon dont vous remplissez la matrice via vos propres fonctions setter, afin d'obtenir une interface qui lui ressemble. Je pense également que vous pouvez ajouter des tableaux masqués pour éviter les calculs en aval doubles autant que les tableaux masqués prennent en charge suffisamment de vos scénarios de manipulation de matrice. Rien n'est intégré ni de manière génériquement robuste.
0 votes
numpy.all(a == a.T)
ne semble pas fonctionner pour les matrices symétriques avec desnan
sur la diagonale.