Skip to main content
Ctrl+K

KKF documentation

  • API Reference
  • Examples
  • GitHub
  • API Reference
  • Examples
  • GitHub

Section Navigation

  • Basic example: Simple Linear System
  • SIR Epidemic Model
  • Advanced: Parameter Optimization with Grid Search
  • Examples
  • Advanced: Parameter Optimization with Grid Search

Note

Go to the end to download the full example code.

Advanced: Parameter Optimization with Grid Search#

This example demonstrates how to optimize kernel parameters and explore the performance of the KKF with different configurations.

Filter Performance vs Number of Features, Error Heatmap (lower is better)
======================================================================
KKF Advanced Example: Parameter Exploration
======================================================================

Nonlinear System created:
  Dynamics: x[k+1] = [0.5*x[0] + sin(x[1]), 0.7*x[1] - 0.1*x[0]²]
  Observation: y[k] = x[0] + 0.1*x[1]²

Generating synthetic data...
  ✓ Generated 80 timesteps

Exploring different kernel configurations...

  Testing with 10 features:
    Testing RBF (0.5)... ✓ Error: 0.246964
    Testing RBF (1.0)... ✓ Error: 0.197893
    Testing RBF (2.0)... ✓ Error: 0.157648
    Testing Matern (0.5, ν=0.5)... ✓ Error: 0.188136
    Testing Matern (1.0, ν=0.5)... ✓ Error: 0.153730
    Testing Matern (1.0, ν=1.5)... ✓ Error: 0.167921
    Testing Matern (1.0, ν=2.5)... ✓ Error: 0.464270
    Testing ExpSineSquared... /home/docs/checkouts/readthedocs.org/user_builds/kkf/envs/latest/lib/python3.11/site-packages/sklearn/gaussian_process/kernels.py:2072: RuntimeWarning: invalid value encountered in sin
  -2 * (np.sin(np.pi / self.periodicity * dists) / self.length_scale) ** 2
/home/docs/checkouts/readthedocs.org/user_builds/kkf/checkouts/latest/examples/03_advanced_parameter_exploration.py:51: RuntimeWarning: overflow encountered in scalar power
  return np.array([x[0] + 0.1 * x[1] ** 2])
/home/docs/checkouts/readthedocs.org/user_builds/kkf/envs/latest/lib/python3.11/site-packages/numpy/lib/_function_base_impl.py:2882: RuntimeWarning: invalid value encountered in subtract
  X -= avg[:, None]
  Error: SVD did not converge
✗ Failed

  Testing with 20 features:
    Testing RBF (0.5)... ✓ Error: 0.162378
    Testing RBF (1.0)... ✓ Error: 0.153422
    Testing RBF (2.0)... ✓ Error: 1.057204
    Testing Matern (0.5, ν=0.5)... ✓ Error: 0.155281
    Testing Matern (1.0, ν=0.5)... ✓ Error: 0.150585
    Testing Matern (1.0, ν=1.5)... ✓ Error: 0.155577
    Testing Matern (1.0, ν=2.5)... ✓ Error: 0.154337
    Testing ExpSineSquared... /home/docs/checkouts/readthedocs.org/user_builds/kkf/checkouts/latest/examples/03_advanced_parameter_exploration.py:46: RuntimeWarning: overflow encountered in scalar power
  0.7 * x[1] - 0.1 * x[0] ** 2
  Error: SVD did not converge
✗ Failed

  Testing with 30 features:
    Testing RBF (0.5)... ✓ Error: 0.153650
    Testing RBF (1.0)... ✓ Error: 0.152816
    Testing RBF (2.0)... ✓ Error: 0.352561
    Testing Matern (0.5, ν=0.5)... ✓ Error: 0.153690
    Testing Matern (1.0, ν=0.5)... ✓ Error: 0.150283
    Testing Matern (1.0, ν=1.5)... ✓ Error: 0.148903
    Testing Matern (1.0, ν=2.5)... ✓ Error: 0.151051
    Testing ExpSineSquared... /home/docs/checkouts/readthedocs.org/user_builds/kkf/envs/latest/lib/python3.11/site-packages/numpy/lib/_function_base_impl.py:2887: RuntimeWarning: overflow encountered in dot
  c = dot(X, X_T.conj())
  Error: SVD did not converge
✗ Failed

======================================================================
Summary of Results (Mean Absolute Error):
======================================================================
Kernel Config                  10 Features     20 Features     30 Features
---------------------------------------------------------------------------
RBF (0.5)                      0.246964        0.162378        0.153650
RBF (1.0)                      0.197893        0.153422        0.152816
RBF (2.0)                      0.157648        1.057204        0.352561
Matern (0.5, ν=0.5)            0.188136        0.155281        0.153690
Matern (1.0, ν=0.5)            0.153730        0.150585        0.150283
Matern (1.0, ν=1.5)            0.167921        0.155577        0.148903
Matern (1.0, ν=2.5)            0.464270        0.154337        0.151051
ExpSineSquared                 nan             nan             nan

======================================================================
Best Configuration: Matern (1.0, ν=1.5) with 30 features
Mean Absolute Error: 0.148903
======================================================================

Generating comparison plots...
  Figure saved as 'kkf_parameter_exploration.png'

======================================================================
Advanced example completed successfully!
======================================================================

import numpy as np
import matplotlib.pyplot as plt
from scipy import stats
from sklearn.gaussian_process.kernels import Matern, RBF, ExpSineSquared

from kkf import DynamicalSystem, KoopmanOperator, apply_koopman_kalman_filter


def evaluate_filter(system, kernel, n_features, observations, initial_dist, noise_samples=50):
    """Evaluate filter performance for a given kernel."""
    try:
        koop = KoopmanOperator(kernel, system)
        solution = apply_koopman_kalman_filter(
            koopman_operator=koop,
            observations=observations,
            initial_distribution=initial_dist,
            n_features=n_features,
            optimize=False,
            noise_samples=noise_samples,
        )
        return solution
    except Exception as e:
        print(f"  Error: {e}")
        return None


def main():
    """Run advanced optimization example."""
    print("=" * 70)
    print("KKF Advanced Example: Parameter Exploration")
    print("=" * 70)

    # Create nonlinear system
    def f(x):
        """Nonlinear dynamics."""
        return np.array([
            0.5 * x[0] + np.sin(x[1]),
            0.7 * x[1] - 0.1 * x[0] ** 2
        ])

    def g(x):
        """Nonlinear observation."""
        return np.array([x[0] + 0.1 * x[1] ** 2])

    nx, ny = 2, 1
    X_dist = stats.multivariate_normal(mean=np.zeros(nx), cov=np.eye(nx))
    dyn_noise = stats.multivariate_normal(mean=np.zeros(nx), cov=0.01 * np.eye(nx))
    obs_noise = stats.multivariate_normal(mean=np.zeros(ny), cov=0.02 * np.eye(ny))

    system = DynamicalSystem(
        nx=nx,
        ny=ny,
        f=f,
        g=g,
        dist_X=X_dist,
        dist_dyn=dyn_noise,
        dist_obs=obs_noise,
        discrete_time=True,
    )

    print(f"\nNonlinear System created:")
    print(f"  Dynamics: x[k+1] = [0.5*x[0] + sin(x[1]), 0.7*x[1] - 0.1*x[0]²]")
    print(f"  Observation: y[k] = x[0] + 0.1*x[1]²")

    # Generate data
    print(f"\nGenerating synthetic data...")
    n_timesteps = 80
    x_true = np.zeros((n_timesteps, nx))
    y_meas = np.zeros((n_timesteps, ny))

    x_true[0] = np.array([1.0, 0.5])
    y_meas[0] = g(x_true[0]) + obs_noise.rvs()

    np.random.seed(42)
    for t in range(1, n_timesteps):
        x_true[t] = f(x_true[t - 1]) + dyn_noise.rvs()
        y_meas[t] = g(x_true[t]) + obs_noise.rvs()

    print(f"  ✓ Generated {n_timesteps} timesteps")

    # Kernel parameter exploration
    print(f"\nExploring different kernel configurations...")

    kernels_to_test = {
        "RBF (0.5)": RBF(length_scale=0.5),
        "RBF (1.0)": RBF(length_scale=1.0),
        "RBF (2.0)": RBF(length_scale=2.0),
        "Matern (0.5, ν=0.5)": Matern(length_scale=0.5, nu=0.5),
        "Matern (1.0, ν=0.5)": Matern(length_scale=1.0, nu=0.5),
        "Matern (1.0, ν=1.5)": Matern(length_scale=1.0, nu=1.5),
        "Matern (1.0, ν=2.5)": Matern(length_scale=1.0, nu=2.5),
        "ExpSineSquared": ExpSineSquared(length_scale=1.0, periodicity=1.0),
    }

    n_features_list = [10, 20, 30]
    results = {}

    for n_feat in n_features_list:
        results[n_feat] = {}
        print(f"\n  Testing with {n_feat} features:")

        for kernel_name, kernel in kernels_to_test.items():
            print(f"    Testing {kernel_name}...", end=" ")

            solution = evaluate_filter(
                system,
                kernel,
                n_feat,
                y_meas,
                stats.multivariate_normal(mean=x_true[0], cov=0.1 * np.eye(nx)),
                noise_samples=50,
            )

            if solution is not None:
                error = np.mean(np.linalg.norm(solution.x_plus - x_true, axis=1))
                results[n_feat][kernel_name] = error
                print(f"✓ Error: {error:.6f}")
            else:
                results[n_feat][kernel_name] = np.nan
                print(f"✗ Failed")

    # Print summary
    print(f"\n" + "=" * 70)
    print("Summary of Results (Mean Absolute Error):")
    print("=" * 70)
    print(f"{'Kernel Config':<30} {'10 Features':<15} {'20 Features':<15} {'30 Features':<15}")
    print("-" * 75)

    for kernel_name in kernels_to_test.keys():
        e10 = results[10].get(kernel_name, np.nan)
        e20 = results[20].get(kernel_name, np.nan)
        e30 = results[30].get(kernel_name, np.nan)

        print(
            f"{kernel_name:<30} {e10:<15.6f} {e20:<15.6f} {e30:<15.6f}"
        )

    # Find best configuration
    best_config = None
    best_error = np.inf

    for n_feat, kernel_results in results.items():
        for kernel_name, error in kernel_results.items():
            if not np.isnan(error) and error < best_error:
                best_error = error
                best_config = (n_feat, kernel_name)

    print("\n" + "=" * 70)
    if best_config:
        print(f"Best Configuration: {best_config[1]} with {best_config[0]} features")
        print(f"Mean Absolute Error: {best_error:.6f}")
    print("=" * 70)

    # Visualization of results
    print(f"\nGenerating comparison plots...")

    fig, axes = plt.subplots(1, 2, figsize=(14, 5))

    # Plot 1: Error vs number of features for different kernels
    for kernel_name in ["RBF (1.0)", "Matern (1.0, ν=0.5)", "Matern (1.0, ν=2.5)"]:
        errors = []
        features = sorted(results.keys())
        for n_feat in features:
            err = results[n_feat].get(kernel_name, np.nan)
            errors.append(err if not np.isnan(err) else None)

        # Filter out None values
        valid_idx = [i for i, e in enumerate(errors) if e is not None]
        if valid_idx:
            axes[0].plot(
                [features[i] for i in valid_idx],
                [errors[i] for i in valid_idx],
                "o-",
                label=kernel_name,
                linewidth=2,
                markersize=8,
            )

    axes[0].set_xlabel("Number of Features")
    axes[0].set_ylabel("Mean Absolute Error")
    axes[0].set_title("Filter Performance vs Number of Features")
    axes[0].legend()
    axes[0].grid(True, alpha=0.3)

    # Plot 2: Heatmap of errors
    kernel_names = list(kernels_to_test.keys())
    error_matrix = np.zeros((len(kernel_names), len(n_features_list)))

    for i, kernel_name in enumerate(kernel_names):
        for j, n_feat in enumerate(n_features_list):
            error_matrix[i, j] = results[n_feat].get(kernel_name, np.nan)

    im = axes[1].imshow(error_matrix, cmap="RdYlGn_r", aspect="auto")
    axes[1].set_xticks(np.arange(len(n_features_list)))
    axes[1].set_yticks(np.arange(len(kernel_names)))
    axes[1].set_xticklabels(n_features_list)
    axes[1].set_yticklabels(kernel_names, fontsize=9)
    axes[1].set_xlabel("Number of Features")
    axes[1].set_title("Error Heatmap (lower is better)")

    # Add text annotations
    for i in range(len(kernel_names)):
        for j in range(len(n_features_list)):
            text = axes[1].text(
                j,
                i,
                f"{error_matrix[i, j]:.4f}",
                ha="center",
                va="center",
                color="black" if error_matrix[i, j] < np.nanmax(error_matrix) / 2 else "white",
                fontsize=8,
            )

    cbar = plt.colorbar(im, ax=axes[1])
    cbar.set_label("Mean Absolute Error")

    plt.tight_layout()
    plt.savefig("kkf_parameter_exploration.png", dpi=100, bbox_inches="tight")
    print("  Figure saved as 'kkf_parameter_exploration.png'")

    plt.show()

    print("\n" + "=" * 70)
    print("Advanced example completed successfully!")
    print("=" * 70)


if __name__ == "__main__":
    main()

Total running time of the script: (0 minutes 2.078 seconds)

Download Jupyter notebook: 03_advanced_parameter_exploration.ipynb

Download Python source code: 03_advanced_parameter_exploration.py

Download zipped: 03_advanced_parameter_exploration.zip

Gallery generated by Sphinx-Gallery

previous

SIR Epidemic Model

Show Source

© Copyright 2026, Diego Olguin-Wende.

Created using Sphinx 9.0.4.

Built with the PyData Sphinx Theme 0.19.0.