Schleife über Sympy-Ausdruck beschleunigen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
LowDepth
User
Beiträge: 4
Registriert: Mittwoch 19. September 2012, 08:15

Hallo, ich bin Neuling in Sachen Python und benutze es dazu naturwissenschaftliche Rechnungen durchzuführen.
Was ich tun muss ist Wigner 3j Symbole ausrechnen (sympy.physics.wigner.wigner_3j) und anschließend über einen Index summieren. Die Summation scheint ein Performancefresser zu sein und ich würde es gerne durch eine Array-Operation ersetzen.
Beispielcode:

Code: Alles auswählen

from sympy.physics.wigner import wigner_3j

def funktion(l,ls,m):
      min=abs(l-ls)   # l und ls sind positive Integer
      max=l+ls
      U=0
      for lss in xrange(min, max):
            U+=wigner_3j( l , ls , lss , m , -m , 0 )
      return U

funktion(10,4,0)  # Beispiel
Die for-Schleife würde ich jetzt natürlich gerne vermeiden, in dem ich wigner_3j auf ein Array aus Integers (meinen lss) anwende und anschließend ein .sum() durchführe.
Hat jemand eine Idee, wie ich das schaffe, oder welche schnelleren Alternativen es gibt?

Eine Idee von mir war bisher das Wignersymbol zu "lambdifien", um es auf ein Array anwenden zu können, aber das habe ich nicht hinbekommen.

[EDIT] Was ich verschwiegen hatte ist, dass ich die gleichen Wigner-Symbole am laufenden Band für unterschiedliche Elemente einer Matrix berechne. Mein Performanceproblem ist also bereits gelöst, wenn ich einmal alle benötigten Wigner-Symbole berechne und diese in einem Array-speichere... so kann ich einfach Funktionen auf dieses Array verweisen, wenn sie ein 3j brauchen.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Ich habe keine Ahnung, was wigner_3j macht, aber wenn ich es deinem Edit richtig entnehme, scheint hier die Schwachstelle zu sein. Eine Addition ist in den meisten fällen auch recht einfach berechnet. Aber trotzdem als Tipp am Rande: da immer symbolisch weitergerechnet wird, lohnt sich oft ein evalf(). Muss man natürlich selber wissen, ob das für den konkreten Fall notwendig ist.

Ich weiß nicht, ob du genau vorraussagen kannst, welche Belegungen von wigner_3j ausgewertet werden müssen. Wenn ja, dann ist deine Idee mit der Liste schon nicht schlecht. Mit einem Dictionary würdest du dir wahrscheinlich noch ein wenig arbeit sparen. Falls es vorher unbekannt ist, würde ich einfach einen kleinen Pool erstellen, welcher ggf. den Ausdruck auswerten und dann speichert oder, falls der Wert schon existiert, dieser einfach zurückgegeben wird. Abhängig davon, wie zeitkritisch deine Berechnungen sind, könntest du eventuell auch noch Symmetrien ausnutzen, falls so etwas in diesem Fall vorliegt.

Eine Vermutung auf Grund der Namen habe ich noch: Soll es wirklich xrange(min, max) heißen und nicht xrange(min, max+1)?
Das Leben ist wie ein Tennisball.
LowDepth
User
Beiträge: 4
Registriert: Mittwoch 19. September 2012, 08:15

Hallo EyDu, danke für deine Antwort. Ich weiß vor der Berechnung eigentlich, wie "genau" ich meine Rechnung machen will. Mein Abschneideparameter ist eben das lmax. Danke für den Hinweis mit xrange, den Fehler habe ich nur im Codeschnipsel gemacht ;).
Wie hast du das mit dem Dictionary gemeint? Klingt so, als könnte ich das später vielleicht noch brauchen.


Gruß
Stefan
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Im Prinzip ist mein Vorschlag mit dem Dictionary nur dann sinnvoll, wenn die zu berechnende Funktion relativ komplex ist und wenn du die Funktion "oft" (manchmal sind zwei Aufrufe schon oft) ausführen musst. Dann kannst du die Parameter als Schlüssel des Dictionarys verwenden und das Ergebnis des Funktionsaufrufs als Wert. Vielleicht einfach mal als Beispiel:

Code: Alles auswählen

class Cache(objet):
    def __init__(self, func):
        self.func = func
        self.results = dict()

    def __call__(class, *args):
        try:
            return self.results[args]
        except KeyError:
            result = self.func(*args)
            self.results[args] = result
            return result

def funktion(l,ls,m,cache):
      min=abs(l-ls)   # l und ls sind positive Integer
      max=l+ls
      U=0
      for lss in xrange(min, max):
            U+=cache( l , ls , lss , m , -m , 0 )
      return U

cache = Cache(wigner_3j)
funktion(10,4,0, cache)
Das kann man noch ein wenig allgemeiner machen und funktioniert auch nicht für alle Parameter (zum Beispiel Listen, bzw. alle veränderbaren Objekte), aber in deinem Fall liegen eh nur Zahlen vor.
Das Leben ist wie ein Tennisball.
LowDepth
User
Beiträge: 4
Registriert: Mittwoch 19. September 2012, 08:15

Sowas in der Art sieht mir sehr günstig aus. Vielen Dank für den Tipp!
Antworten