Source code for pams.utils.json_random

import json
import math
import random
from typing import Dict
from typing import List
from typing import Union

JsonValue = Union[Dict, List, float, int]


[docs]class JsonRandom: """random generator from json. The following direction can be used for config as randomized values: - :code:`[a, b]`: uniform distribution started from a and ended to b. Not that the value should be int, this automatically converted into int. This always satisfy :math:`a \leq x < b` - :code:`{"const": [a]}`: constant value. Always set to a. - :code:`{"uniform": [a, b]}`: same as [a, b] - :code:`{"normal": [u, s]}`: normal distribution whose mean and deviation is u and s. - :code:`{"expon": [lam]}`: exponential distribution whose mean and deviation is lam. Examples: >>> from pams.utils.json_random import JsonRandom >>> import random >>> jr = JsonRandom(prng=random.Random(42)) >>> [jr.random([10, 20]) for x in range(10)] [16.39426798457884, 10.25010755222667, 12.750293183691193, 12.232107381488227, 17.364712141640126, 16.766994874229113, 18.921795677048454, 10.869388326294162, 14.219218196852704, 10.297972194380703] >>> [jr.random({"const": [10]}) for x in range(10)] [10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0, 10.0] >>> [jr.random({"uniform": [10, 20]}) for x in range(10)] [12.186379748036034, 15.053552881033625, 10.265359696838637, 11.988376506866485, 16.49884437779523, 15.449414806032166, 12.204406220406966, 15.892656838759088, 18.094304566778266, 10.064987596780611] >>> [jr.random({"normal": [0, 1]}) for x in range(10)] [0.5317762204008692, -1.453545298008678, -0.3122773171445598, 0.49036253259352475, 0.8734043853794468, -0.2406296726551354, 0.3765998586879102, 0.24821344932841446, 0.7823268087036421, -1.1132222142481727] >>> [jr.random({"expon": [3]}) for x in range(10)] [0.642818017709456, 0.9452346835236866, 1.869586994011895, 0.08175668259806873, 2.9143451561160503, 1.7824008841046926, 0.5611413226153803, 1.4412784552296345, 0.4465202669419299, 1.6479086846872075] """ # NOQA def __init__(self, prng: random.Random) -> None: """initialization. Args: prng (random.Random): pseudo random number generator for this event. Returns: None """ self.prng: random.Random = prng def _next_uniform(self, min_value: float, max_value: float) -> float: """get next uniform. Its probability density function is :math:`p(x) = \frac{1}{max - min}` anywhere within the interval :math:`[min, max)`, and 0 elsewhere. Args: min_value (float): min value. max_value (float): max value. Returns: float: uniform. """ return self.prng.random() * (max_value - min_value) + min_value def _next_normal(self, mu: float, sigma: float) -> float: """get next normal. Its probability density function is :math:`p(x) = \frac{1}{\\sqrt{2 \\pi \\sigma^2}e^{- \frac{(x - \\mu)^2}{2 \\sigma^2}}}` where :math:`\\mu` is the mean and :math:`\\sigma` is the standard deviation. Args: mu (float): mu. sigma (float): sigma. Returns: float: normal. """ return self.prng.gauss(mu=mu, sigma=sigma) def _next_exponential(self, lam: float) -> float: """get next exponential. Its probability density function is :math:`p(x) = \\lambda exp(- \\lambda x)` for :math:`x \\gt 0` and 0 elsewhere. Where :math:`\\lambda` is the scale parameter. Args: lam (float): lambda. Returns: float: exponential. """ return lam * -math.log(self.prng.random())
[docs] def random(self, json_value: JsonValue) -> float: """get a random value. Args: json_value (JsonValue): random type. This can include the parameter "const", "uniform", "normal", and "expon". Returns: float: random value. """ if isinstance(json_value, list): if len(json_value) != 2: raise ValueError( "Uniform distribution must be [min, max] but " + json.dumps(json_value) ) min_value: float = float(json_value[0]) max_value: float = float(json_value[1]) return self._next_uniform(min_value=min_value, max_value=max_value) if isinstance(json_value, dict): if len(json_value) != 1: raise ValueError( "Multiple specification of distribution type: " + json.dumps(json_value) ) if "const" in json_value: args = json_value["const"] if not isinstance(args, list): raise ValueError( "Constant must be [value] (list) but " + json.dumps(json_value) ) if len(args) != 1: raise ValueError( "Constant must be [value] but " + json.dumps(json_value) ) value = float(args[0]) return value if "uniform" in json_value: args = json_value["uniform"] if not isinstance(args, list): raise ValueError( "Uniform distribution must be [min, max] (list) but " + json.dumps(json_value) ) if len(args) != 2: raise ValueError( "Uniform distribution must be [min, max] but " + json.dumps(json_value) ) min_value = float(args[0]) max_value = float(args[1]) return self._next_uniform(min_value=min_value, max_value=max_value) if "normal" in json_value: args = json_value["normal"] if not isinstance(args, list): raise ValueError( "Normal distribution must be [mu, sigma] (list) but " + json.dumps(json_value) ) if len(args) != 2: raise ValueError( "Normal distribution must be [mu, sigma] but " + json.dumps(json_value) ) mu = float(args[0]) sigma = float(args[1]) return self._next_normal(mu=mu, sigma=sigma) if "expon" in json_value: args = json_value["expon"] if not isinstance(args, list): raise ValueError( "Exponential distribution must be [lambda] (list) but " + json.dumps(json_value) ) if len(args) != 1: raise ValueError( "Exponential distribution must be [lambda] but " + json.dumps(json_value) ) lam = float(args[0]) return self._next_exponential(lam=lam) raise ValueError("Unknown distribution type: " + json.dumps(json_value)) return float(json_value)