import numpy as np
from swarmsim.Interactions import Interaction
from swarmsim.Populations import Population
from swarmsim.Utils import compute_distances
from typing import Optional
[docs]
class PowerLawRepulsion(Interaction):
"""
Pure power-law repulsion interaction with finite range cutoff.
This interaction implements a repulsive force that decays according to an inverse
power law with respect to inter-agent distance. Unlike the more general PowerLawInteraction,
this class focuses specifically on repulsion with a clean cutoff mechanism that ensures
zero force at the maximum interaction distance.
The repulsion force follows:
.. math::
F(r) = k \\left( \\frac{1}{r^p} - \\frac{1}{r_{max}^p} \\right)
for :math:`r < r_{max}`, and :math:`F(r) = 0` for :math:`r \\geq r_{max}`, where:
- :math:`k` is the repulsion strength parameter
- :math:`r` is the inter-agent distance
- :math:`p` is the power law exponent
- :math:`r_{max}` is the maximum interaction distance
The subtraction term ensures the force smoothly approaches zero at the cutoff distance,
preventing discontinuous force jumps that can cause numerical instabilities.
Parameters
----------
target_population : Population
The population that receives repulsion forces.
source_population : Population
The population that generates repulsion forces.
config_path : str
Path to the YAML configuration file containing interaction parameters.
name : str, optional
Name identifier for the interaction. Defaults to class name if None.
Attributes
----------
target_population : Population
Population affected by power-law repulsion forces.
source_population : Population
Population generating power-law repulsion forces.
strength : np.ndarray or None
Repulsion strength parameter for each agent in the target population.
max_distance : np.ndarray or None
Maximum interaction distance for each agent in the target population.
p : int
Power law exponent controlling the decay rate of repulsion.
Higher values create steeper decay (stronger short-range repulsion).
params_shapes : dict
Defines expected shapes for interaction parameters.
Config Requirements
-------------------
The YAML configuration file must contain the following parameters under the interaction's section:
parameters : dict
Parameter configuration for the power-law repulsion:
p : int
Power law exponent for the repulsion force. Common values:
Notes
-----
**Force Characteristics:**
- **Monotonic Decay**: Force decreases monotonically with distance
- **Finite Range**: Zero force beyond ``max_distance``
- **Customizable Steepness**: Power exponent controls interaction range vs strength
Raises
------
FileNotFoundError
If the configuration file is not found.
KeyError
If required interaction parameters are missing in the configuration file.
ValueError
If power exponent p is not positive or if parameter shapes are incompatible.
Examples
--------
**Soft Repulsion Configuration:**
.. code-block:: yaml
PowerLawRepulsion:
id: "soft_repulsion"
p: 2
parameters:
mode: "Fixed"
names: ["strength", "max_distance"]
values:
strength: 1.0
max_distance: 3.0
"""
[docs]
def __init__(self,
target_population: Population,
source_population: Population,
config_path: str,
name: str = None) -> None:
super().__init__(target_population, source_population, config_path, name)
self.strength: Optional[np.ndarray] = None
self.max_distance: Optional[np.ndarray] = None
self.params_shapes = {
"strength": (),
"max_distance": ()
}
self.p: int = self.config.get("p")
[docs]
def reset(self):
super().reset()
self.strength = self.params['strength']
self.max_distance = self.params['max_distance']
[docs]
def get_interaction(self):
"""
Compute power-law repulsion forces between source and target populations.
This method calculates repulsive forces using the power-law formula with a smooth
cutoff mechanism. The computation handles distance calculations, applies the power-law
kernel, and includes numerical stability measures to prevent instabilities.
Returns
-------
np.ndarray
Repulsion forces array of shape ``(N_target, 2)`` where:
- ``N_target`` is the number of agents in the target population
- Each row represents the total repulsion force on one target agent in 2D space
Force vectors point away from source agents (repulsive direction).
Notes
-----
**Algorithm Steps:**
1. **Distance Computation**: Calculate pairwise distances between all source-target pairs
2. **Cutoff Application**: Apply smooth cutoff using reference force at max_distance
3. **Kernel Evaluation**: Compute power-law repulsion kernel with bounds
4. **Force Assembly**: Convert scalar forces to vector forces using relative positions
5. **Force Summation**: Sum contributions from all source agents
**Mathematical Implementation:**
The force kernel is computed as:
.. math::
K(r) = k \\cdot \\text{clip}\\left( \\frac{1}{r^p} - \\frac{1}{r_{max}^p}, 0, F_{max} \\right)
where:
- :math:`k` is the strength parameter
- :math:`r` is the inter-agent distance (with minimum threshold)
- :math:`p` is the power law exponent
- :math:`r_{max}` is the maximum interaction distance
- :math:`F_{max} = 1000` is the force magnitude cap
"""
# Compute pairwise distances and relative positions between agents
distances, relative_positions = compute_distances(self.target_population.x[:, :2], self.source_population.x[:, :2])
# Prevent division by zero
distances = np.maximum(distances, 1e-6)
# Compute the force kernel using power-law repulsion
y_f = 1 / (self.max_distance ** self.p)
kernel = (1 / (distances ** self.p) - y_f[:, np.newaxis])
kernel = self.strength[:, np.newaxis] * np.minimum(np.maximum(kernel, 0), 1000) # Cap forces to avoid instability
# Compute final repulsion forces
repulsion = np.sum(kernel[:, :, np.newaxis] * relative_positions, axis=1)
return repulsion