sympy var und diff mit math.log verwenden?

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
lefrcom
User
Beiträge: 8
Registriert: Dienstag 18. September 2018, 14:04

Hallo allerseits,
Ich habe eine kurze funktion geschrieben, die mir aus zwei inputs (Variable und Formel) die partielle ableitung nach der Variablen zurück geben soll:

Code: Alles auswählen

from math import log
from sympy import var, diff

def erzeuge_var(name, value):
    globals()[name] = value

def part_abl(va, formel):
    part_abl = []
    for i in va:
        erzeuge_var(i, var(i))
    for i in va:                
        f = eval(formel)
        part_abl.append(f.diff(i))
        
    return part_abl
Über folgenden Aufruf läuft das dann:

Code: Alles auswählen

v  = ['a', 'b']
f = 'a**3+b
part_abl(v, f)

>>> [3*a**2, 1]
wobei ich auch das erhalte, das ich will. Einen Error bekomme ich allerdings, wenn ich mit math. Operatoren wie math.log() arbeite:

Code: Alles auswählen

v = ['a', 'b']
f = 'log(a)'
part_abl(v, f)

>>> Traceback (most recent call last):

  File "<ipython-input-41-e5b07169c79e>", line 18, in <module>
    part_abl(v, f)

  File "<ipython-input-41-e5b07169c79e>", line 12, in part_abl
    f = eval(formel)

  File "<string>", line 1, in <module>

  File "/home/leonfrcom/.local/lib/python3.6/site-packages/sympy/core/expr.py", line 256, in __float__
    raise TypeError("can't convert expression to float")

TypeError: can't convert expression to float
Habt ihr eine Idee, wie ich das Lösen kann?
Der Punkt ist, dass der "eval()" aufruf mit "math.log(x)" arbeiten kann, wenn man x als float oder integer definiert. Kann sympy.diff einfach kein log() ableiten?

Danke für jede Hilfe!

Und ja ich weiß, eval() wird nicht gerne gesehen... allerdings sind glaube ich 'Sicherheitslücken' in meinem kleinen mathe progrämmchen nicht so tragisch :lol:
Benutzeravatar
__blackjack__
User
Beiträge: 13006
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@lefrcom: Kannst Du `eval()` nicht durch `sympy.sympify()` ersetzen‽ Dann brauchst Du auch das erstellen von globalen nicht, was ebenfalls megahässlich ist und Dir potentiell das Programm kaputtmachen kann.

`math.log()` versucht den Logarithmus vom Argument zu berechnen. Dazu muss das Argument mindestens in eine Gleitkommazahl umwandelbar sein. Ein `Symbol` von `sympy` kann man aber nicht in eine konkrete Zahl umwandeln, denn das ist ja ein Platzhalter für beliebige Zahlen:

Code: Alles auswählen

In [12]: type(a)
Out[12]: sympy.core.symbol.Symbol

In [13]: float(a)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-13-6c8cade3f87c> in <module>()
----> 1 float(a)

/usr/local/lib/python3.6/dist-packages/sympy/core/expr.py in __float__(self)
    254         if result.is_number and result.as_real_imag()[1]:
    255             raise TypeError("can't convert complex to float")
--> 256         raise TypeError("can't convert expression to float")
    257 
    258     def __complex__(self):

TypeError: can't convert expression to float

In [14]: import math

In [15]: math.log(a)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-15-c35a2570e118> in <module>()
----> 1 math.log(a)

/usr/local/lib/python3.6/dist-packages/sympy/core/expr.py in __float__(self)
    254         if result.is_number and result.as_real_imag()[1]:
    255             raise TypeError("can't convert complex to float")
--> 256         raise TypeError("can't convert expression to float")
    257 
    258     def __complex__(self):

TypeError: can't convert expression to float
Was Du brauchst ist keine Funktion die den Logarithmus ausrechnet, sondern ein Objekt das so eine Funktion symbolisiert, also `sympy.log`. Das Problem löst sich durch die Verwendung von `sympify()` automatisch:

Code: Alles auswählen

In [16]: sympy.sympify('log(a)')
Out[16]: log(a)

In [17]: sympy.sympify('log(a)').diff(a)
Out[17]: 1/a
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Ohne `eval` gibt es auch kein Problem. Um das nochmal in Code zu packen:

Code: Alles auswählen

import sympy

def part_abl(va, formel):
    formel = sympy.sympify(formel)
    return [formel.diff(i) for i in va]

v  = ['a', 'b']
f = 'a**3+b'
print(part_abl(v, f))
# [3*a**2, 1]
f = 'log(a)**3+b'
print(part_abl(v, f))
# [3*log(a)**2/a, 1]
lefrcom
User
Beiträge: 8
Registriert: Dienstag 18. September 2018, 14:04

Wow das ist ja einfach :lol:
von sympify habe ich gar nichts gewusst... Vielen Dank an euch beide!
lefrcom
User
Beiträge: 8
Registriert: Dienstag 18. September 2018, 14:04

Eine Frage hätte ich noch, dann muss ich das vielleicht nicht ewig suchen... Wie berechne ich denn die ableitung des 10er log? Ich bekomme da folgendes:

Code: Alles auswählen

In [11]: sympy.sympify('log10(a)').diff('a')
Out[11]: Derivative(log10(a), a)
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Logarithmus zu einer anderen Basis schreibt man als `log(a, 10)`.
Antworten