Source code for hypersynchronization.simulation.generate

"""
Functions to generate initial states for coupled phase oscillators.
"""

import numpy as np

__all__ = [
    "generate_q_twisted_state",
    "generate_k_clusters",
    "generate_state",
]


[docs]def generate_q_twisted_state(N, q, noise=1e-2, seed=None): """ Generate a q-twisted state for N phase oscillators. Parameters ---------- N : int The number of oscillators. q : int The number of twists. noise : float, optional The magnitude of gaussian noise added to the phases. Default is 1e-2. seed : int, numpy.random.Generator, or None, optional Seed for the random number generator. Default is None. Returns ------- theta : numpy.ndarray of shape (N,) Array of generated phase angles representing the initial state. See Also -------- generate_state : Unified interface for all initial-condition types. identify_q_twisted : Identify a q-twisted state from phases. Examples -------- >>> import numpy as np >>> import hypersynchronization as hs >>> theta = hs.generate_q_twisted_state(10, q=1, noise=0) >>> theta.shape (10,) >>> np.allclose(np.diff(theta), 2 * np.pi / 10) True """ rng = np.random.default_rng(seed) rand_phase = rng.random() * 2 * np.pi perturbation = noise * rng.standard_normal(N) theta = 2 * np.pi * q * np.arange(1, N + 1) / N + rand_phase + perturbation return theta
[docs]def generate_k_clusters(N, k, ps, noise=1e-2, seed=None): """ Generate a k-cluster state for N phase oscillators. Parameters ---------- N : int The number of oscillators. k : int The number of clusters to generate. ps : array-like The proportion of oscillators in each cluster. The number of elements in `ps` must be equal to `k`, and the sum of probabilities must be 1. noise : float, optional The magnitude of gaussian noise added to the phases. Default is 1e-2. seed : int, numpy.random.Generator, or None, optional Seed for the random number generator. Default is None. Returns ------- theta : numpy.ndarray of shape (N,) Array of generated phase angles representing the initial state. Raises ------ ValueError If `len(ps) != k` or if `ps` does not sum to 1. See Also -------- generate_state : Unified interface for all initial-condition types. identify_k_clusters : Identify a k-cluster state from phases. Examples -------- >>> import hypersynchronization as hs >>> theta = hs.generate_k_clusters(10, k=2, ps=[0.5, 0.5], noise=0, seed=0) >>> len(set(theta)) 2 """ rng = np.random.default_rng(seed) if len(ps) != k: raise ValueError( "The number of elements in ps must be equal to the number of clusters k." ) if not np.isclose(sum(ps), 1): raise ValueError("The ps must sum to one.") rand_phase = rng.random() * 2 * np.pi perturbation = noise * rng.standard_normal(N) choices = rand_phase + np.linspace(0, 2 * np.pi, num=k, endpoint=False) theta = rng.choice(choices, size=N, p=ps) + perturbation return theta
[docs]def generate_state(N, kind="random", noise=1e-2, seed=None, **kwargs): """ Generate initial conditions for a system of N oscillators. Parameters ---------- N : int Number of oscillators in the system. kind : str, optional Kind of state to generate. Default is "random". * "sync": all oscillators at the same random phase, * "random": uniform random phases on [0, 2pi[, * "splay": evenly spaced on [0, 2pi[, * "k-cluster": random k-cluster state, * "q-twisted": q-twisted state. noise : float, optional Level of noise to add to the initial conditions. Default is 1e-2. seed : int, numpy.random.Generator, or None, optional Seed for the random number generator. Default is None. **kwargs Keyword arguments passed to `generate_k_clusters` or `generate_q_twisted_state`. Returns ------- theta : numpy.ndarray of shape (N,) Initial phases for each oscillator. Raises ------ ValueError If `kind` is not one of the supported options. See Also -------- generate_k_clusters : Generate a k-cluster state directly. generate_q_twisted_state : Generate a q-twisted state directly. Examples -------- >>> import numpy as np >>> import hypersynchronization as hs >>> theta = hs.generate_state(10, kind="sync", noise=0, seed=0) >>> float(np.std(theta)) 0.0 >>> theta = hs.generate_state(10, kind="q-twisted", q=2, noise=0) >>> hs.identify_state(theta) '2-twisted' """ rng = np.random.default_rng(seed) if kind == "sync": rand_phase = rng.random() * 2 * np.pi perturbation = noise * rng.standard_normal(N) theta = rand_phase * np.ones(N) + perturbation elif kind == "random": theta = rng.random(N) * 2 * np.pi elif kind == "splay": perturbation = noise * rng.standard_normal(N) theta = np.linspace(0, 2 * np.pi, num=N, endpoint=False) + perturbation elif kind == "k-cluster": theta = generate_k_clusters(N, **kwargs, noise=noise, seed=rng) elif kind == "q-twisted": theta = generate_q_twisted_state(N, **kwargs, noise=noise, seed=rng) else: raise ValueError( "Unknown kind. Must be one of 'sync', 'random', 'splay', 'k-cluster', 'q-twisted'." ) return theta