General purpose exponential function fitting
Project description
bicfit
bicfit is a lightweight Python library for fitting damped cosine and exponential functions. It combines the Generalized Pencil-of-Function method [^1] (to generate robust initial guesses) with standard optimization routines (to refine the fit).
This makes it well-suited for researchers and practitioners dealing with decaying oscillations, resonance signals, or exponential relaxations in noisy data.
Currently, bicfit supports fitting:
- Complex exponential decay $f(t) = x_0 + \sum_k A_k \exp((j\omega_k - \kappa_k) t)$
- Damped cosine $f(t) = x_0 + \sum_k A_k \exp(-\kappa_k t) \cos(\omega_k t + \phi_k)$
- Exponential decay $f(t) = x_0 + \sum_k A_k \exp(-\kappa_k t)$
[^1]: Generalized Pencil-of-Function Method for Extracting Poles of an EM System from Its Transient Response Hua and Sarkar (IEEE TRANSACTIONS ON ANTENNAS AND PROPAGATION, VOL. 37, NO. 2, FEBRUARY 1989)
Installation
You can install bicfit via pip:
pip install bicfit
Usage
Start with
import numpy as np
w = 0.2
kappa = 0.05
offset = 1 + 1j
sigma_noise = 0.03
n_points = 100
Complex exponential decay
from bicfit import fit_complex_exponential
times = np.linspace(0, 150, n_points)
noise = np.random.normal(0, sigma_noise, n_points) + 1j * np.random.normal(0, sigma_noise, n_points)
signal = offset + np.exp((1j * w - kappa) * times) + noise
result = fit_complex_exponential(times, signal)
result.plot()
# you can call `result` directly to evaluate it
fitted_signal = result(times)
result
In this example, result is an object of type ComplexResult that has the following attributes:
offset: the offset of the dataamplitudes: the amplitudes of the modespulsations: the pulsations of the modesdecay_rates: the decay rates of the modestimes: the times of the datasignal: the original signalfrequencies: (read-only) the frequencies of the modes, computed aspulsations / (2 * np.pi)
As a convenience, the result object also has a plot() method that plots the original signal and the fitted function.
It also exposes a modes property that returns a list of Mode objects, each containing the amplitude pulsation and decay rate of the mode.
The ComplexResult and ComplexMode classes (as all Result and Mode classes) are callable so you can use them to
evaluate the fitted function at any time.
Damped cosine
from bicfit import fit_damped_cosine
signal = signal.real
result = fit_damped_cosine(times, signal)
result.plot()
result
Here the result is an object of type DampedCosineResult that has similar attributes to the ComplexResult class:
offset: the offset of the dataamplitudes: the amplitudes of the modesphases: the phases of the modespulsations: the pulsations of the modesdecay_rates: the decay rates of the modestimes: the times of the datasignal: the original signalfrequencies: (read-only) the frequencies of the modes, computed aspulsations / (2 * np.pi)
The DampedCosineResult object also has a plot() method that plots the original signal and the fitted function.
It exposes a modes property that returns a list of DampedCosineMode objects, each containing the amplitude, pulsation, decay rate, and phase of the mode.
Exponential decay
from bicfit import fit_exponential_decay
noise = np.random.normal(0, sigma_noise, n_points) + 1j * np.random.normal(0, sigma_noise, n_points)
signal = offset + np.exp(- kappa * times) + noise
result = fit_exponential_decay(times, signal, is_complex=True)
result.plot()
result
⚠️ Fitting an exponential can be tricky in general. If you do not have data long enough that the exponential plateaus, the fit will not work well. If you know where the exponential plateaus, you do not need as much data but bicfit does not exploit this knowledge yet.
Multiple modes
a1, a2 = 1.0, 0.5
w1, w2 = 0.2, 0.4
kappa1, kappa2 = 0.05, 0.01
noise = np.random.normal(0, sigma_noise, n_points) + 1j * np.random.normal(0, sigma_noise, n_points)
signal = offset + a1 * np.exp((1j * w1 - kappa1) * times) + a2 * np.exp((1j * w2 - kappa2) * times) + noise
result = fit_complex_exponential(times, signal, n_modes=2, with_post_fit=True)
result.plot()
result
Here the result is an object of type ExponentialDecayResultthat has similar attributes to the ComplexResult class:
offset: the offset of the dataamplitudes: the amplitudes of the modespulsations: the decay rates of the modestimes: the times of the datasignal: the original signal
It also has the plot() function, modes attribute and is callable like the other result classes.
Coming features
There are a few features that are not implemented yet but could be in the future, if there is a demand for them:
- exploiting knowledge of the plateau of an exponential decay
- fitting non uniformly sampled data
- port fit functions to JAX
- [performance] amplitude fitting is probably overkill and could be done from the pencil eigenvalues
Feedback
We welcome any feedback, suggestions, or bug reports to help improve bicfit.
If you find a case where the fit fails, please include your data in CSV format for reproducibility.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bicfit-0.1.1.tar.gz.
File metadata
- Download URL: bicfit-0.1.1.tar.gz
- Upload date:
- Size: 13.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
864ff8211f3bbdb0d44dd051fb6faea05d196c51869ac1e02e4af3a4f2d317ed
|
|
| MD5 |
33da8be23da1398cbd3d990d2b3dd5a1
|
|
| BLAKE2b-256 |
2dc24dbda46673a4415b6dcce5e52a2c723f5b70627294f732ac12f28a7fbce6
|
File details
Details for the file bicfit-0.1.1-py3-none-any.whl.
File metadata
- Download URL: bicfit-0.1.1-py3-none-any.whl
- Upload date:
- Size: 16.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.8.22
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
90382016a2f644a9005712e8e7f806f74c80f842259d3ab73878a0738626d489
|
|
| MD5 |
51a661ce2388c29439125dd2c7375808
|
|
| BLAKE2b-256 |
8d5037266e4bbccc620c89aec3714c9446b0573d2be18a747e92dd57f075f2a1
|