<commit_before>import numpy as np

from sifra.modelling.structural import Base, Element
from sifra.modelling.structures import XYPairs
from sifra.modelling.structural import Element as _Element
import scipy.stats as stats

from sifra.modelling.iodict import IODict


class ResponseModel(Base):
    """
    Statistical model that assesses the response (amount of likely
    damage) for a given hazard
    """
    def __call__(self, *args, **kwargs):
        raise NotImplementedError('__call__ is not implemented'
                                  ' on {}'.format(self.__class__.__name__))

class DamageState(ResponseModel):
    """
    The allocated damage state for a given component
    """
    damage_state = Element('str', 'Damage state name')
    # damage_state_description = Element('str', 'A description of what the damage state means')
    mode = Element('int','The mode used to estimate the damage')
    functionality = Element('float', 'Level of functionality for this damage level',
                            0.0, [lambda x: float(x) >= 0.0])
    # fragility_source = Element('str', 'The source of the parameter values')
    damage_ratio = Element('float', 'damage ratio',
                           0.0, [lambda x: float(x) >= 0.0])


class StepFunc(DamageState):
    """
    A response model that does not have a cumulative distribution
    function, rather a series of steps for damage.
    """
    xys = _Element('XYPairs', 'A list of X, Y pairs.', list,
        [lambda xy: [(float(x), float(y)) for x, y in xy]])

    def __call__(self, value):
        """
        Note that intervals are closed on the right.
        """

        for x, y in self.xys:
            if value < x:
                return y

        raise ValueError('value is greater than all xs!')


class LogNormalCDF(DamageState):
    """
    The log normal CDF response model for components.
    """
    median = _Element('float', 'Median of the log normal CDF.',
            _Element.NO_DEFAULT, [lambda x: float(x) > 0.])

    beta = _Element('float', 'Log standard deviation of the log normal CDF',
            _Element.NO_DEFAULT, [lambda x: float(x) > 0.])

    def __call__(self, hazard_intensity):
        """
        In scipy lognormal CDF is implemented thus:
            scipy.stats.lognorm.cdf(x, s, loc=0, scale=1)
        where,
            s = sigma   # or beta or standard deviation
            scale = exp(mean) = median
            loc is used to shift the distribution and commonly not used
        """
        return stats.lognorm.cdf(hazard_intensity, self.beta, loc=0, scale=self.median)


class NormalCDF(DamageState):
    """
    The normal CDF response model for components
    """
    mean = _Element('float', 'Mean of the normal or Gaussian CDF',
                    _Element.NO_DEFAULT, [lambda x: float(x) >= 0.])

    stddev = _Element('float', 'Standard deviation of the normal CDF',
                      _Element.NO_DEFAULT, [lambda x: float(x) > 0.])

    def __call__(self, value):
        """
        In scipy normal CDF is implemented thus:
            scipy.stats.norm.cdf(x, loc=0, scale=1)
        where,
        loc = Mean
        scale = Standard Deviation i.e. square root of Variance
        """
        return stats.norm.cdf(value, loc=self.mean, scale=self.stddev)


class Level0Response(DamageState):
    """
    Standard response for no damage.
    """
    mode = 1
    damage_ratio = 0.0
    functionality = 1.0
    beta = 0.0
    median = 1.0

    def __call__(self, hazard_level):
        return 0.0


class Level0Recovery(DamageState):
    """
    Standard recovery for no damage.
    """
    recovery_mean = 0.00001
    recovery_std = 0.00001

    def __call__(self, hazard_level):
        return 0.0


class DamageAlgorithm(Base):
    """
    The collection of damage states that will calculate
    the component's response to a hazard.<commit_msg>Added piecewise defined function and a constant function.<commit_after>import numpy as np

from sifra.modelling.structural import Base, Element
from sifra.modelling.structures import XYPairs
from sifra.modelling.structural import Element as _Element
import scipy.stats as stats

from sifra.modelling.iodict import IODict


class ResponseModel(Base):
    """
    Statistical model that assesses the response (amount of likely
    damage) for a given hazard
    """
    def __call__(self, *args, **kwargs):
        raise NotImplementedError('__call__ is not implemented'
                                  ' on {}'.format(self.__class__.__name__))

class DamageState(ResponseModel):
    """
    The allocated damage state for a given component
    """
    damage_state = Element('str', 'Damage state name')
    # damage_state_description = Element('str', 'A description of what the damage state means')
    mode = Element('int','The mode used to estimate the damage')
    functionality = Element('float', 'Level of functionality for this damage level',
                            0.0, [lambda x: float(x) >= 0.0])
    # fragility_source = Element('str', 'The source of the parameter values')
    damage_ratio = Element('float', 'damage ratio',
                           0.0, [lambda x: float(x) >= 0.0])


class StepFunc(DamageState):
    """
    A response model that does not have a cumulative distribution
    function, rather a series of steps for damage.
    """
    xys = _Element('XYPairs', 'A list of X, Y pairs.', list,
        [lambda xy: [(float(x), float(y)) for x, y in xy]])

    def __call__(self, value):
        """
        Note that intervals are closed on the right.
        """

        for x, y in self.xys:
            if value < x:
                return y

        raise ValueError('value is greater than all xs!')


class LogNormalCDF(DamageState):
    """
    The log normal CDF response model for components.
    """
    median = _Element('float', 'Median of the log normal CDF.',
            _Element.NO_DEFAULT, [lambda x: float(x) > 0.])

    beta = _Element('float', 'Log standard deviation of the log normal CDF',
            _Element.NO_DEFAULT, [lambda x: float(x) > 0.])

    def __call__(self, hazard_intensity):
        """
        In scipy lognormal CDF is implemented thus:
            scipy.stats.lognorm.cdf(x, s, loc=0, scale=1)
        where,
            s = sigma   # or beta or standard deviation
            scale = exp(mean) = median
            loc is used to shift the distribution and commonly not used
        """
        return stats.lognorm.cdf(hazard_intensity, self.beta, loc=0, scale=self.median)


class NormalCDF(DamageState):
    """
    The normal CDF response model for components
    """
    mean = _Element('float', 'Mean of the normal or Gaussian CDF',
                    _Element.NO_DEFAULT, [lambda x: float(x) >= 0.])

    stddev = _Element('float', 'Standard deviation of the normal CDF',
                      _Element.NO_DEFAULT, [lambda x: float(x) > 0.])

    def __call__(self, value):
        """
        In scipy normal CDF is implemented thus:
            scipy.stats.norm.cdf(x, loc=0, scale=1)
        where,
        loc = Mean
        scale = Standard Deviation i.e. square root of Variance
        """
        return stats.norm.cdf(value, loc=self.mean, scale=self.stddev)


class ConstantFunction(DamageState):
    """
    A function for defining a constant amplitude for a given range
    """
    amplitude = _Element('float', 'Constant amplitude of function',
                    _Element.NO_DEFAULT, [lambda x: float(x) >= 0.])

    def __call__(self, value):
        return np.ones_like(value) * self.amplitude


class PiecewiseDefinedFunction(DamageState):
    """
    x          : array of x values over which the function is calculated

    boundaries : a list of floats the define the edges of the ranges
                 within which each function is applicable

    funcnames  : a list of names of functions or distributions

    arglist    : a list of lists, each containing the parameters
                 that required by the corresponding functions

    These lists must meet the following criteria:
        len(funcnames) == len(arglist) == len(boundaries)-1

    """
    boundaries = []
    funcnames = []
    arglist = []

    def __call__(self, xvalues):
        indices = [np.amax(np.where(xvalues <= val)) for val in self.boundaries]
        funclist = []
        for i in range(len(indices) - 1):
            if i + 1 < len(indices) - 1:
                xtmp = xvalues[indices[i]:indices[i + 1]]
            else:
                xtmp = xvalues[indices[i]:]
            funclist.append(self.funcnames[i](xtmp, *self.arglist[i]))
        allfuncs = np.concatenate(funclist)
        return allfuncs


class Level0Response(DamageState):
    """
    Standard response for no damage.
    """
    mode = 1
    damage_ratio = 0.0
    functionality = 1.0
    beta = 0.0
    median = 1.0

    def __call__(self, hazard_level):
        return 0.0


class Level0Recovery(DamageState):
    """
    Standard recovery for no damage.
    """
    recovery_mean = 0.00001
    recovery_std = 0.00001

    def __call__(self, hazard_level):
        return 0.0


class DamageAlgorithm(Base):
    """
    The collection of damage states that will calculate
    the component's response to a hazard.
{'fraction_non_alphanumeric': 0.10133822577163824, 'fraction_numerical': 0.010144614720483489, 'mean_word_length': 2.4157758938444527, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 16, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1863118', 'n_tokens_mistral': 2754, 'n_tokens_neox': 2629, 'n_words': 899}