SymPy: Warum funktioniert hier das Vereinfachen eines mathematischen Ausdrucks nicht?
Ich schaue mir gerade das Python-Modul SymPy an und versuche mich als erstes Beispiel im fitten einer Funktion f(x) durch ein Funktionsset (fi,) in einem gegebenen Intervall.
Es soll als Ergebnis die gefittete Funktion u(x) herauskommen:
import sympy as sym
def functionFit(f, funcset, interval):
N = len(funcset) - 1
A = sym.zeros(N + 1, N + 1)
b = sym.zeros(N + 1, 1)
x = sym.Symbol('x')
for i in range(N + 1):
for j in range(i, N + 1):
A[i, j] = sym.integrate(
funcset[i] * funcset[j],
(x, interval[0], interval[1]))
A[j, i] = A[i, j]
b[i, 0] = sym.integrate(funcset[i] * f, (x, interval[0], interval[1]))
c = A.LUsolve(b)
u = 0
for i in range(len(funcset)):
u += c[i, 0] * funcset[i]
return u, c
x = sym.Symbol('x')
f = 10 * sym.cos(x) + 3 * sym.sin(x)
fooset = (sym.sin(x), sym.cos(x))
interval = (1, 2)
print("function to approximate:", f)
print("Basic functions:")
for foo in fooset:
print(" - ", foo)
u,c = functionFit(f, fooset, interval)
print()
print("simplified u:")
print(sym.simplify(u))
print()
print("simplified c:")
print(sym.simplify(c))
Wenn ich dieses simple Beispiel laufen lasse, erhalte ich:
function to approximate: 10 * cos(x)
Basic functions:
- sin(x)
- cos(x)
simplified u: 10 * cos(x)
simplified c:
Matrix([[0], [10]])
womit ich zufrieden bin.
Setze ich aber
f = 10sym.cos(x) + 3sym.cos(x)
dann erhalte ich:
function to approximate: 3sin(x) + 10cos(x)
Basic functions:
- sin(x)
- cos(x)
simplified u: (12sin(2)2sin(4)sin(x) + 3sin(8)sin(x) + 12sin(2)sin(x) + 40sin(2)*2sin(4)cos(x) + 10sin(8)cos(x) + 40sin(2)cos(x))/(2(sin(4) + 2*sin(2)))
simplified c:
Matrix([[3], [10]])
Der Ausdruck für u ist natürlich richtig, aber wieso schafft es SymPy nicht, den Ausdruck auf
zu vereinfachen?
Mache ich hier als Anfänger etwas falsch oder kann ich da nicht mehr erwarten? Dann wäre SymPy aber ziemlich unbrauchbar, wenn es schon bei derart elementaren Dingen versagt.
Als Vergleich: Mathematica reduziert das ausgewiesene u(x) schnell und sauber auf den richtigen Ausdruck. Wenn man mit SymPy schon so etwas nicht richtig hinkriegt, lohnt sich die weitere Beschäftigung damit eigentlich nicht. Ich hoffe inbrünstig, dass ich etwas falsch mache. Ich bitte um Hinweise was das sein könnte!
4 Antworten
Ich habe Python mal ausprobiert gehabt, aber da mir die Art und Weise wie in Python Arrays indiziert werden nicht gefallen hat, habe ich diese Programmiersprache nicht mehr weiter verfolgt.
Ich hätte da mal eine Gegenfrage, und zwar -->
Was soll dein Programm eigentlich leisten ?
Soll es
(12sin(2)2sin(4)sin(x) + 3sin(8)sin(x) + 12sin(2)sin(x) + 40sin(2)*2sin(4)cos(x) + 10sin(8)cos(x) + 40sin(2)cos(x))/(2(sin(4) + 2*sin(2)))
zu 3 * sin(x) + 10 * cos(x) vereinfachen, oder wie ist das zu verstehen ?
Oder soll das zu 3 * sin(x) + 10 * cos(x) approximiert werden, wobei der Fehler so gering wie möglich zu halten ist ?
Habe noch ein Beispiel eingefügt, wo es klaglos funktioniert. Neue Eigenantwort.
Vielen Dank für den Stern !
Schade, dass du nicht mehr Antworten bekommen hast, außer meiner.
Anmerkung :
Irgendwas stimmt da nicht -->
1.)
(12 * SIN(2) * 2 * SIN(4) * SIN(x) + 3 * SIN(8) * SIN(x) + 12 * SIN(2) * SIN(x) + 40 * SIN(2) * 2 * SIN(4) * COS(x) + 10 * SIN(8) * COS(x) + 40 * SIN(2) * COS(x)) / (2 * (SIN(4) + 2 * SIN(2)))
ist nicht identisch mit 3 * SIN(x) + 10 * COS(x)
2.)
-1.2413734172586 * SIN(x) - 4.1379113908621 * COS(x)
approximiert viel besser als 3 * SIN(x) + 10 * COS(x)
Hast du dich irgendwo verschrieben ?
Anmerkung :
(12 * SIN(2) * 2 * SIN(4) * SIN(x) + 3 * SIN(8) * SIN(x) + 12 * SIN(2) * SIN(x) + 40 * SIN(2) * 2 * SIN(4) * COS(x) + 10 * SIN(8) * COS(x) + 40 * SIN(2) * COS(x)) / (2 * (SIN(4) + 2 * SIN(2)))
ist auch nicht mit 10 * COS(x) + 3 * COS(x) identisch.
(12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2)))
ist identisch mit
3*sin(x)+10*cos(x)
Du hast meine ** durch * ersetzt, in Python ist das Zeichen für "hoch ^" der Operator "**"
In deinem Originaltext tauchen die ** gar nicht auf, scheinbar hat der GF - Editor die verschluckt.
Wie lautet der Ausdruck denn vernünftig hingeschrieben ?
Ach Gott, der sch... Editor bei GF... das wurde bei copy/paste verschluckt.
Der Ausdruck lautet:
u(x) = (12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2)))
Ja, daran ist der GF-Editor Schuld, ich erinnere mich in der Vergangenheit schon mal ähnliche Probleme gelesen zu haben, die Leute mit dem GF-Editor hatten.
Problem ist, dass man auch nichts mehr ändern kann, wenn die Zeit abgelaufen ist. Das ist jener Faktor, der mich hier am allermeisten stört...Aber das hier habe ich selbst nicht bemerkt.
Jetzt ist aber Dank deines Kommentare für jeden ersichtlich, wie es exakt gemeint ist.
Für die zu fittende Funktion
f(x) = x + (x - 1)² + 1
und den Basisfunktionen (1, x)
findet man für das Intervall [1,2]
den Fit
u(x) = 2*x-1/6
Wie man sieht, ist das die richtige Lösung.
Hier hat SymPy kein Problem und gibt einen Ausdruck, der nicht weiter vereinfacht werden kann.
function to approximate: x + (x - 1)**2 + 1
Basic functions:
- 1
- x
simplified u:
2*x - 1/6
Ich hoffe es wird klar, worauf ich hinaus will.

Hier nochmal das Python Skript in lesbarer Form:
import sympy as sym
def functionFit(f, funcset, interval):
N = len(funcset) - 1
A = sym.zeros(N+1, N+1)
b = sym.zeros(N+1, 1)
x = sym.Symbol('x')
for i in range(N+1):
for j in range(i, N+1):
A[i,j] = sym.integrate(funcset[i]*funcset[j],
(x, interval[0], interval[1]))
A[j,i] = A[i,j]
b[i,0] = sym.integrate(funcset[i]*f, (x, interval[0], interval[1]))
c = A.LUsolve(b)
u = 0
for i in range(len(funcset)):
u += c[i,0]*funcset[i]
return u, c
x = sym.Symbol('x')
f = 10*sym.cos(x)+3*sym.sin(x)
fooset=(sym.sin(x), sym.cos(x))
interval = (1,2)
print("function to approximate:", f)
print("Basic functions:")
for foo in fooset:
print(" - ", foo)
u,c = functionFit(f, fooset, interval)
print()
print("simplified u:")
print(sym.simplify(u))
print()
print("simplified c:")
print(sym.simplify(c))
Setze ich aber
f = 10sym.cos(x)+3sym.sin(x)
meinte ich natürlich.
Es geht hier nicht um ein konkretes Problem, für welches ich ein Skript schreibe, sondern ich probiere SymPy einfach mal quer durch den Gemüsegarten aus.
Die Aufgabe hier wäre:
Gegeben ist eine funktion f(x). Approximiere diese durch eine Linearkombination eines Sets aus Funktionen λ_i(x), sodass die Summe der qauadratischen Abweichungen minimal wird (ein klassischer Funktions-Fit also).
Nun wäre meine Funktion f(x) = 3 * SIN(x) + 10 * COS(x)
und meine Basisfuinktionen
λ1(x) = SIN(x)
λ2(x) = COS(x)
Das Prgramm ermittelt die Koeffizienten c1=3, c2=10 richtig und bekommt den Ausdruck
u(x) = (12*sin(2)**2*sin(4)*sin(x) + 3*sin(8)*sin(x) + 12*sin(2)*sin(x) + 40*sin(2)**2*sin(4)*cos(x) + 10*sin(8)*cos(x) + 40*sin(2)*cos(x))/(2*(sin(4) + 2*sin(2)))
Das ist vereinfacht wieder 3 * SIN(x) + 10 * COS(x); somit funktioniert das Programm (hier ist die Anpassung eben perfekt), nur verstehe ich nicht, wieso SymPy es nicht schafft, den Ausdruck zu vereinfachen.