API Reference¶
This page documents simplesvgd's public API -- everything listed in
simplesvgd.__all__.
Core¶
simplesvgd.update ¶
Core SVGD update function with optional preconditioning and hierarchical sigma.
update ¶
update(
x0: NDArray[FloatDType],
gradient_fn: GradientFn[FloatDType]
| MinibatchGradientFn[FloatDType],
config: SVGDConfig[FloatDType] | None = None,
) -> SVGDState[FloatDType]
Update a collection of samples using Stein Variational Gradient Descent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x0
|
ndarray
|
Initial particle positions, shape |
required |
gradient_fn
|
callable
|
Computes gradients of the negative log-probability. Accepts particles
of shape |
required |
config
|
SVGDConfig or None
|
Every other tunable of the run (iteration count, step size,
preconditioner, kernel, bounds, callback, resume, animation, ...).
|
None
|
Returns:
| Type | Description |
|---|---|
SVGDState
|
Final optimizer state. Access |
Source code in src/simplesvgd/update.py
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 | |
simplesvgd.SVGDConfig
dataclass
¶
SVGDConfig(
n_iter: int = 1000,
stepsize: float = 0.001,
bandwidth: float = -1,
preconditioner: str | None = None,
lbfgs: LBFGSConfig = LBFGSConfig(),
svn: SVNConfig = SVNConfig(),
hessian_vector_product: HessianVectorProductFn[
FloatDType
]
| None = None,
step_schedule: str | None = None,
temperature_schedule: str
| Callable[[int], float]
| None = None,
sigma: SigmaConfig = SigmaConfig(),
minibatch_sampler: Callable[[int], BatchIndices]
| None = None,
kernel: str | KernelFn[FloatDType] | None = None,
bounds: tuple[float, float] | None = None,
callback: Callable[[int, SVGDState[FloatDType]], None]
| None = None,
disable_progressbar: bool = False,
resume_from: SVGDState[FloatDType] | None = None,
animation: AnimationConfig[
FloatDType
] = AnimationConfig(),
rerun: RerunConfig[FloatDType] = RerunConfig(),
)
Bases: Generic[FloatDType]
Every tunable of :func:update, grouped into one object.
All fields have defaults, so SVGDConfig() reproduces update()'s
previous behavior with no arguments. Pass a partially-filled instance to
override just what you need, e.g. SVGDConfig(n_iter=500, bandwidth=2.0).
Related tunables are grouped into sub-objects -- L-BFGS settings under
lbfgs, hierarchical noise estimation under sigma, and the legacy
animation under animation -- constructed the same way, e.g.
SVGDConfig(sigma=SigmaConfig(value=0.1, estimate=True)).
Attributes:
| Name | Type | Description |
|---|---|---|
n_iter |
int
|
Number of iterations. |
stepsize |
float
|
Base step size (interpretation depends on |
bandwidth |
float
|
RBF kernel bandwidth. |
preconditioner |
str or None
|
|
lbfgs |
LBFGSConfig
|
L-BFGS preconditioning parameters (only used for |
svn |
SVNConfig
|
Mean-field SVN preconditioning parameters (only used for |
hessian_vector_product |
callable or None
|
Computes a Hessian-vector product, called as
|
step_schedule |
str or None
|
|
temperature_schedule |
str, callable, or None
|
Anneals the data-misfit gradient contribution: the (preconditioned,
sigma-scaled) gradient is multiplied by a temperature in |
sigma |
SigmaConfig
|
Hierarchical likelihood-noise estimation parameters. |
minibatch_sampler |
callable or None
|
Called as |
kernel |
str, KernelFn, or None
|
Kernel type. |
bounds |
tuple[float, float] or None
|
|
callback |
callable or None
|
Called as |
disable_progressbar |
bool
|
Suppress the live progress display (bar, ETA, and a live misfit/sigma/particle-variance/repulsion-ratio stats line). |
resume_from |
SVGDState or None
|
Resume from a previous run's state. When set, the run continues from
|
animation |
AnimationConfig
|
Legacy live-scatter animation parameters. |
rerun |
RerunConfig
|
Live particle + diagnostics visualization via the Rerun viewer,
with a scrubbable timeline (see |
simplesvgd.SVGDState
dataclass
¶
SVGDState(
particles: NDArray[FloatDType],
iteration: int = 0,
lbfgs_states: list[LBFGSState[FloatDType]]
| None = None,
historical_grad: NDArray[FloatDType] | None = None,
data_sigma: float | None = None,
sigma_history: list[float] = list(),
misfit_history: list[float] = list(),
particle_misfit_history: list[list[float]] = list(),
particle_variance_history: list[float] = list(),
repulsion_ratio_history: list[float] = list(),
prev_particles: NDArray[FloatDType] | None = None,
prev_grads: NDArray[FloatDType] | None = None,
)
Bases: Generic[FloatDType]
Complete state of an SVGD run.
This object is returned by :func:simplesvgd.update and can be passed
back via SVGDConfig(resume_from=...) to continue optimization.
Attributes:
particles: Current particle positions, shape (n_particles, n_dims).
iteration: Total number of completed iterations.
lbfgs_states: Per-particle L-BFGS states (None when using AdaGrad).
historical_grad: AdaGrad accumulator (None when using L-BFGS).
data_sigma: Current likelihood noise standard deviation.
sigma_history: data_sigma at each iteration.
misfit_history: Mean misfit across particles at each iteration.
particle_misfit_history: Per-particle misfits at each iteration.
particle_variance_history: Total particle-ensemble variance (trace of
the empirical covariance, i.e. sum of per-dimension variances) at
each iteration -- a cheap proxy for ensemble spread. A value that
shrinks steadily over the run, well below what the target
distribution's actual variance should be, is a variance-collapse
warning sign (see Ba et al., "Understanding the Variance Collapse
of SVGD in High Dimensions", ICLR 2022).
repulsion_ratio_history: Ratio of the repulsive kernel-gradient term's
norm to the attractive term's norm, at each iteration a
displacement is computed (shorter than the other histories -- the
final iteration of any given update() call only records
state, it doesn't step, so each resume_from boundary drops
one more entry than the other histories accumulate). Read
this early in a run, not as a monotonic trend over the whole
run -- the attractive term shrinks toward zero near any converged
mode regardless of collapse, which swamps the ratio's trend late
in a run. A ratio far below 1 in the first few iterations (while
particles are still diffuse and attraction hasn't decayed yet) is
the direct, cheap signature of the collapse mechanism in the
paper above: repulsion already overwhelmed by attraction before
it's had any chance to spread the ensemble out.
prev_particles: Previous particle positions (for deferred L-BFGS update).
prev_grads: Previous gradients (for deferred L-BFGS update).
PyTorch bridge¶
simplesvgd.update_torch ¶
update_torch(
x0: NDArray[FloatDType],
gradient_fn: Callable[
[NDArray[FloatDType]], NDArray[FloatDType]
],
optimizer_class: type[_TorchOptimizerLike],
optimizer_parameters: dict[str, Any] | None = None,
schedulers: list[_TorchSchedulerLike] | None = None,
*,
n_iter: int = 1000,
animate: bool = False,
figure: Figure | None = None,
dimensions_to_plot: list[int] | None = None,
background: Background[FloatDType] | None = None,
disable_progressbar: bool = False,
) -> npt.NDArray[FloatDType]
Update samples using SVGD with a PyTorch optimizer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x0
|
ndarray
|
Initial samples, shape |
required |
gradient_fn
|
callable
|
Computes gradients of the negative log-probability. |
required |
optimizer_class
|
torch.optim.Optimizer subclass
|
PyTorch optimizer to use. |
required |
optimizer_parameters
|
dict or None
|
Keyword arguments forwarded to the optimizer constructor. |
None
|
schedulers
|
list or None
|
Learning rate schedulers to step after each iteration. |
None
|
n_iter
|
int
|
Number of iterations. |
1000
|
animate
|
bool
|
Enable 2D scatter animation. |
False
|
figure
|
matplotlib Figure or None
|
Figure to draw the animation on; created if not given. |
None
|
dimensions_to_plot
|
list of int or None
|
Which two particle dimensions to animate. Defaults to |
None
|
background
|
tuple or None
|
|
None
|
disable_progressbar
|
bool
|
Suppress the tqdm progress bar. |
False
|
Source code in src/simplesvgd/__init__.py
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
Kernels¶
simplesvgd.rbf_kernel_normalized ¶
rbf_kernel_normalized(
particles: NDArray[FloatDType], h: float = -1
) -> tuple[
npt.NDArray[FloatDType], npt.NDArray[FloatDType]
]
RBF kernel with per-dimension normalization for high-dimensional spaces.
In high dimensions (d >> 1), the standard median heuristic produces bandwidth h^2 ~ O(d), which causes the repulsive gradient per dimension to scale as O(1/d) while the attractive gradient stays O(1). This kills particle diversity as d grows.
This variant normalizes each dimension to unit variance before computing pairwise distances and the bandwidth, then maps the kernel gradient back to the original space. The effective bandwidth is dimension-independent, preserving repulsion in spaces with thousands of dimensions (e.g. FWI parameter vectors).
Source code in src/simplesvgd/kernels.py
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
L-BFGS preconditioning¶
simplesvgd.LBFGSState
dataclass
¶
LBFGSState(
s_history: NDArray[FloatDType],
y_history: NDArray[FloatDType],
cursor: int = 0,
count: int = 0,
)
Bases: Generic[FloatDType]
Circular buffer storing L-BFGS curvature pairs (s, y).
Attributes: s_history: Array of shape (m, n) storing s_k = x_{k+1} - x_k vectors. y_history: Array of shape (m, n) storing y_k = g_{k+1} - g_k vectors. cursor: Index where the next pair will be written. count: Number of pairs stored so far (up to m).
simplesvgd.make_lbfgs_state ¶
make_lbfgs_state(
n: int, m: int = 10
) -> LBFGSState[np.float64]
make_lbfgs_state(
n: int, m: int = 10, *, dtype: dtype[FloatDType]
) -> LBFGSState[FloatDType]
make_lbfgs_state(
n: int,
m: int = 10,
*,
dtype: dtype[FloatDType] | type[float64] = np.float64,
) -> LBFGSState[FloatDType] | LBFGSState[np.float64]
Create an empty L-BFGS state with history size m for vectors of length n.
dtype should match the dtype of the gradients/particles this state
will be used with (e.g. particles.dtype), so the two-loop recursion
in :func:lbfgs_direction doesn't get upcast by a mismatched buffer
dtype. Defaults to float64 when omitted.
Source code in src/simplesvgd/lbfgs.py
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | |
simplesvgd.lbfgs_direction ¶
lbfgs_direction(
state: LBFGSState[FloatDType], grad: NDArray[FloatDType]
) -> npt.NDArray[FloatDType]
Compute the L-BFGS search direction via two-loop recursion.
Returns -H_k @ grad where H_k is the L-BFGS approximation to the
inverse Hessian. Falls back to -grad when the history is empty.
Source code in src/simplesvgd/lbfgs.py
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
simplesvgd.lbfgs_update ¶
lbfgs_update(
state: LBFGSState[FloatDType],
s: NDArray[FloatDType],
y: NDArray[FloatDType],
) -> None
Push a new (s, y) pair into the circular buffer.
Skips the update if the curvature condition y.s > 0 is not satisfied.
Source code in src/simplesvgd/lbfgs.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | |
Mean-field SVN preconditioning¶
simplesvgd.SVNConfig
dataclass
¶
SVNConfig(cg_iters: int = 10, damping: float = 1e-06)
Mean-field Stein Variational Newton preconditioning parameters.
Only used when SVGDConfig.preconditioner == "svn", which also
requires SVGDConfig.hessian_vector_product to be set. See
:mod:simplesvgd.svn for what "mean-field" means here and how it
differs from the full Stein Variational Newton algorithm.
Attributes:
| Name | Type | Description |
|---|---|---|
cg_iters |
int
|
Conjugate-gradient iterations used to solve each iteration's shared Newton system. |
damping |
float
|
Tikhonov damping added to the shared curvature operator before
solving, so it stays positive-definite (and the CG solve
well-posed) even when |
simplesvgd.svn_direction ¶
svn_direction(
hessian_vector_product: HessianVectorProductFn[
FloatDType
],
particles: NDArray[FloatDType],
phi: NDArray[FloatDType],
svn_config: SVNConfig,
) -> npt.NDArray[FloatDType]
Newton-precondition phi with the shared, mean-field curvature operator.
Solves (H_mean + damping * I) @ v_i = phi_i for every particle i
via batched CG, where H_mean is hessian_vector_product evaluated at
the particle ensemble's mean position (the same operator for every
particle). phi is the already kernel-combined SVGD direction
(attraction and repulsion together), shape (n_particles, n_dims).
Source code in src/simplesvgd/svn.py
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | |
Utilities¶
simplesvgd.gradient_vectorizer ¶
gradient_vectorizer(
non_vectorized_gradient: Callable[
[NDArray[FloatDType]], NDArray[FloatDType]
],
) -> Callable[
[npt.NDArray[FloatDType]], npt.NDArray[FloatDType]
]
Wrap a single-point gradient function to accept batched inputs.
Source code in src/simplesvgd/__init__.py
161 162 163 164 165 166 167 168 169 | |