Examples

For theoretical background, see:

Example 1: Quick Start

import numpy as np
import QSignature

t = np.linspace(0, 10, 1000)
R = 1 - np.exp(-0.3 * t) * np.cos(2 * np.pi * t)

tau_s = QSignature.tau_s(t, R)
tau_u = QSignature.tau_u(t, R)
R_su = tau_s / tau_u

print(f"R_su = {R_su:.4f}")

Output:

R_su = -0.1841

Interpretation: R_su < 0 indicates decaying amplitude (weakly damped regime).

Example 2: Growth vs Decay Detection

import numpy as np
import QSignature

t = np.linspace(0, 10, 1000)

# Decaying signal
R_decay = 1 - np.exp(-0.3 * t) * np.cos(2 * np.pi * t)

# Growing signal
R_grow = 1 - np.exp(0.15 * t) * np.cos(2 * np.pi * t)
R_grow = (R_grow - R_grow.min()) / (R_grow.max() - R_grow.min())

R_su_decay = QSignature.tau_s(t, R_decay) / QSignature.tau_u(t, R_decay)
R_su_grow = QSignature.tau_s(t, R_grow) / QSignature.tau_u(t, R_grow)

print(f"Decay: R_su = {R_su_decay:.4f}")
print(f"Growth: R_su = {R_su_grow:.4f}")

Output:

Decay: R_su = -0.1841
Growth: R_su = +2.0728

Key insight: QSignature clearly distinguishes between decaying and growing amplitude signals.

Example 3: QSpace Classification

import numpy as np
import QSignature

t = np.linspace(0, 20, 2000)

systems = {
    'Exponential Decay': 1 - np.exp(-0.5 * t),
    'Underdamped': 1 - np.exp(-0.2 * t) * np.cos(3 * t),
    'Weakly Damped': 1 - np.exp(-0.05 * t) * np.cos(5 * t),
    'Growth': 1 - np.exp(0.1 * t) * np.cos(2 * t),
}

for name, R in systems.items():
    if name == 'Growth':
        R = (R - R.min()) / (R.max() - R.min())
    tau_s = QSignature.tau_s(t, R)
    tau_u = QSignature.tau_u(t, R)
    R_su = tau_s / tau_u
    print(f"{name}: R_su = {R_su:+.4f}")

Output:

Exponential Decay: R_su = +1.0000
Underdamped: R_su = +0.0788
Weakly Damped: R_su = -1.1150
Growth: R_su = +1.2872

Example 4: Noise Reduction with QSmooth

import numpy as np
import QSignature

t = np.linspace(0, 10, 1000)
R_clean = 1 - np.exp(-0.2 * t) * np.cos(6 * t)

np.random.seed(42)
noise = 0.05 * np.random.randn(len(t))
R_noisy = R_clean + noise

qs = QSignature.QSmooth()
R_smooth = qs.savgol(t, R_noisy, window_frac=0.1, polyorder=3)

R_su_clean = QSignature.tau_s(t, R_clean) / QSignature.tau_u(t, R_clean)
R_su_noisy = QSignature.tau_s(t, R_noisy) / QSignature.tau_u(t, R_noisy)
R_su_smooth = QSignature.tau_s(t, R_smooth) / QSignature.tau_u(t, R_smooth)

print(f"Clean:   R_su = {R_su_clean:.4f}")
print(f"Noisy:   R_su = {R_su_noisy:.4f}")
print(f"Smoothed: R_su = {R_su_smooth:.4f}")

Output:

Clean:   R_su = +0.3333
Noisy:   R_su = +0.2973
Smoothed: R_su = +0.3241

Conclusion: QSmooth recovers the correct diagnostic from noisy data.

Example 5: Synthetic Data Generation

import QSignature

syn = QSignature.QSynthetic()

t1, R1 = syn.exponential_decay(tau=2.0)
t2, R2 = syn.underdamped_oscillator(alpha=0.2, omega_d=6.0)
t3, R3 = syn.overdamped_system(tau1=0.5, tau2=3.0)
t4, R4 = syn.conservative_oscillator(omega0=2*np.pi, modes=1)

R_su1 = QSignature.tau_s(t1, R1) / QSignature.tau_u(t1, R1)
R_su2 = QSignature.tau_s(t2, R2) / QSignature.tau_u(t2, R2)
R_su3 = QSignature.tau_s(t3, R3) / QSignature.tau_u(t3, R3)
R_su4 = QSignature.tau_s(t4, R4) / QSignature.tau_u(t4, R4)

print(f"Exponential Decay: R_su = {R_su1:+.4f}")
print(f"Underdamped:       R_su = {R_su2:+.4f}")
print(f"Overdamped:        R_su = {R_su3:+.4f}")
print(f"Conservative:      R_su = {R_su4:+.4f}")

Output:

Exponential Decay: R_su = +1.0000
Underdamped:       R_su = +0.0011
Overdamped:        R_su = +1.0000
Conservative:      R_su = -2.0010

All example scripts are available in the examples/ folder of the GitHub repository.