2013-01-10 18 views

Antwort

5

Es hierfür keine direkte Unterstützung ist. SymPy kombiniert automatisch allgemeine Ausdrücke in einer Multiplikation mit Exponentiation. Der einzige Weg, dies zu vermeiden, ist die Verwendung des Mechanismus evaluate=False. Zum Beispiel

>>> Mul(x, x, evaluate=False) 
x*x 

Es gab eine Diskussion über die SymPy Mailingliste vor einiger Zeit über diese genaue Frage (https://groups.google.com/d/topic/sympy/qaJGesRbX_0/discussion). Ich habe dort einen Code gepostet, der das macht. Ich werde es hier wiederholen:

def pow_to_mul(expr): 
    """ 
    Convert integer powers in an expression to Muls, like a**2 => a*a. 
    """ 
    pows = list(expr.atoms(Pow)) 
    if any(not e.is_Integer for b, e in (i.as_base_exp() for i in pows)): 

     raise ValueError("A power contains a non-integer exponent") 
    repl = zip(pows, (Mul(*[b]*e,evaluate=False) for b,e in (i.as_base_exp() for i in pows))) 
    return expr.subs(repl) 

Hier ist, wie es

>>> a = Symbol('a') 
>>> exp = a**2 
>>> print(exp) 
a**2 
>>> print(pow_to_mul(exp)) 
a*a 

arbeitet ich hier die gleiche Einschränkung wie auf der Mailing-Liste gesetzt werden: „auswerten = False ist so etwas wie ein Hack, so Seien Sie sich bewusst, dass es zerbrechlich ist.Einige Funktionen werten den Ausdruck neu und wandeln ihn zurück in Pow. Andere Funktionen brechen ab, weil eine erwartete Invariante durch den Ausdruck evaluate = False unterbrochen wird (zB würde der Zweifelsfaktor() korrekt funktionieren). "

+0

Dies ist ordentlich und funktioniert die meiste Zeit. Wie Sie jedoch sagen, das _evaluate_ kwarg ist fragil oft übersehen, auch häufig in der endgültigen _exprs (repl) _ Linie. Nach der Konvertierung möchte ich nur auf eine C-Zeichenfolge drucken (d. H. Ich werde keine weiteren Operationen für den Ausdruck ausführen). Gibt es also eine Möglichkeit, die Methode _subs_ zu erzwingen, die konvertierten Ausdrücke nicht auszuwerten? –

+0

Okay, es sieht so aus, als ob ich dies gelöst habe, indem ich eine Kopie von _xreplace_ hacken musste, um evaluate = False intern zu verwenden, wann immer es einen Unterausdruck regeneriert. Können Sie irgendwelche Probleme mit diesem Ansatz sehen? –

+0

Es sollte möglich sein, eine Version von xreplace zu schreiben, die dies tut, ohne irgendetwas zu hacken, d. H. Genau wie eine Funktion, die einen Ausdruck und eine Ersetzung als Eingabe nimmt und die Ausgabe durch 'evaluate = False' ersetzt. – asmeurer

1

Es scheint so etwas nicht zu geben, es macht nur das Gegenteil.

sympy zeigt immer die Ausgabe in der einfachsten Art und Weise, so wird es immer sagen:

(x**2).expand() -> x**2 

simplify(x**2) -> x**2 
+0

Ist es möglich, die Funktion selektiv auszuschalten? – user1353285

+0

Ich habe keine Optionen gesehen, um 'x * x' tatsächlich anzuzeigen.Aber wer weiß, es ist eine ziemlich große Bibliothek ... – ATOzTOA

0

Nach Aarons akzeptierter Antwort und meinem Kommentar dazu, ist dies die Version xreplace, die ich anstelle der letzten subs Zeile verwende, um zu vermeiden, dass Unterausdrücke ausgewertet werden (und somit die Erweiterung der Leistung auf eine Kette verliert von Multiplikationen).

def non_eval_xreplace(expr, rule): 
    """ 
    Duplicate of sympy's xreplace but with non-evaluate statement included 
    """ 
    if expr in rule: 
     return rule[expr] 
    elif rule: 
     args = [] 
     altered = False 
     for a in expr.args: 
      try: 
       new_a = non_eval_xreplace(a, rule) 
      except AttributeError: 
       new_a = a 
      if new_a != a: 
       altered = True 
      args.append(new_a) 
     args = tuple(args) 
     if altered: 
      return expr.func(*args, evaluate=False) 
    return expr 

Ich dachte, diese Funktionalität zu den bestehenden xreplace in der SymPy Bibliothek hinzugefügt werden könnte, indem es **kwargs nehmen lassen, die den expr.func Aufruf übergeben werden. Ist das etwas, an dem Sie interessiert sind, oder wäre das für die Mehrheit der Benutzer unnötig komplex? (oder habe ich Ihren Kommentar oben falsch verstanden und gibt es einen einfacheren Weg, dies zu tun?)

0

Andere Antworten behandeln nicht -x**2, also habe ich Regex stattdessen nur für Potenzen von 2 zu lösen. Ich verstehe, das ist ein wenig hacky aber es hat für mich funktioniert.

from sympy.printing import ccode 
import re 
CPOW = re.compile(r'pow\((?P<var>[A-Za-z_]\w*)\s*,\s*2\s*\)') 

def to_c_code(expr): 
    code = ccode(expr) 
    # sympy has a hard time unsimplifying x**2 to x*x 
    # replace all pow(var,2) with var*var 
    code = re.sub(CPOW, r'\g<var>*\g<var>', code) 
    return code 
1

Verfahren ersetzen ist gut geeignet, diese Aufgabe für einfache Ausdrücke:

>>> expr = (x**2 + 1)/(x**3 - 2*x) 
>>> expr.replace(
... lambda x: x.is_Pow and x.exp > 0, 
... lambda x: Mul(*[x.base]*x.exp, evaluate=False)) 
(x*x + 1)/(-2*x + x*x*x) 

Tweaking wird notwendig sein, Dinge wie 1/x**3 oder x**2*(1 + x**2) zu behandeln. Wenn Sie jedoch den Zähler und den Nenner der Ausdrücke erweitern und sie getrennt behandeln, kann dies das tun, was Sie brauchen. Und wenn die Basen immer Symbole sind, kann dieser Symbol-Hacker-Trick den Trick noch besser machen:

>>> def sack(expr): 
...  return expr.replace(
...  lambda x: x.is_Pow and x.exp > 0, 
...  lambda x: Symbol('*'.join([x.base.name]*x.exp))) 
... 
>>> sack(-x**2) 
-x*x 
>>> sack(x**2*(1 + x**3) 
x*x*(x*x*x + 1)