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):
"""
Renvoie 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 si a_ij = 0,
alors le tableau renvoyé a' est tel que a'_ij = a_ji.
Les valeurs diagonales sont laissées intactes.
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 (comme ne pas faire à la fois a[0, 1] = 42
et le contraire a[1, 0] = 123
avant d'exécuter symmetrize
).
Si vous avez vraiment besoin d'une symétrisation transparente, vous pouvez envisager de sous-classer numpy.ndarray et simplement redéfinir __setitem__
:
class SymNDArray(numpy.ndarray):
"""
Sous-classe de tableau NumPy pour les matrices symétriques.
Un tableau SymNDArray arr est tel que lorsque arr[i, j] = valeur
fait automatiquement arr[j, i] = valeur, de sorte que les mises à jour
du 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):
"""
Renvoie une version symétrisée du tableau-like input_array.
Le tableau renvoyé a la classe SymNDArray. Les futures affectations au tableau
sont donc automatiquement symétrisées.
"""
return symmetrize(numpy.asarray(tableau)).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 d'arrays, selon vos besoins). Cette approche gère même des affectations plus complexes, 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),…)
, alors le code doit être légèrement adapté avant de s'exécuter avec Python 3 : def __setitem__(self, indexes, valeur): (i, j) = indexes
…
1 votes
Vous pouvez envisager de marquer la réponse comme acceptée, si elle résout votre problème. :)
2 votes
J'ai voulu attendre une meilleure (c'est-à-dire intégrée et efficace en mémoire) réponse à venir. Il n'y a rien de mal avec votre réponse, bien sûr, donc je l'accepterai 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 y ressemble. Je pense que vous pouvez également ajouter des tableaux masqués pour éviter les calculs en double en aval, tant que les tableaux masqués prennent en charge suffisamment de vos scénarios de manipulation de matrice. Rien n'est intégré ni d'une manière génériquement robuste.
0 votes
numpy.all(a == a.T)
ne semble pas fonctionner pour les matrices symétriques avec desnan
s sur la diagonale.