Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/rolch/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
)
from .information_criteria import InformationCriterion
from .link import (
GenericInverseLink,
IdentityLink,
InverseSoftPlusLink,
InverseSoftPlusShiftTwoLink,
Expand All @@ -45,15 +46,17 @@
LogLink,
LogShiftTwoLink,
LogShiftValueLink,
ScaledInverseSigmoidLink,
ScaledSigmoidLink,
SqrtLink,
SqrtShiftTwoLink,
SqrtShiftValueLink,
)
from .methods import (
ElasticNetPathMethod,
LassoPathMethod,
OrdinaryLeastSquaresMethod,
RidgeMethod,
ElasticNetPathMethod,
)
from .scaler import OnlineScaler
from .utils import (
Expand Down Expand Up @@ -86,6 +89,9 @@
"InverseSoftPlusLink",
"InverseSoftPlusShiftValueLink",
"InverseSoftPlusShiftTwoLink",
"ScaledInverseSigmoidLink",
"ScaledSigmoidLink",
"GenericInverseLink",
"DistributionNormal",
"DistributionNormalMeanVariance",
"DistributionT",
Expand Down
5 changes: 5 additions & 0 deletions src/rolch/link/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from .generic import GenericInverseLink
from .identitylinks import IdentityLink
from .loglinks import LogIdentLink, LogLink, LogShiftTwoLink, LogShiftValueLink
from .sigmoidlinks import ScaledInverseSigmoidLink, ScaledSigmoidLink
from .softpluslinks import (
InverseSoftPlusLink,
InverseSoftPlusShiftTwoLink,
Expand All @@ -8,6 +10,7 @@
from .sqrtlinks import SqrtLink, SqrtShiftTwoLink, SqrtShiftValueLink

__all__ = [
"GenericInverseLink",
"LogLink",
"IdentityLink",
"LogShiftValueLink",
Expand All @@ -19,4 +22,6 @@
"InverseSoftPlusLink",
"InverseSoftPlusShiftValueLink",
"InverseSoftPlusShiftTwoLink",
"ScaledInverseSigmoidLink",
"ScaledSigmoidLink",
]
47 changes: 47 additions & 0 deletions src/rolch/link/generic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
from typing import Tuple

import numpy as np

from rolch.base import LinkFunction


class GenericInverseLink(LinkFunction):
"""
This link function maps an arbitrary link function to its inverse by swapping the links and derivatives.
You need to provide the link function and the support.
This can be used to quickly create inverse links or as base class if you want to implement a custom link function.
The default will not provide the second derivative, which you need to implement in the subclass.
"""

def __init__(
self,
link_function: LinkFunction,
link_support: Tuple,
) -> None:
"""Initializes the GenericInverseLink class.

Args:
link_function (LinkFunction): The Link function to invert
link_support (Tuple): The support of the link function
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is misleading, we want to pass the support of the inverted link function here, right?

Suggested change
link_support (Tuple): The support of the link function
link_support (Tuple): The support of the inverted link function

"""
self.link_function = link_function
self._link_support = link_support

@property
def link_support(self) -> Tuple[float, float]:
return (self._link_support[0], self._link_support[1])

def link(self, x: np.ndarray) -> np.ndarray:
return self.link_function.inverse(x)

def inverse(self, x: np.ndarray) -> np.ndarray:
return self.link_function.link(x)

def link_derivative(self, x: np.ndarray) -> np.ndarray:
return self.link_function.inverse_derivative(x)

def inverse_derivative(self, x: np.ndarray) -> np.ndarray:
return self.link_function.link_derivative(x)

def link_second_derivative(self, x: np.ndarray) -> np.ndarray:
return super().link_second_derivative(x)
84 changes: 84 additions & 0 deletions src/rolch/link/sigmoidlinks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from typing import Tuple

import numpy as np

from ..base import LinkFunction
from .robust_math import robust_log, zero_safe_division


def sigmoid(y):
return 1 / (1 + np.fmax(1e-6, np.exp(-np.fmin(y, 25))))


class ScaledInverseSigmoidLink(LinkFunction):

def __init__(
self,
lower: float = 0,
upper: float = 1,
):
self.lower = lower
self.upper = upper

@property
def link_support(self) -> Tuple[float, float]:
return (self.lower + np.nextafter(0, 1), self.upper - np.nextafter(0, 1))

def link(self, x: np.ndarray) -> np.ndarray:
return robust_log(x - self.lower) - robust_log(self.upper - x)

def inverse(self, x: np.ndarray) -> np.ndarray:
return self.lower + (self.upper - self.lower) * sigmoid(x)

def inverse_derivative(self, x):
return (
(self.upper - self.lower)
* sigmoid(x)
* (1 - sigmoid(x))
/ (self.upper - self.lower)
)

def link_derivative(self, x: np.ndarray) -> np.ndarray:
return zero_safe_division(1, x - self.lower) - zero_safe_division(
1, x - self.upper
)

def link_second_derivative(self, x) -> np.ndarray:
return super().link_second_derivative(x)


class ScaledSigmoidLink(LinkFunction):

def __init__(
self,
lower: float = 0,
upper: float = 1,
):
self.lower = lower
self.upper = upper

@property
def link_support(self) -> Tuple[float, float]:
return (self.lower + np.nextafter(0, 1), self.upper - np.nextafter(0, 1))

def link(self, x: np.ndarray) -> np.ndarray:
return self.lower + (self.upper - self.lower) * sigmoid(x)

def inverse(self, x: np.ndarray) -> np.ndarray:
return np.log(np.fmax(1e-10, x - self.lower)) - np.log(
np.fmax(1e-10, self.upper - x)
)

def inverse_derivative(self, x):
return 1 / (x - self.lower) - 1 / (x - self.upper)

def link_derivative(self, x: np.ndarray) -> np.ndarray:
return (
(self.upper - self.lower)
* sigmoid(x)
* (1 - sigmoid(x))
/ (self.upper - self.lower)
)

def link_second_derivative(self, x) -> np.ndarray:
return super().link_second_derivative(x)