Source code for pyFDN.generate.allpass_FDN.rand_admissible_homogeneous_allpass
"""
Random admissible diagonal matrix for homogeneous uniallpass FDN.
See "Allpass Feedback Delay Networks" by Sebastian J. Schlecht.
"""
from __future__ import annotations
import numpy as np
from numpy.typing import ArrayLike
[docs]
def rand_admissible_homogeneous_allpass(
G: ArrayLike,
range_: tuple[float, float] | ArrayLike,
) -> np.ndarray:
"""
Generate a random admissible diagonal matrix P for homogeneous uniallpass FDN.
Used with homogeneous_allpass_fdn(G, P) to obtain a uniallpass FDN with
homogeneous decay. Admissibility is defined by the construction in the paper.
Parameters
----------
G : (N, N) array-like
Diagonal attenuation matrix with 0 < diag(G) < 1.
range_ : tuple (low, high) or array-like of length 2
Random range with 0 < low < high < 1. Diagonal entries of P are built
from random ratios in [low, high] scaled by diag(G)^2.
Returns
-------
P : (N, N) ndarray
Admissible diagonal matrix (first diagonal entry 1, rest from cumprod).
Examples
--------
>>> G = np.diag([0.9, 0.8, 0.7])
>>> P = rand_admissible_homogeneous_allpass(G, (0.3, 0.8))
>>> A, b, c, d, U = homogeneous_allpass_fdn(G, P)
"""
G = np.asarray(G, dtype=float)
N = G.shape[0]
if G.shape != (N, N):
raise ValueError("G must be square")
range_arr = np.asarray(range_, dtype=float).ravel()
if range_arr.shape[0] != 2:
raise ValueError("range_ must be a pair (low, high)")
low, high = float(range_arr[0]), float(range_arr[1])
if not (0 < low < high < 1):
raise ValueError("range_ must satisfy 0 < low < high < 1")
ratios = np.diag(G) ** 2
rand_ratios = (np.random.rand(N) * (high - low) + low) * ratios
cum = np.cumprod(1.0 / rand_ratios[1:])
diag_P = np.concatenate([[1.0], cum])
return np.diag(diag_P)