Skip to content

Commit

Permalink
Merge pull request #97 from nasa/feature/guassian_wedge
Browse files Browse the repository at this point in the history
Wedge load noise
  • Loading branch information
teubert committed Sep 15, 2023
2 parents 4772616 + 2e0b94a commit b9613b4
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/progpy/loading/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
# National Aeronautics and Space Administration. All Rights Reserved.

from progpy.loading.moving_average import MovingAverage
from progpy.loading.gaussian_wrapper import GaussianNoiseLoadWrapper
from progpy.loading.gaussian_wrapper import GaussianNoiseLoadWrapper, GaussianNoiseWrapper
from progpy.loading.piecewise import Piecewise
25 changes: 19 additions & 6 deletions src/progpy/loading/gaussian_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import numpy as np


class GaussianNoiseLoadWrapper():
class GaussianNoiseWrapper():
"""
.. versionadded:: 1.5.0
Expand All @@ -22,6 +22,10 @@ class GaussianNoiseLoadWrapper():
-------------------
seed: {int, SeedSequence, BitGenerator, Generator}, optional
The seed for random number generator. This can be set to make results repeatable.
std_slope: float, optional
The increase in standard deviation per second of time. Std is the standard deviation at t<=t0.
t0: float, optional
Initial time (at which standard deviation is std). After t0, the standard deviation increases at a rate of std_slope.
Example
-------
Expand All @@ -30,23 +34,32 @@ class GaussianNoiseLoadWrapper():
>>> future_load = GaussianNoiseLoadWrapper(future_load, STANDARD_DEV)
>>> m.simulate_to_threshold(future_load)
"""
def __init__(self, fcn: Callable, std: float, seed: int = None):
def __init__(self, fcn: Callable, std: float, seed: int = None, std_slope: float = 0, t0: float = 0):
self.fcn = fcn
self.std = std
self.std_slope = std_slope
self.t0 = t0
self.gen = np.random.default_rng(seed)

def __call__(self, t: float, x=None):
"""
Return the load with noise added
Args:
t (float): Time (s)
x (StateContainer, optional): Current state. Defaults to None.
Arguments
------------
t: float
Time (s)
x: StateContainer, optional
Current state. Defaults to None.
Returns:
InputContainer: The load with noise added
"""
input = self.fcn(t, x)
std = self.std + self.std_slope*(t-self.t0) if t > self.t0 else self.std
for key, value in input.items():
input[key] = self.gen.normal(value, self.std)
input[key] = self.gen.normal(value, std)
return input

# Old name kept for backwards compatability
GaussianNoiseLoadWrapper = GaussianNoiseWrapper
47 changes: 42 additions & 5 deletions tests/test_loading.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Copyright © 2021 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved.

import numpy as np
import unittest

from progpy.loading import Piecewise, GaussianNoiseLoadWrapper
from progpy.loading import Piecewise, GaussianNoiseWrapper


class Testloading(unittest.TestCase):
Expand Down Expand Up @@ -30,21 +31,57 @@ def loading(t, x=None):
return {'a': 10}

# Default: two values should be different (because of randomness)
loading_with_noise = GaussianNoiseLoadWrapper(loading, 10)
loading_with_noise = GaussianNoiseWrapper(loading, 10)
load1 = loading_with_noise(10)

loading_with_noise = GaussianNoiseLoadWrapper(loading, 10)
loading_with_noise = GaussianNoiseWrapper(loading, 10)
load2 = loading_with_noise(10)

self.assertNotEqual(load1['a'], load2['a'])

# Setting seed, two values should be the same now
loading_with_noise = GaussianNoiseLoadWrapper(loading, 10, seed=550)
loading_with_noise = GaussianNoiseWrapper(loading, 10, seed=550)
load1 = loading_with_noise(10)

loading_with_noise = GaussianNoiseLoadWrapper(loading, 10, seed=550)
loading_with_noise = GaussianNoiseWrapper(loading, 10, seed=550)
load2 = loading_with_noise(10)
self.assertEqual(load1['a'], load2['a'])

def test_wedge_gaussian_load(self):
def loading(t, x=None):
return {'a': 10}

loading_with_noise = GaussianNoiseWrapper(loading, 0, std_slope=10)

# Standard deviation at t=0 should be 0.
self.assertEqual(
loading_with_noise(0),
loading_with_noise(0))

# At any greater time, the standard deviation is not 0
self.assertNotEqual(
loading_with_noise(10),
loading_with_noise(10))

# Check that standard deviation increases with time.
values = [loading_with_noise(10)['a'] for _ in range(20)]
std = np.std(values)

values = [loading_with_noise(1e4)['a'] for _ in range(20)]
std2 = np.std(values)
self.assertGreater(std2, std)

# Check t0 functionality
loading_with_noise.t0 = 10
self.assertEqual( # Case t<t0
loading_with_noise(5),
loading_with_noise(5))
self.assertEqual( # Case t==t0
loading_with_noise(10),
loading_with_noise(10))
self.assertNotEqual( # Case t > t0
loading_with_noise(11),
loading_with_noise(11))

# This allows the module to be executed directly
def main():
Expand Down

0 comments on commit b9613b4

Please sign in to comment.