La multiplicación entre impulso δ(t) o escalón unitario μ(t) aparece al desarrollar en integral de convolución cuando ambas señales son de tipo causal.
x(t) \circledast h(t) = \int_{-\infty}^{+\infty} x(\tau)h(t-\tau) \delta \tau
Las expresiones resultantes pueden incluir algunas de éstas operaciones como se muestra en el ejemplo 3 de una convolución entre señales tipo rectangular y rampa de carácter causal.
x(t) = \mu (t) - \mu (t-1)
h(t) = t\mu (t) - t\mu (t-2)
Con respuestas al usar el algoritmo con Sympy que integran varias multiplicaciones de escalón unitario.
Como Sympy ‘1,11,1’ revisada en Enero del 2023, aún no incluye este tipo de operaciones, se realizan dos funciones: simplifica_impulso() y simplifica_escalon(). Se desarrolla como un ejercicio de análisis de expresiones con Sympy.
>>> sym.__version__
'1.11.1'
Simplificar multiplicación de impulso unitario δ(t)
Las operaciones encontradas en los ejercicios del curso dan una idea básica de por dónde empezar. También se probó las instrucciones doit(), evalf() sin cambios.
>>> sym.DiracDelta(t)*sym.DiracDelta(t-1)
DiracDelta(t)*DiracDelta(t - 1)
>>> sym.simplify(sym.DiracDelta(t)*sym.DiracDelta(t-1))
DiracDelta(t)*DiracDelta(t - 1)
>>> sym.DiracDelta(t)**2
DiracDelta(t)**2
>>> sym.simplify(sym.DiracDelta(t)**2)
DiracDelta(t)**2
>>>
Como punto de partida de plantea encontrar todos los puntos de la expresión donde intervienen los impulsos. La función busca_impulso() permite revisar si existiendo impulsos en diferentes tiempos, el resultado deberá ser cero.
x = d
h = d.subs(t,t-1)
xh = x*h
Para facilitar el análisis se realizan las gráficas de las dos funcines que se multiplican y la operación resultante del algoritmo:

otro caso a considerar es:
x = sym.sin(t)
h = d.subs(t,t-2)
xh = x*h

Instrucciones con Python
El algoritmo se desarrolla como funciones, para ser incorporadas a telg1001.py
# Simplificar multiplicacion de impulso unitario d(t)*d(t-1)
# https://blog.espol.edu.ec/telg1001/simplificar-multiplicacion-impulso-o-escalon-unitario-sympy/
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
equivalentes = [{'DiracDelta': lambda x: 1*(x==0)},
{'Heaviside': lambda x,y: np.heaviside(x, 1)},
'numpy',]
import telg1001 as fcnm
# INGRESO
t = sym.Symbol('t',real=True)
tau = sym.Symbol('tau',real=True)
u = sym.Heaviside(t)
d = sym.DiracDelta(t)
# entrada x(t), respuesta impulso h(t)
# x = d
# h = d.subs(t,t-1)
# h = d # h= d**2
# x = 2
# h = d
# h = sym.pi*d.subs(t,t-2)
x = sym.sin(t)
h = d.subs(t,t-2)
# h = 5*d.subs(t,t-2)**2
# x = d.subs(t,t-1)
# h = u.subs(t,t-1)
# h = d*u.subs(t,t-2)+ u*u.subs(t,t-2)
# h = d*d.subs(t,t-1)*u.subs(t,t+2)+3
# grafica intervalo [t_a,t_b] plano simétrico
t_b = 4 ; t_a = -t_b
muestras = 81
# PROCEDIMIENTO
def simplifica_impulso(ft):
''' simplifica d**2, d(t-1)*d
'''
def simplifica_d_d(ft):
'''un termino de suma d**2, d(t+1)*d,
'''
respuesta = ft
if ft.has(sym.DiracDelta):# tiene impulsos
impulso_en = fcnm.busca_impulso(ft)
if len(impulso_en)==0: # anulado por d(t-a)*d(t-b)
respuesta = 0*t
elif len(impulso_en)>0: # tiene impulsos
respuesta = 1
factor_mul = sym.Mul.make_args(ft)
for factor_k in factor_mul:
if not(factor_k.has(sym.DiracDelta)):
if not(factor_k.has(sym.Heaviside)):
termino = factor_k.subs(t,impulso_en[0])
else: # tiene escalón
termino = factor_k.subs(t,impulso_en[0])
if termino == 1/2: #coinciden d,u
termino = 1
respuesta = respuesta*termino
else: # factor con impulso
if factor_k.is_Pow: # tiene exponente
respuesta = respuesta*factor_k.args[0]
else: # termino sin exponente
respuesta = respuesta*factor_k
return(respuesta)
# revisa terminos suma
respuesta = 0*t
ft = sym.expand(ft,t)
term_suma = sym.Add.make_args(ft)
for term_k in term_suma:
respuesta = respuesta + simplifica_d_d(term_k)
return(respuesta)
xh = x*h
ft = simplifica_impulso(xh)
# SALIDA
print('xh inicial:')
sym.pprint(xh)
print('h simplificado:')
sym.pprint(ft)
# grafica
figura = fcnm.graficar_xh_y(x,h,ft,t_a,t_b,muestras,y_nombre='xh')
plt.show()
Simplificar multiplicación de escalón unitario μ(t)
Las operaciones encontradas en los ejercicios del curso dan una idea básica de por dónde empezar
>>> sym.Heaviside(t)*sym.Heaviside(t-1)
Heaviside(t)*Heaviside(t - 1)
>>> sym.simplify(sym.Heaviside(t)*sym.Heaviside(t-1))
Heaviside(t)*Heaviside(t - 1)
>>> sym.simplify(sym.Heaviside(-t+1)*sym.Heaviside(t-1))
Heaviside(1 - t)*Heaviside(t - 1)
>>> sym.simplify(sym.Heaviside(t)**2)
Heaviside(t)**2
>>> sym.simplify(sym.DiracDelta(t-1)*sym.Heaviside(t))
DiracDelta(t - 1)*Heaviside(t)
>>>
Como punto de partida de plantea encontrar todos los puntos de la expresión donde intervienen el escalón unitario. La función busca_escalon() permite revisar la ubicación y el sentido de desarrollo de cada escalón unitario, con estos datos se puede determinar si la función se anula con la otra o la parte que permanece.
x = u
h = u.subs(t,t-1)
xh = x*h
Con una grafica se puede comprobar el escalón que se superpone en la multiplicación de x(t)h(t).

otro ejercicio a considerar es cuando los escalones unitarios se pueden complementar o anular. En el siguiente ejemplo x(t) h(t) se anulan entre si.
x = u.subs(t,t-1)
h = u.subs(t,-t-1)
xh = x*h

mientras en en otro caso, las señales tienen una región donde pueden coexistir
x = u.subs(t,-t+1)
h = u.subs(t,t-0)

Instrucciones con Python
# Simplificar multiplicacion de escalon unitario u(t)*u(t-1)
# https://blog.espol.edu.ec/telg1001/simplificar-multiplicacion-impulso-o-escalon-unitario-sympy/
import numpy as np
import matplotlib.pyplot as plt
import sympy as sym
equivalentes = [{'DiracDelta': lambda x: 1*(x==0)},
{'Heaviside': lambda x,y: np.heaviside(x, 1)},
'numpy',]
import telg1001 as fcnm
# INGRESO
t = sym.Symbol('t',real=True)
u = sym.Heaviside(t)
d = sym.DiracDelta(t)
# entrada x(t), respuesta impulso h(t)
# x = u
# h = u.subs(t,t-1)
# x = u.subs(t,t-1)
# h = u.subs(t,-t-1)
x = u.subs(t,-t+1)
h = u.subs(t,t-0)
#x = 3*u.subs(t,-t+2)
#h = 2*u.subs(t,-t+3)
# x = u.subs(t,t+1)*u.subs(t,-t+2)
# h = 1
# h = d.subs(t,-t+2)#*u.subs(t,t-3)
# grafica intervalo [t_a,t_b] plano simétrico
t_b = 4 ; t_a = -t_b
muestras = 301
# PROCEDIMIENTO
def simplifica_escalon(ft):
''' simplifica multiplicaciones
Heaviside(t-a)*Heaviside(t-b) en f(t)
'''
def simplifica_u_u(ft):
'''solo dos pares de u(t-1)*u(t-2),
sin coeficientes
'''
donde_u = fcnm.busca_escalon(ft)
donde_u = np.array(donde_u)
# direccion donde_[:,1],
# lugar donde[:,0]
# analiza multiplicación
resultado = ft # todo igual
if donde_u[0,1]*donde_u[1,1] > 0: # direccion igual
k = 0
if donde_u[0,1]>0: # hacia derecha
k = np.argmax(donde_u[:,0])
k_signo = 1
else: # hacia izquierda
k = np.argmin(donde_u[:,0])
k_signo = -1
ubica = donde_u[k,1]*t-k_signo*donde_u[k][0]
resultado = sym.Heaviside(ubica)
else: # direccion diferente
if donde_u[0][1]>0 and (donde_u[0,0]>donde_u[1,0]):
resultado = 0
if donde_u[0][1]<0 and (donde_u[0,0]<=donde_u[1,0]):
resultado = 0
return(resultado)
def simplifica_u_term(ft):
''' simplifica un termino de varios
factores que multiplican 2*pi*u*u(t-1)
'''
respuesta = ft
if ft.has(sym.Heaviside): # tiene escalon
escalon_en = fcnm.busca_escalon(ft)
revisa = 1 ; otros = 1 ; cuenta = 0
factor_mul = sym.Mul.make_args(ft)
for factor_k in factor_mul:
if factor_k.has(sym.Heaviside):
if factor_k.is_Pow: # con exponente
revisa = revisa*factor_k.args[0]
cuenta = cuenta + 1
else: # sin exponente
revisa = revisa*factor_k
cuenta = cuenta + 1
if cuenta>1: # simplificar
revisa = simplifica_u_u(revisa)
cuenta = len(fcnm.busca_escalon(revisa))
else: # factor sin Heaviside
otros = otros*factor_k
respuesta = otros*revisa
return(respuesta)
# revisa terminos suma
respuesta = 0*t
ft = sym.expand(ft,t)
if ft.has(sym.DiracDelta): # tiene impulsos
ft = fcnm.simplifica_impulso(ft)
term_suma = sym.Add.make_args(ft)
for term_k in term_suma:
respuesta = respuesta + simplifica_u_term(term_k)
return(respuesta)
xh = x*h
ft = simplifica_escalon(xh)
# SALIDA
print('busca_impulso(ft)', fcnm.busca_impulso(h))
print('busca_escalon(ft)', fcnm.busca_escalon(h))
print('x*h inicial:')
sym.pprint(xh)
print('simplificado:')
sym.pprint(ft)
# grafica
figura = fcnm.graficar_xh_y(x,h,ft,t_a,t_b,y_nombre='xh')
plt.show()
..