<commit_before>"""Register, NDimensionalRegister and ResolutionSet
abstract classes to contain area, interval and unit metadata.

Implemented by :class:`smif.convert.interval.TimeIntervalRegister`,
:class:`smif.convert.area.RegionRegister` and
:class:`smif.convert.unit.UnitRegister`.
"""
import logging
from abc import ABCMeta, abstractmethod
from collections import OrderedDict, defaultdict

import numpy as np


class Register(metaclass=ABCMeta):
    """Abstract class which holds the ResolutionSets

    Arguments
    ---------
    axis : int, default=None
        The axis over which operations on the data array are performed

    """
    def __init__(self, axis=None):
        self.logger = logging.getLogger(__name__)
        self.axis = axis

    @property
    @abstractmethod
    def names(self):
        raise NotImplementedError

    @abstractmethod
    def register(self, resolution_set):
        raise NotImplementedError

    @abstractmethod
    def get_coefficients(self, source, destination):
        raise NotImplementedError

    def convert(self, data, from_set_name, to_set_name):
        """Convert a list of data points for a given set to another set

        Parameters
        ----------
        data: numpy.ndarray
        from_set_name: str
        to_set_name: str

        Returns
        -------
        numpy.ndarray

        """
        coefficients = self.get_coefficients(from_set_name, to_set_name)

        if self.axis is not None:
            data_count = data.shape[self.axis]
            if coefficients.shape[0] != data_count:
                msg = "Size of coefficient array does not match source " \
                      "resolution set from data matrix: %s != %s"
                raise ValueError(msg, coefficients.shape[self.axis],
                                 data_count)

        if self.axis == 0:
            converted = np.dot(coefficients.T, data)
        elif self.axis == 1:
            converted = np.dot(data, coefficients)
        else:
            converted = np.dot(data, coefficients)

        return converted


class NDimensionalRegister(Register):
    """Abstract class which holds N-Dimensional ResolutionSets

    Arguments
    ---------
    axis : int, default=None
        The axis over which operations on the data array are performed

    """
    def __init__(self, axis=None):
        super().__init__(axis)
        self._register = OrderedDict()
        self._conversions = defaultdict(dict)

    def register(self, resolution_set):
        """Add a ResolutionSet to the register

        Detects duplicate references to the same annual-hours by performing a
        convolution of the two one-dimensional arrays of time-intervals.

        Parameters
        ----------
        resolution_set : smif.convert.ResolutionSet

        Raises
        ------
        ValueError
            If a ResolutionSet of the same name already exists in the register
        """
        if resolution_set.name in self._register:
            msg = "A ResolutionSet named {} has already been loaded"
            raise ValueError(msg.format(resolution_set.name))

        self._register[resolution_set.name] = resolution_set

    @property
    def names(self):
        """Names of registered region sets

        Returns
        -------
        sets: list of str
        """
        return list(self._register.keys())

    def get_entry(self, name):
        """Returns the ResolutionSet of `name`

        Arguments
        ---------
        name : str
            The unique identifier of a ResolutionSet in the register

        Returns
        -------
        smif.convert.ResolutionSet

        """
        if name not in self._register:
            msg = "ResolutionSet '{}' not registered"
            raise ValueError(msg.format(name))
        return self._register[name]

    def get_coefficients(self, source, destination):
        """Get coefficients representing intersection of sets

        Arguments
        ---------
        source : string
            The name of the source set
        destination : string
            The name of the destination set

        Returns
        -------
        numpy.ndarray
        """

        from_set = self.get_entry(source)
        to_set = self.get_entry(destination)

        if from_set.coverage == to_set.coverage and \
                len(from_set) == len(to_set):
            msg = "Perform conversion from %s to %s"
            self.logger.info(msg, source, destination)
            coefficients = self._obtain_coefficients(from_set, to_set)

        elif from_set.coverage == to_set.coverage and \
                len(from_set) > len(to_set):
            msg = "Perform aggregation from %s to %s"
            self.logger.info(msg, source, destination)
            coefficients = self._obtain_coefficients(from_set, to_set)

        elif from_set.coverage == to_set.coverage and \
                len(from_set) < len(to_set):
            msg = "Perform disaggregation from %s to %s"
            self.logger.info(msg, source, destination)
            coefficients = self._obtain_coefficients(from_set, to_set)

        elif from_set.coverage < to_set.coverage:
            # Perform remapping
            msg = "Remapping is not yet implemented"
            raise NotImplementedError(msg)

        elif from_set.coverage > to_set.coverage:
            # Perform resampling
            msg = "Resampling is not yet implemented"
            raise NotImplementedError(msg)
        else:
            msg = "Cannot perform conversions where coverage is not " \
                  "equal for resolution sets"
            raise NotImplementedError(msg)

        return coefficients

    def _obtain_coefficients(self, from_set, to_set):
        """
        """
        coefficients = np.zeros((len(from_set), len(to_set)), dtype=np.float)
        self.logger.debug("Coefficients array is of shape %s for %s to %s",
                          coefficients.shape, from_set.name, to_set.name)

        from_names = from_set.get_entry_names()
        for to_idx, to_entry in enumerate(to_set):
            for from_idx in from_set.intersection(to_entry):

                proportion = from_set.get_proportion(from_idx, to_entry)
<commit_msg>Register raises error if incomplete coverage<commit_after>"""Register, NDimensionalRegister and ResolutionSet
abstract classes to contain area, interval and unit metadata.

Implemented by :class:`smif.convert.interval.TimeIntervalRegister`,
:class:`smif.convert.area.RegionRegister` and
:class:`smif.convert.unit.UnitRegister`.
"""
import logging
from abc import ABCMeta, abstractmethod
from collections import OrderedDict, defaultdict

import numpy as np


class Register(metaclass=ABCMeta):
    """Abstract class which holds the ResolutionSets

    Arguments
    ---------
    axis : int, default=None
        The axis over which operations on the data array are performed

    """
    def __init__(self, axis=None):
        self.logger = logging.getLogger(__name__)
        self.axis = axis

    @property
    @abstractmethod
    def names(self):
        raise NotImplementedError

    @abstractmethod
    def register(self, resolution_set):
        raise NotImplementedError

    @abstractmethod
    def get_coefficients(self, source, destination):
        raise NotImplementedError

    def convert(self, data, from_set_name, to_set_name):
        """Convert a list of data points for a given set to another set

        Parameters
        ----------
        data: numpy.ndarray
        from_set_name: str
        to_set_name: str

        Returns
        -------
        numpy.ndarray

        """
        coefficients = self.get_coefficients(from_set_name, to_set_name)

        if self.axis is not None:
            data_count = data.shape[self.axis]
            if coefficients.shape[0] != data_count:
                msg = "Size of coefficient array does not match source " \
                      "resolution set from data matrix: %s != %s"
                raise ValueError(msg, coefficients.shape[self.axis],
                                 data_count)

        if self.axis == 0:
            converted = np.dot(coefficients.T, data)
        elif self.axis == 1:
            converted = np.dot(data, coefficients)
        else:
            converted = np.dot(data, coefficients)

        return converted


class NDimensionalRegister(Register):
    """Abstract class which holds N-Dimensional ResolutionSets

    Arguments
    ---------
    axis : int, default=None
        The axis over which operations on the data array are performed

    """
    def __init__(self, axis=None):
        super().__init__(axis)
        self._register = OrderedDict()
        self._conversions = defaultdict(dict)

    def register(self, resolution_set):
        """Add a ResolutionSet to the register

        Detects duplicate references to the same annual-hours by performing a
        convolution of the two one-dimensional arrays of time-intervals.

        Parameters
        ----------
        resolution_set : smif.convert.ResolutionSet

        Raises
        ------
        ValueError
            If a ResolutionSet of the same name already exists in the register
        """
        if resolution_set.name in self._register:
            msg = "A ResolutionSet named {} has already been loaded"
            raise ValueError(msg.format(resolution_set.name))

        self._register[resolution_set.name] = resolution_set

    @property
    def names(self):
        """Names of registered region sets

        Returns
        -------
        sets: list of str
        """
        return list(self._register.keys())

    def get_entry(self, name):
        """Returns the ResolutionSet of `name`

        Arguments
        ---------
        name : str
            The unique identifier of a ResolutionSet in the register

        Returns
        -------
        smif.convert.ResolutionSet

        """
        if name not in self._register:
            msg = "ResolutionSet '{}' not registered"
            raise ValueError(msg.format(name))
        return self._register[name]

    def get_coefficients(self, source, destination):
        """Get coefficients representing intersection of sets

        Arguments
        ---------
        source : string
            The name of the source set
        destination : string
            The name of the destination set

        Returns
        -------
        numpy.ndarray
        """

        from_set = self.get_entry(source)
        to_set = self.get_entry(destination)

        if from_set.coverage == to_set.coverage and \
                len(from_set) == len(to_set):
            msg = "Perform conversion from %s to %s"
            self.logger.info(msg, source, destination)
            coefficients = self._obtain_coefficients(from_set, to_set)

        elif from_set.coverage == to_set.coverage and \
                len(from_set) > len(to_set):
            msg = "Perform aggregation from %s to %s"
            self.logger.info(msg, source, destination)
            coefficients = self._obtain_coefficients(from_set, to_set)

        elif from_set.coverage == to_set.coverage and \
                len(from_set) < len(to_set):
            msg = "Perform disaggregation from %s to %s"
            self.logger.info(msg, source, destination)
            coefficients = self._obtain_coefficients(from_set, to_set)

        elif from_set.coverage != to_set.coverage:
            # Perform remapping
            msg = "Cannot perform conversions where coverage is not " \
                  "equal for resolution sets"
            raise NotImplementedError(msg)

        else:
            msg = "An unidentified error occured"
            raise RuntimeError(msg)

        return coefficients

    def _obtain_coefficients(self, from_set, to_set):
        """
        """
        coefficients = np.zeros((len(from_set), len(to_set)), dtype=np.float)
        self.logger.debug("Coefficients array is of shape %s for %s to %s",
                          coefficients.shape, from_set.name, to_set.name)

        from_names = from_set.get_entry_names()
        for to_idx, to_entry in enumerate(to_set):
            for from_idx in from_set.intersection(to_entry):

                proportion = from_set.get_proportion(from_idx, to_entry)

{'fraction_non_alphanumeric': 0.08895556276354201, 'fraction_numerical': 0.00048653908530651963, 'mean_word_length': 2.0265030674846627, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 6, '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': '334002', 'n_tokens_mistral': 3292, 'n_tokens_neox': 3158, 'n_words': 935}