kups.core.potential
¶
Potential energy calculations with gradients and Hessians.
This module provides the infrastructure for computing potential energies and their derivatives in molecular simulations. Potentials are composable and can be cached for efficient evaluation.
Key components: - PotentialOut: Container for energy, gradients, and Hessians - Potential: Protocol for energy computation with optional state patches - SummedPotential: Compose multiple potentials by summation - CachedPotential: Cache potential outputs - ScaledPotential: Scale a potential by a constant factor
Potentials support linearity: energies, gradients, and Hessians can be summed, enabling modular force field composition (e.g., bonded + non-bonded + Coulomb).
Every potential can also report the Kahan compensation of its accumulated total
via include_compensate=True, see
Potential.
EMPTY = EmptyType()
module-attribute
¶
Singleton instance of EmptyType.
Use this instead of constructing EmptyType() directly.
EMPTY_LENS = const_lens(EMPTY)
module-attribute
¶
Lens that always returns EMPTY, ignoring input. Set is a no-op.
This is useful for potentials that don't compute gradients or Hessians.
CompensatedPotentialResult = WithPatch[KahanSummand[PotentialOut[Gradients, Hessians]], Patch[State]]
¶
Potential output as an accumulator carrying the compensation of its total.
Energy = Array
¶
Type alias for energy arrays, typically shape (n_systems,).
PotentialResult = WithPatch[PotentialOut[Gradients, Hessians], Patch[State]]
¶
Potential output paired with the patch that commits it to the state.
CachedPotential
¶
Bases: Potential[State, Gradients, Hessians, StatePatch]
Wrap a potential with caching for efficient incremental updates.
Caches the potential output in the state and updates it via patches. Crucial for Monte Carlo simulations where only small perturbations are made and you want to avoid recomputing the entire potential.
Attributes:
| Name | Type | Description |
|---|---|---|
potential |
Potential[State, Gradients, Hessians, StatePatch]
|
Base potential to wrap |
cache |
Lens[State, PotentialOut[Any, Any]]
|
Lens to the cache location in state |
patch_idx_view |
View[State, PotentialOut[Gradients, Hessians]] | None
|
Maps acceptance mask indices to cached structure.
If |
compensate_cache |
Lens[State, PotentialOut[Any, Any]] | None
|
Lens to a second location holding the Kahan
compensation of the cached output. Required for potentials that
accumulate onto the cached total, so that differencing two cached
totals stays exact; |
The patch_idx_view provides the indexing structure matching the potential output, used to selectively update cached values based on acceptance masks.
Example
# Cache LJ potential for MC simulation
cached_lj = CachedPotential(
potential=lj_potential,
cache=lens(lambda s: s.lj_cache),
patch_idx_view=lambda s: s.particle_indices
)
# First call computes and caches
result = cached_lj(state, patch=None)
state = result.patch(state, accept_mask)
# The previous value can be easily accessed
result = cached_lj.cached_value(state)
Source code in src/kups/core/potential.py
436 437 438 439 440 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 | |
__call__(state, patch=None, *, include_compensate=False)
¶
Evaluate potential and update cache.
Computes the base potential, then creates a patch that will update the
cached value when applied with an acceptance mask. The cache update uses
the patch_idx_view to determine which cached entries to modify. When
compensate_cache is set, the compensation is committed alongside the
value so that the next call can difference against a compensated total.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State
|
Current simulation state |
required |
patch
|
StatePatch | None
|
Optional state patch for incremental updates |
None
|
include_compensate
|
bool
|
Return the accumulator rather than its compensated total |
False
|
Returns:
| Type | Description |
|---|---|
PotentialResult[State, Gradients, Hessians] | CompensatedPotentialResult[State, Gradients, Hessians]
|
Potential output with cache update patch composed |
Source code in src/kups/core/potential.py
cached_value(state)
¶
Retrieve the cached potential output from state.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State
|
Simulation state containing cached values |
required |
Returns:
| Type | Description |
|---|---|
KahanSummand[PotentialOut[Gradients, Hessians]]
|
Previously computed and cached potential output. The compensation is |
KahanSummand[PotentialOut[Gradients, Hessians]]
|
the one accumulated up to that point, or zero when no |
KahanSummand[PotentialOut[Gradients, Hessians]]
|
|
Source code in src/kups/core/potential.py
EmptyType
¶
Sentinel type indicating empty gradients or Hessians.
Use this when a potential does not compute gradients or Hessians, rather than None, to maintain type safety.
LinearMappedPotential
¶
Bases: Potential[State, OutGrad, OutHess, StatePatch]
Wrap a potential and transform its gradient and hessian outputs.
Applies mapping functions to gradients and hessians returned by the inner potential, enabling projection (e.g., extracting position gradients from a combined position+lattice gradient structure).
The mapping must be linear: it is applied to the running sum and to the compensation separately, so a cached accumulator survives the mapping. Projections and weighted sums of the output qualify, anything nonlinear does not.
Attributes:
| Name | Type | Description |
|---|---|---|
potential |
Potential[State, InGrad, InHess, StatePatch]
|
Base potential to wrap |
gradient_map |
Potential[State, InGrad, InHess, StatePatch]
|
Function to transform gradients from InGrad to OutGrad |
hessian_map |
Potential[State, InGrad, InHess, StatePatch]
|
Function to transform hessians from InHess to OutHess |
Example
# Extract position gradients from VirialTheoremGradients
position_potential = LinearMappedPotential(
potential=full_potential, # Returns VirialTheoremGradients
gradient_map=lambda g: g.positions,
hessian_map=lambda h: h, # Pass through hessians unchanged
)
result = position_potential(state)
# result.data.gradients is now just the position array
Source code in src/kups/core/potential.py
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 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 | |
__call__(state, patch=None, *, include_compensate=False)
¶
Evaluate the wrapped potential and map its output.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State
|
Current simulation state |
required |
patch
|
StatePatch | None
|
Optional state patch for incremental updates |
None
|
include_compensate
|
bool
|
Return the accumulator rather than its compensated total |
False
|
Returns:
| Type | Description |
|---|---|
PotentialResult[State, OutGrad, OutHess] | CompensatedPotentialResult[State, OutGrad, OutHess]
|
Mapped potential output with the original patch |
Source code in src/kups/core/potential.py
Potential
¶
Bases: Protocol
Protocol for potential energy functions.
A potential computes energy, gradients, and optionally Hessians for a given simulation state. Potentials can optionally accept a state patch describing recent changes, enabling efficient incremental updates.
Class Type Parameters:
| Name | Bound or Constraints | Description | Default |
|---|---|---|---|
State
|
Simulation state type |
required | |
Gradients
|
Structure of first derivatives |
required | |
Hessians
|
Structure of second derivatives (subset of gradients) |
required | |
StatePatch
|
Patch[Any]
|
Type of state modification patches |
required |
The patch argument enables incremental computation:
- Monte Carlo: Only recompute for moved particles
- Molecular dynamics: Reuse neighbor lists
- General: Avoid redundant calculations
Incremental updates accumulate small energy changes onto a large cached total,
which loses low-order bits in single precision. Potentials therefore keep a
KahanSummand internally and expose its
compensation via include_compensate=True. Differencing two compensated
totals (see
KahanSummand.difference)
recovers the energy change exactly, which matters for Monte Carlo acceptance
where the change is far smaller than the total.
Example
class LennardJonesPotential:
def __call__(self, state, patch=None, *, include_compensate=False):
# Compute LJ energy and forces
energy = compute_lj_energy(state.positions)
forces = compute_lj_forces(state.positions)
out = PotentialOut(energy, {"positions": forces}, EMPTY)
if include_compensate:
out = KahanSummand.init(out) # Nothing accumulated, so zero
return WithPatch(out, IdPatch()) # No state caching needed
# Use in simulation
potential = LennardJonesPotential()
result = potential(state)
energy = result.data.total_energies
forces = result.data.gradients.positions
Source code in src/kups/core/potential.py
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | |
__call__(state, patch=None, *, include_compensate=False)
¶
Compute potential energy and derivatives.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State
|
Current simulation state |
required |
patch
|
StatePatch | None
|
Optional state patch for incremental updates |
None
|
include_compensate
|
bool
|
Return the output as a KahanSummand carrying the rounding error accumulated so far. The compensation is zero unless the potential accumulates onto a cached total. |
False
|
Returns:
| Type | Description |
|---|---|
PotentialResult[State, Gradients, Hessians] | CompensatedPotentialResult[State, Gradients, Hessians]
|
Potential output and state patch |
Source code in src/kups/core/potential.py
PotentialAsPropagator
¶
Bases: Propagator[State]
Adapt a potential to the Propagator interface.
Converts a potential into a propagator that computes energies and applies the resulting patch to the state. Useful for integrating potential evaluations into propagator pipelines.
Attributes:
| Name | Type | Description |
|---|---|---|
potential |
Potential[State, Gradients, Hessians, StatePatch]
|
Potential to wrap as a propagator |
Note
The propagator accepts all patches (acceptance mask all True). This is typically used for energy/force evaluations rather than Monte Carlo moves.
Example
Source code in src/kups/core/potential.py
__call__(key, state)
¶
Evaluate potential and apply patch to state.
Computes the potential energy and applies the resulting patch with all acceptance flags set to True (all updates accepted). Ignores the random key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
Array
|
JAX PRNG key (unused) |
required |
state
|
State
|
Current simulation state |
required |
Returns:
| Type | Description |
|---|---|
State
|
Updated state after applying potential patch |
Source code in src/kups/core/potential.py
PotentialOut
¶
Output of a potential energy calculation.
Contains the total energy per system, gradients with respect to specified tensors (e.g., positions, charges), and optionally Hessians (second derivatives).
Assumes linearity: energies, gradients, and Hessians can be summed, enabling composition of multiple potentials via SummedPotential (e.g., U_total = U_bonded + U_vdw + U_elec).
Class Type Parameters:
| Name | Bound or Constraints | Description | Default |
|---|---|---|---|
Gradients
|
PyTree structure containing first derivatives |
required | |
Hessians
|
PyTree structure containing second derivatives (subset of gradients) |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
total_energies |
Table[SystemId, Energy]
|
Total energy per system as a |
gradients |
Gradients
|
First derivatives (e.g., forces = -∇U) |
hessians |
Hessians
|
Second derivatives (e.g., for normal mode analysis) |
Example
# Simple potential output with position gradients only
out = PotentialOut(
total_energies=jnp.array([10.5, 12.3]), # 2 systems
gradients={"positions": force_array}, # Forces on particles
hessians=EMPTY # No Hessians computed
)
# Combine potentials
total = lj_out + coulomb_out # Element-wise addition
Source code in src/kups/core/potential.py
ScaledPotential
¶
Bases: Potential[State, Gradients, Hessians, StatePatch]
Scale a potential's output by a constant factor.
Multiplies energies, gradients, and Hessians by a scalar. Useful for thermodynamic integration, replica exchange, or applying coupling parameters.
Attributes:
| Name | Type | Description |
|---|---|---|
potential |
Potential[State, Gradients, Hessians, StatePatch]
|
Base potential to scale |
scale |
float
|
Multiplicative factor (lambda in thermodynamic integration) |
Example
Source code in src/kups/core/potential.py
__call__(state, patch=None, *, include_compensate=False)
¶
Evaluate potential and scale the output.
Computes the base potential then multiplies energies, gradients, and Hessians by the scale factor. The compensation is scaled along with the value. The patch is passed through unchanged.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State
|
Current simulation state |
required |
patch
|
StatePatch | None
|
Optional state patch for incremental updates |
None
|
include_compensate
|
bool
|
Return the accumulator rather than its compensated total |
False
|
Returns:
| Type | Description |
|---|---|
PotentialResult[State, Gradients, Hessians] | CompensatedPotentialResult[State, Gradients, Hessians]
|
Scaled potential output with original patch |
Source code in src/kups/core/potential.py
SummedPotential
¶
Bases: Potential[State, Gradients, Hessians, StatePatch]
Compose multiple potentials by summing their outputs.
Enables modular force field composition where total energy is the sum of individual contributions (e.g., bonded + Lennard-Jones + Coulomb).
Class Type Parameters:
| Name | Bound or Constraints | Description | Default |
|---|---|---|---|
State
|
Simulation state type |
required | |
Gradients
|
Gradient structure type |
required | |
Hessians
|
Hessian structure type |
required | |
StatePatch
|
Patch[Any]
|
State patch type |
required |
Attributes:
| Name | Type | Description |
|---|---|---|
potentials |
tuple[Potential[State, Gradients, Hessians, StatePatch], ...]
|
Tuple of potentials to sum (must have at least one) |
Example
Source code in src/kups/core/potential.py
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 | |
__call__(state, patch=None, *, include_compensate=False)
¶
Evaluate all potentials and sum their outputs.
Calls each potential in sequence with the same state and patch, then sums the resulting energies, gradients, and Hessians element-wise. Patches are composed in order. The summands are always compensated so that the terms are added with Kahan summation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
State
|
Current simulation state |
required |
patch
|
StatePatch | None
|
Optional state patch for incremental updates |
None
|
include_compensate
|
bool
|
Return the accumulator rather than its compensated total |
False
|
Returns:
| Type | Description |
|---|---|
PotentialResult[State, Gradients, Hessians] | CompensatedPotentialResult[State, Gradients, Hessians]
|
Combined potential output with composed patches |
Source code in src/kups/core/potential.py
empty_patch_idx_view(state)
¶
Default patch index view covering all systems with no gradient/Hessian outputs.
Source code in src/kups/core/potential.py
sum_potentials(*potentials)
¶
Compose multiple potentials by summing their outputs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
potentials
|
Potential[State, Gradients, Hessians, StatePatch]
|
Potentials to sum. |
()
|
Returns:
| Type | Description |
|---|---|
Potential[State, Gradients, Hessians, StatePatch]
|
A single potential producing the summed output. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If no potentials are provided. |