Source code for frlearn.uncategorised.weights

"""
Generating functions for weight vectors parametrised by length.
These weight vectors should sum to one, and they typically descend,
as they are meant to be used with the
`soft_head`, `soft_max`, `soft_min` and `soft_tail` functions in `utilities.numpy`,
where the first weight corresponds respectively to the first, largest, smallest and last values.
However, since `k` may in general be less than the length of the relevant axis,
weight vectors that are not descending can also be useful.

Specifically, using these weights with `soft_max` gives an Ordered Weighted Averaging (OWA) operator,
while using the same weights with `soft_min` is equivalent to an OWA operator with dual weights.
"""

from __future__ import annotations

from abc import abstractmethod
from dataclasses import dataclass
from typing import Callable

import numpy as np

__all__ = [
    'Weights', 'ConstantWeights', 'ExponentialWeights', 'LinearWeights',
    'QuantifierWeights', 'ReciprocallyLinearWeights',
]


[docs]class Weights: """ Abstract base class for parametrisable weights functions. Classes that inherit from `Weights` should overwrite `__call__` with the weight function, while `__init__` can be used to set any parameters. Returns ------- f: int -> np.array Function that takes a positive integer `k` and returns a weight vector of length `k`. References ---------- .. [1] `Yager RR (1988). On Ordered Weighted Averaging Aggregation Operators in Multicriteria Decisionmaking. IEEE Transactions on Systems, Man, and Cybernetics, vol 18, no 1, pp 183–190. doi: 10.1109/21.87068 <https://ieeexplore.ieee.org/document/87068>`_ """ @abstractmethod def __call__(self, k: int): pass
[docs]@dataclass class ConstantWeights(Weights): """ `(1/4, 1/4, 1/4, 1/4)` Also known as *mean* weights, as they compute the unweighted mean. Returns ------- f: int -> np.array Function that takes a positive integer `k` and returns a weight vector of length `k` with constant weights. """ def __call__(self, k: int): return np.full(k, 1 / k)
[docs]@dataclass class ExponentialWeights(Weights): """ `(8/15, 4/15, 2/15, 1/15)` Exponentially decreasing weights with parametrisable base. Parameters ---------- base: float Exponential base. Should be larger than 1. Returns ------- f: int -> np.array Function that takes a positive integer `k` and returns a weight vector of length `k` with exponentially decreasing weights with base `b`. Notes ----- With base 2, weights rapidly approach 0, meaning: - the resulting weight vector is not very useful, and quickly becomes insensitive to increasing `k`, - using large values for `k` will produce weights that are so small as to cause computational wonkiness. These issues are exacerbated for larger bases, so bases only slightly larger than 1 may be most useful. """ base: float = 2 def __call__(self, k: int): w = np.flip(self.base ** np.arange(k)) return w / np.sum(w)
[docs]@dataclass class LinearWeights(Weights): """ `(4/10, 3/10, 2/10, 1/10)` Also known as *additive* weights. Returns ------- f: int -> np.array Function that takes a positive integer `k` and returns a weight vector of length `k` with linearly decreasing weights. """ def __call__(self, k: int): return np.flip(2 * np.arange(1, k + 1) / (k * (k + 1)))
[docs]@dataclass class QuantifierWeights(Weights): """ Weights that encode a regular non-decreasing quantifier [1]_. Parameters ---------- q: float -> float Regular non-decreasing quantifier (a surjective, non-decreasing function `[0, 1] -> [0, 1]`). Returns ------- f: int -> np.array Function that takes a positive integer `k` and returns the weight vector of length `k` corresponding to `q`. References ---------- .. [1] `Yager RR (1988). On Ordered Weighted Averaging Aggregation Operators in Multicriteria Decisionmaking. IEEE Transactions on Systems, Man, and Cybernetics, vol 18, no 1, pp 183–190. doi: 10.1109/21.87068 <https://ieeexplore.ieee.org/document/87068>`_ """ q: Callable[[float], float] def __call__(self, k: int): return self.q(np.arange(1, k+1) / k) - self.q(np.arange(k) / k)
[docs]@dataclass class ReciprocallyLinearWeights(Weights): """ `(12/25, 12/50, 12/75, 12/100)` Also known as *inverse additive* weights. Returns ------- f: int -> np.array Function that takes a positive integer `k` and returns a weight vector of length `k` with reciprocally linearly decreasing weights. """ def __call__(self, k: int): return 1 / (np.arange(1, k + 1) * np.sum(1 / np.arange(1, k + 1)))