import streamlit as st
import numpy as np
import pandas as pd
import altair as alt
from nllsm import NonlinearLeastSquares
# Función no lineal 1: exponencial
func1 = lambda x, p: (p[0] * np.exp(p[1] * x))
f1a = lambda x,p: (-np.exp(p[1] * x)) # Funciones derivadas
f1b = lambda x,p: (-p[0] * x * np.exp(p[1] * x))
# Función no lineal 2: Fisher
func2 = lambda x, p: (1 / (1+p[0]*np.exp(-p[1]*x)))
f2a = lambda x, p: (-np.exp(-p[1] * x) / (1 + p[0] * np.exp(-p[1] * x))**2)
f2b = lambda x, p: ((p[0] * x * np.exp(-p[1] * x)) / (1 + p[0] * np.exp(-p[1] * x))**2)
# Funcion no lineal 3: Gompertz
func3 = lambda x, p: p[0] * np.exp(-p[1] * np.exp(-p[2] * x))
f3a = lambda x, p: np.exp(-p[1] * np.exp(-p[2] * x))
f3b = lambda x, p: -p[0] * np.exp(-p[1] * np.exp(-p[2] * x)) * np.exp(-p[2] * x)
f3c = lambda x, p: p[0] * p[1] * x * np.exp(-p[1] * np.exp(-p[2] * x)) * np.exp(-p[2] * x)
# Funcion no lineal 4: Landau
func4 = lambda x, p: np.sqrt(0.5 + 0.5 * np.tanh(p[0] * x - p[1]))
f4a = lambda x, p: 0.5 * x * (1 / np.cosh(p[0] * x - p[1]))**2 / np.sqrt(0.5 + 0.5 * np.tanh(p[0] * x - p[1]))
f4b = lambda x, p: -0.5 * (1 / np.cosh(p[0] * x - p[1]))**2 / np.sqrt(0.5 + 0.5 * np.tanh(p[0] * x - p[1]))
print("\x1b[1;92mStreamlit script running...\x1b[0m")
st.title("Non-Linear Least Square Fit")
st.markdown(
"""
Minimizamos $\sum r^2=\sum(y_i-f(x))^2$ mediante el algoritmo iterativo
Levenberg-Marquardt, que combina el método de Gauss-Newton con una regularización
para mejorar la convergencia:
"""
)
st.latex("\\beta^{k+1}=\\beta^{k}-(J^TJ+\\lambda I)^{-1}J^Tr")
st.markdown(
"""
Donde:
- $J$ es la matriz jacobiana de los residuos respecto a los parámetros
- $\lambda$ es el parámetro de regularización
- $r$ es el vector de residuos
""")
st.header("Funciones")
opciones = [
r'$f(x)=\beta_0e^{\beta_1x},\quad\quad\quad\quad\quad$ (exponencial)',
r'$f(x)=1 / (1 + \beta_0 e^{-\beta_1x}),\quad$ (Fisher)',
r'$f(x) = \beta_0 e^{-\beta_1 e^{-\beta_2 x}},\quad\quad\quad$ (Gompertz)',
r'$f(x) =\sqrt{0.5 + 0.5 \cdot \tanh(a \cdot x - b)},\quad$ (Landau)',
]
# Crear la lista desplegable
seleccion = st.radio('Selecciona una función:', opciones)
indice_seleccionado = opciones.index(seleccion)
modelos = ({"func":func1, "deriv_func":(f1a, f1b)},
{"func":func2, "deriv_func":(f2a, f2b)},
{"func":func3, "deriv_func":(f3a, f3b, f3c)},
{"func":func4, "deriv_func":(f4a, f4b)}
)
func, deriv_func = modelos[indice_seleccionado].values()
val_init = ({"beta_init":np.array([2.0, 0.5]), "x0_xn":np.array([0, 5]), "noise":1},
{"beta_init":np.array([3.7, 1.5]), "x0_xn":np.array([-4, 4]), "noise":0.03},
{"beta_init":np.array([1.0, 1.5, 1.0]), "x0_xn":np.array([-4, 4]), "noise":0.03},
{"beta_init":np.array([3.7, 1.5]), "x0_xn":np.array([-4, 4]), "noise":0.03},
)
# Valores iniciales de los parámetros
beta_init, dominio, w = val_init[indice_seleccionado].values()
x_data = np.linspace(dominio[0], dominio[1], 50)
y_data = func(x_data, beta_init) + w*np.random.randn(x_data.size)
nls = NonlinearLeastSquares(func, deriv_func, x_data, y_data, beta_init)
beta_opt = nls.fit()
print(f"{indice_seleccionado} - {beta_opt}")
np.random.seed(42)
data = pd.DataFrame({
'x': x_data,
'y_scatter': y_data,
'y_line': func(x_data, beta_opt)
})
# Crear el gráfico de dispersión
scatter = alt.Chart(data).mark_circle(color='blue').encode(
x='x',
y='y_scatter'
)
# Crear el gráfico de líneas
line = alt.Chart(data).mark_line(color='red').encode(
x='x',
y='y_line'
)
# Superponer los gráficos
chart = scatter + line
# Mostrar el gráfico combinado
st.altair_chart(chart, use_container_width=True)
st.markdown(
"""
### Author
* [EmiLópez](https://gitlab.com/emilopez)
"""
)