Ich frage mich nur, ob es eine bestehende Methode der Erweiterung von algebraischen Potenzen wie x**2
zu ihren multiplikativen Formen (d. H. x**2 -> x*x
) in Pythons Sympy-Modul?Erweiterung der algebraischen Kräfte in Python (Sympy)
Danke!
Ich frage mich nur, ob es eine bestehende Methode der Erweiterung von algebraischen Potenzen wie x**2
zu ihren multiplikativen Formen (d. H. x**2 -> x*x
) in Pythons Sympy-Modul?Erweiterung der algebraischen Kräfte in Python (Sympy)
Danke!
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). "
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
Ist es möglich, die Funktion selektiv auszuschalten? – user1353285
Ich habe keine Optionen gesehen, um 'x * x' tatsächlich anzuzeigen.Aber wer weiß, es ist eine ziemlich große Bibliothek ... – ATOzTOA
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?)
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
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)
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? –
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? –
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