2013-01-10 15 views

risposta

5

Non c'è alcun supporto diretto per questo. SymPy combina automaticamente i termini comuni in una moltiplicazione con l'esponenziazione. L'unico modo per evitare che ciò accada è utilizzare il meccanismo evaluate=False. Per esempio

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

C'è stata una discussione sulla mailing list SymPy un po 'indietro su questa questione esatto (https://groups.google.com/d/topic/sympy/qaJGesRbX_0/discussion). Ho postato del codice lì che lo farà. Lo ripeto qui:

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) 

Ecco come funziona

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

metterò lo stesso avvertimento qui come sulla mailing list: "valutare = False è un po 'di un hack, così sii consapevole che è fragile.Alcune funzioni rivaluteranno l'espressione, convertendola di nuovo in Pow.Altre funzioni si romperanno perché alcune invarianti attese verranno interrotte dall'espressione = False (ad esempio, dubito che factor() funzioni correttamente). "

+0

Questo è pulito e funziona un po 'di tempo. Tuttavia, come dici tu il _evaluate_ kwarg è fragile, spesso trascurato, anche frequentemente nella _expr.subs finale (repl) _ line. Dopo la conversione voglio solo stampare su una stringa C (cioè non eseguirò più operazioni sull'espressione), quindi c'è un modo per forzare il metodo _subs_ a non valutare le espressioni convertite? –

+0

Ok, sembra che l'ho risolto hackerando una copia di _xreplace_ per usare valutare = False internamente ogni volta che rigenera una sottoespressione. Riesci a vedere qualche problema con questo approccio? –

+0

Dovrebbe essere possibile scrivere una versione di xreplace che faccia ciò senza hackerare nulla, cioè come una funzione che accetta un'espressione e una sostituzione come input e restituisce l'output sostituito con 'evaluate = False'. – asmeurer

1

Sembra che non ci sia nulla, fa solo il contrario.

sympy mostra sempre l'uscita nel modo più semplice, così sarà sempre dire:

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

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

E 'possibile disattivare la funzione in modo selettivo? – user1353285

+0

Non ho visto nessuna opzione per visualizzare effettivamente 'x * x'.Ma, chi lo sa, è una biblioteca piuttosto vasta ... – ATOzTOA

0

In seguito alla risposta accettata da Aaron e al mio commento, questa è la versione di xreplace che sto usando al posto della riga finale subs per evitare che le sottoespressioni vengano valutate (e quindi perdere l'espansione della potenza in una catena di moltiplicazioni).

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 

Stavo pensando questa funzionalità potrebbe essere aggiunto al già esistente xreplace nella biblioteca SymPy lasciando ci vuole **kwargs che sono passato alla chiamata expr.func. È qualcosa che ti interessa fare o sarebbe inutilmente complesso per la maggior parte degli utenti? (o ho frainteso il tuo commento sopra e c'è un modo più semplice per farlo?)

0

Altre risposte non gestiscono -x**2 quindi ho usato regex invece di risolvere solo per i poteri di 2. Capisco che questo è un po 'hacky ma ha funzionato per me

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

Procedimento sostituire è adatto a questo compito per semplici espressioni: sarà necessario gestire le cose come 1/x**3 o x**2*(1 + x**2)

>>> 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. Ma se espandi il numeratore e il denominatore delle espressioni e le gestisci separatamente, ciò potrebbe fare ciò che ti serve. E se le basi sono sempre Simboli allora questo simbolo-hackery può fare ancora meglio il trucco:

>>> 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)