Skip to content
jmineauPublic

About

Flexible Inverse Problem Solver (FIPS)

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

fips logo

Tests Documentation Code Quality codecov License: MIT DOI Ruff Pyrefly

⚠️ Beta: fips is in beta, and its API may still change between releases. The changelog says what changed. Please report any issues or edge cases you encounter.

Bridging Physics and Data

Inverse problems in geophysics and atmospheric science often involve large state spaces, heterogeneous observation networks, and explicit matrix algebra. General-purpose optimization tools tend to make researchers strip away the labels of their data (times, places, instruments) and translate their physical models into bare arrays.

fips (Flexible Inverse Problem Solver) solves inverse problems without losing the labels of your data:

  • Everything is matched by label: every value carries a pandas index (time, latitude, site, ...). The inputs of a problem are matched by their labels, never by position, and a label on one side only is an error that names it.

  • Built from named blocks: a state of fluxes and offsets, observations from towers and satellites. Each block keeps its own labels, and a matrix is built block by block.

  • The standard linear model: observations are the forward operator times the state, plus an offset, plus an error. Plug in your forward operator (a Jacobian you computed), the prior and its covariance, and the observation covariance. Covariances are written as correlation models (exponential, Gaussian, Matérn, Kronecker products) with standard deviations.

  • Fast and exact: each matrix is kept in its cheapest form (dense, sparse, diagonal, or Kronecker factors), and fips computes the exact Gaussian posterior with direct linear algebra, for large state spaces in seconds.

  • Past the linear Gaussian problem: bounds on the state, forward models that are not linear (with ready-made ones that keep a quantity positive), models that can only be run, and problems too large to factor, each a method of the same fips.solve on the same Problem; and covariance hyperparameters fitted from the data, with their uncertainty carried into the answer.

Installation

pip install fips            # the solver
pip install "fips[viz]"     # with plot_comparison (matplotlib)

For development:

git clone https://github.com/jmineau/fips.git
cd fips
uv sync

Usage

Estimate monthly CO₂ fluxes on a small grid from a year of tower measurements:

import numpy as np
import pandas as pd

import fips

rng = np.random.default_rng(0)  # made-up data, to show the calls

# The state: monthly fluxes on a 3 x 3 grid, labeled by (time, lat, lon)
cells = pd.MultiIndex.from_product(
    [
        pd.date_range("2024-01", periods=12, freq="MS"),
        [40.6, 40.7, 40.8],
        [-112.0, -111.9, -111.8],
    ],
    names=["time", "lat", "lon"],
)
prior = fips.Vector.from_series(pd.Series(1.0, index=cells), block="flux")

# The observations: CO₂ (ppm) at one tower every 6 hours
times = pd.Index(pd.date_range("2024-01-01", "2024-12-31", freq="6h"), name="obs_time")
co2 = pd.Series(rng.normal(420.0, 1.0, len(times)), index=times)
obs = fips.Vector.from_series(co2, block="co2")

# The forward operator: a transport Jacobian with labeled rows and columns
jacobian = pd.DataFrame(
    rng.uniform(0.0, 0.5, (len(times), len(cells))), index=times, columns=cells
)
forward = fips.Operator.from_blocks(
    {("co2", "flux"): jacobian}, rows=obs.space, cols=prior.space
)

problem = fips.Problem(
    prior=prior,
    prior_cov=fips.Independent().with_std(0.5),  # prior flux error
    obs=obs,
    obs_cov=fips.Independent().with_std(1.0),  # model-data mismatch (ppm)
    forward=forward,
    offset=415.0,  # the background concentration
)
post = fips.solve(problem)

print(post.estimate.to_series("flux"))  # posterior fluxes, labeled by (time, lat, lon)
print(post.summary())  # DOFS, chi-squared, R², RMSE, ... in one row

# The monthly mean flux, prior and posterior, with one standard deviation (needs fips[viz])
from fips.visualization import plot_comparison

monthly = fips.aggregate.mean(prior.space, by="time")
fig, ax = plot_comparison(
    (monthly @ prior).to_series().droplevel("block").rename("prior"),
    (monthly @ post.estimate).to_series().droplevel("block").rename("posterior"),
    errors=[problem.prior_cov.aggregate(monthly), post.cov(monthly)],
)

The prior and the observations are Vectors, the forward operator is an Operator, the covariances are put on the labels by the Problem, and fips.solve returns the Posterior. Everything is matched by label: the Jacobian's rows and columns could come in any order.

More in the documentation:

  • A first problem, step by step, with a state of two blocks.
  • The user guide: building a problem (correlated covariances, sparse Jacobians, offsets, aggregated observations), reading the posterior, and choosing a method for bounds, nonlinear models and large problems.
  • A regional flux inversion, worked from start to finish.

Documentation

Full documentation is available at https://jmineau.github.io/fips/

Contributing

Contributions are welcome! Please see CONTRIBUTING.md for guidelines.

Use of AI coding agents

This project is developed with the help of AI coding agents, directed and reviewed by the maintainer, who owns the design and the science.

About

Flexible Inverse Problem Solver (FIPS)

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages