Skip to content

Cost versus probe RMS

Both criteria use the same held-out cells and positive capacities. Rank zero has no variational cost. Cost and probe RMS have different units and are plotted on separate axes; these short sweeps do not qualify a selection policy.

These synthetic examples run at most eight fit iterations. They demonstrate API behavior; they do not establish posterior convergence, calibration or a recommended capacity.

This example uses the 0.5 API with the plot extra. From a matching source checkout, install python -m pip install '.[plot]', then run python scripts/gallery_selection.py --output-dir outputs. The downloaded source can also run directly against that installed 0.5 build.

Download this standalone source.

Output

{
  "selected_capacity": {
    "cost": 3,
    "prms": 3
  },
  "common_probe_cells": 5,
  "records": [
    {
      "k": 1,
      "cost": 189.03411532522344,
      "prms": 0.7860246936967983
    },
    {
      "k": 2,
      "cost": 171.5860896877125,
      "prms": 0.5051694580924303
    },
    {
      "k": 3,
      "cost": 156.97196014329776,
      "prms": 0.41122587681948575
    }
  ]
}

Candidate records

k cost prms
1 189.034 0.786025
2 171.586 0.505169
3 156.972 0.411226

Cost versus probe RMS: selection

Executed source

"""Compare cost and probe-RMS selection on the same held-out observations."""

import argparse
import json
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np

from vbpca_py import SelectionConfig, select_n_components


def run_example(output_dir):
    rng = np.random.default_rng(25)
    x = rng.normal(size=(24, 2)) @ rng.normal(size=(2, 5))
    x += 0.3 * rng.normal(size=x.shape)
    x[0, 0] = np.nan
    probe = np.full_like(x, np.nan)
    for row, column in [(2, 0), (5, 1), (8, 2), (11, 3), (14, 4)]:
        probe[row, column] = x[row, column]
    options = {
        "maxiters": 8,
        "niter_broadprior": 0,
        "random_state": 25,
        "runtime_tuning": "off",
        "num_cpu": 1,
        "verbose": 0,
    }
    selected, traces = {}, {}
    for metric in ["cost", "prms"]:
        chosen, _, trace, _ = select_n_components(
            x,
            xprobe=probe,
            components=[1, 2, 3],
            config=SelectionConfig(metric=metric),
            **options,
        )
        selected[metric] = chosen
        traces[metric] = trace
    fig, axes = plt.subplots(1, 2, figsize=(8, 3.5))
    for ax, metric, label in zip(
        axes,
        ["cost", "prms"],
        ["Variational cost", "Probe RMS (input units)"],
        strict=True,
    ):
        trace = traces[metric]
        ax.plot(
            [record["k"] for record in trace],
            [record[metric] for record in trace],
            "o-",
        )
        ax.set(
            xlabel="Component capacity",
            ylabel=label,
            xticks=[1, 2, 3],
            title=f"Selected capacity: {selected[metric]}",
        )
    fig.tight_layout()
    output_dir.mkdir(parents=True, exist_ok=True)
    fig.savefig(output_dir / "selection.png", dpi=130)
    plt.close(fig)
    summary = {
        "selected_capacity": selected,
        "common_probe_cells": int(np.isfinite(probe).sum()),
        "records": [
            {key: record[key] for key in ["k", "cost", "prms"]}
            for record in traces["cost"]
        ],
    }
    (output_dir / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
    print(json.dumps(summary, indent=2))


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--output-dir", type=Path, required=True)
    run_example(parser.parse_args().output_dir)