Source code for polymerist.genutils.sequences.discernment.enumeration

'''Front-facing solver facade for the DISCERNMENT Problem'''

__author__ = 'Timotej Bernat'
__email__ = 'timotej.bernat@colorado.edu'

from typing import Generator, Iterable, Mapping, Sequence, Union

from .inventory import SymbolInventory, T, L
from .strategies import DISCERNMENTStrategy, DISCERNMENTStrategyStack


# TODO : add custom Exceptions to provide more detailed feedback?
[docs] class DISCERNMENTSolver(): '''Encapsulation class for solving generalized ransom-note index enumeration problems for arbitrary words, bins, and solving algorithms''' def __init__(self, symbol_inventory : Union[SymbolInventory[T, L], Sequence[Iterable[T]], Mapping[L, Sequence[Iterable[T]]]], strategy : DISCERNMENTStrategy=DISCERNMENTStrategyStack()) -> None: if isinstance(symbol_inventory, SymbolInventory): self._symbol_inventory = symbol_inventory else: self._symbol_inventory = SymbolInventory.from_bins(symbol_inventory) self.strategy = strategy @property def symbol_inventory(self) -> SymbolInventory[T, L]: '''Cast symbol inventory as copy to avoid mutation during partial traversals (i.e. peek at first item)''' return self._symbol_inventory.deepcopy()
[docs] def enumerate_choices(self, word : Sequence[T], ignore_multiplicities : bool=False, unique_bins : bool=False) -> Generator[L, None, None]: '''Enumerate all possible choices using specified solution strategy''' return self.strategy.enumerate_choice_labels( word=word, symbol_inventory=self.symbol_inventory, ignore_multiplicities=ignore_multiplicities, unique_bins=unique_bins, )
[docs] def choice_solutions_exist(self, word : Sequence[T], ignore_multiplicities : bool=False, unique_bins : bool=False) -> bool: '''Precheck to see if a solution exists without attemting full enumeration''' if not self.symbol_inventory.contains_word(word, ignore_multiplicities=ignore_multiplicities): return False try: first_solution = next(self.enumerate_choices(word, ignore_multiplicities=ignore_multiplicities, unique_bins=unique_bins)) except StopIteration: return False else: return (first_solution != tuple()) # consider the null index sequence of the empty tuple to be a failure
choices_exist = choice_exists = solutions_exist = solution_exists = choice_solutions_exist # aliases for convenience