# Path: brainforge/util/typing.py
# def scalX(scalar, dtype=floatX):
#     return np.asscalar(np.array([scalar], dtype=dtype))
# 
# def zX_like(array, dtype=floatX):
#     return zX(*array.shape, dtype=dtype)
# 
# Path: brainforge/config/numeric.py



import numpy as np
from ..util.typing import scalX, zX_like
from ..config import floatX

    def forward(self, Z: np.ndarray) -> np.ndarray:
        raise NotImplementedError
    
    def __str__(self):
        return self.type

    def backward(self, Z: np.ndarray) -> np.ndarray:
        raise NotImplementedError


class Sigmoid(ActivationFunction):

    type = "sigmoid"

    def forward(self, Z: np.ndarray):
        return s1 / (s1 + np.exp(-Z))
    
    def backward(self, A: np.ndarray) -> np.ndarray:
        return A * (s1 - A)


class HardSigmoid(ActivationFunction):

    type = "hardsigmoid"

    def forward(self, Z: np.ndarray):
        return np.clip((Z+s1)*s05, s0, s1)

    def backward(self, A: np.ndarray):

# Path: brainforge/util/typing.py
# def scalX(scalar, dtype=floatX):
#     return np.asscalar(np.array([scalar], dtype=dtype))
# 
# def zX_like(array, dtype=floatX):
#     return zX(*array.shape, dtype=dtype)
# 
# Path: brainforge/config/numeric.py



# Path: brainforge/atomic/activation_op.py
import numpy as np
from ..util.typing import scalX, zX_like
from ..config import floatX


    def forward(self, Z: np.ndarray) -> np.ndarray:
        raise NotImplementedError
    
    def __str__(self):
        return self.type

    def backward(self, Z: np.ndarray) -> np.ndarray:
        raise NotImplementedError


class Sigmoid(ActivationFunction):

    type = "sigmoid"

    def forward(self, Z: np.ndarray):
        return s1 / (s1 + np.exp(-Z))
    
    def backward(self, A: np.ndarray) -> np.ndarray:
        return A * (s1 - A)


class HardSigmoid(ActivationFunction):

    type = "hardsigmoid"

    def forward(self, Z: np.ndarray):
        return np.clip((Z+s1)*s05, s0, s1)

    def backward(self, A: np.ndarray):

        mask = zX_like(A)