Skip to content

Commit

Permalink
new method for noise + rng_seed can be a rng_state
Browse files Browse the repository at this point in the history
rng_seed is updated inplace. This requires processing data to be
deeply copied before execution.
  • Loading branch information
georgievgeorgi authored and kerberizer committed Apr 22, 2024
1 parent 3761814 commit 51b6a78
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 12 deletions.
6 changes: 4 additions & 2 deletions src/ramanchada2/misc/spectrum_deco/spectrum_filter.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3

from functools import wraps
from copy import copy
from copy import copy, deepcopy
from ramanchada2.spectrum.spectrum import Spectrum
from .dynamically_added import dynamically_added_filters
import logging
Expand All @@ -13,7 +13,9 @@ def add_spectrum_filter(fun):
@wraps(fun)
def retf(old_spe: Spectrum, *args, **kwargs) -> Spectrum:
new_spe = copy(old_spe)
new_spe._applied_processings.append(proc=fun.__name__, args=args, kwargs=kwargs)
new_spe._applied_processings.append(proc=fun.__name__,
args=deepcopy(args),
kwargs=deepcopy(kwargs))
fun(old_spe, new_spe, *args, **kwargs)
new_spe.write_cache()
return new_spe
Expand Down
12 changes: 10 additions & 2 deletions src/ramanchada2/spectrum/baseline/add_baseline.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,14 @@
def generate_baseline(
n_freq: int = Field(..., gt=2),
size: int = Field(..., gt=2),
rng_seed: Union[int, None] = None):
rng = np.random.default_rng(rng_seed)
# validation for rng_seed is removed because
# it makes in-place modification impossible
rng_seed=None):
if isinstance(rng_seed, dict):
rng = np.random.default_rng()
rng.__setstate__(rng_seed)
else:
rng = np.random.default_rng(rng_seed)
k = rng.normal(0, size, size=(2, n_freq))
k[1][0] = 0
z = k[0] + k[1]*1j
Expand All @@ -26,6 +32,8 @@ def generate_baseline(
base = base[:size]
base -= base.min()
base /= base.max()
if isinstance(rng_seed, dict):
rng_seed.update(rng.__getstate__())
return base


Expand Down
19 changes: 13 additions & 6 deletions src/ramanchada2/spectrum/filters/add_gaussian_noise.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
#!/usr/bin/env python3

from typing import Union

import numpy as np
from pydantic import validate_arguments, PositiveFloat

Expand All @@ -15,7 +13,9 @@ def add_gaussian_noise(
old_spe: Spectrum,
new_spe: Spectrum, /,
sigma: PositiveFloat,
rng_seed: Union[int, None] = None):
# validation for rng_seed is removed because
# it makes in-place modification impossible
rng_seed=None):
r"""
Add gaussian noise to the spectrum.
Random number i.i.d. $N(0, \sigma)$ is added to every sample
Expand All @@ -24,11 +24,18 @@ def add_gaussian_noise(
----------
sigma : float
sigma of the gaussian noise
rng_seed : int, optional
seed for the random generator
rng_seed : int or rng state, optional
seed for the random generator.
If a state is provided it is updated in-place
"""
rng = np.random.default_rng(rng_seed)
if isinstance(rng_seed, dict):
rng = np.random.default_rng()
rng.__setstate__(rng_seed)
else:
rng = np.random.default_rng(rng_seed)
dat = old_spe.y + rng.normal(0., sigma, size=len(old_spe.y))
if any(dat < 0):
dat += abs(dat.min())
if isinstance(rng_seed, dict):
rng_seed.update(rng.__getstate__())
new_spe.y = np.array(dat)
74 changes: 74 additions & 0 deletions src/ramanchada2/spectrum/filters/add_gaussian_noise_drift.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import numpy as np
from pydantic import PositiveFloat, confloat, validate_arguments

from ramanchada2.misc.spectrum_deco import add_spectrum_filter

from ..spectrum import Spectrum


@validate_arguments(config=dict(arbitrary_types_allowed=True))
def generate_add_gaussian_noise_drift(y, /,
sigma: PositiveFloat,
coef: confloat(ge=0, le=1), # type: ignore [valid-type]
# validation for rng_seed is removed because
# it makes in-place modification impossible
rng_seed=None):
if isinstance(rng_seed, dict):
rng = np.random.default_rng()
rng.__setstate__(rng_seed)
else:
rng = np.random.default_rng(rng_seed)
gaus = rng.normal(0., sigma+coef/np.sqrt(2), size=len(y))
cs = np.cumsum(gaus)
# coef*sum(cs[:i]) + (1-coef)*gaus is identical to
# coef*sum(cs[:i-1]) + gaus
noise = coef*cs + gaus*(1-coef)
noise -= np.std(noise)
dat = y + noise
if any(dat < 0):
dat += abs(dat.min())
if isinstance(rng_seed, dict):
rng_seed.update(rng.__getstate__())
return np.array(dat)


@add_spectrum_filter
@validate_arguments(config=dict(arbitrary_types_allowed=True))
def add_gaussian_noise_drift(
old_spe: Spectrum,
new_spe: Spectrum, /,
sigma: PositiveFloat,
coef: confloat(ge=0, le=1), # type: ignore [valid-type]
# validation for rng_seed is removed because
# it makes in-place modification impossible
rng_seed=None):
r"""
Add cumulative gaussian noise to the spectrum.
Exponential-moving-average-like gaussian noise is added
to each sample. The goal is to mimic the low-frequency noise
(or random substructures in spectra).
The additive noise is
.. math::
a_i = coef*\sum_{j=0}^{i-1}g_j + g_i,
where
.. math::
g_i = \mathcal{N}(0, 1+\frac{coef}{\sqrt 2}).
This way drifting is possible while keeping the
.. math::
\sigma(\Delta(a)) \approx 1.
Parameters
----------
sigma : float
sigma of the gaussian noise
coef : float in [0, 1]
drifting coefficient. if `coef == 0` the result is
identical to `add_gaussian_noise()`.
rng_seed : int or rng state, optional
seed for the random generator.
If a state is provided it is updated in-place
"""
new_spe.y = generate_add_gaussian_noise_drift(old_spe.y,
sigma=sigma,
coef=coef,
rng_seed=rng_seed)
11 changes: 9 additions & 2 deletions src/ramanchada2/spectrum/filters/add_poisson_noise.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,17 @@ def add_poisson_noise(
----------
scale : float, optional
scale the amplitude of the noise, by default 1
rng_seed : int, optional
rng_seed : int or rng state, optional
seed for the random generator
If a state is provided it is updated in-place
"""
rng = np.random.default_rng(rng_seed)
if isinstance(rng_seed, dict):
rng = np.random.default_rng()
rng.__setstate__(rng_seed)
else:
rng = np.random.default_rng(rng_seed)
dat = old_spe.y + [rng.normal(0., np.sqrt(i*scale)) for i in old_spe.y]
dat[dat < 0] = 0
if isinstance(rng_seed, dict):
rng_seed.update(rng.__getstate__())
new_spe.y = np.array(dat)

0 comments on commit 51b6a78

Please sign in to comment.