Source code for alogos.exceptions

"""All custom exceptions used in the package."""

from ._utilities.operating_system import NEWLINE as _NEWLINE


__all__ = [
    "GrammarError",
    "ParserError",
    "ParameterError",
    "GenotypeError",
    "InitializationError",
    "OperatorError",
    "MappingError",
    "DatabaseError",
]


[docs]class GrammarError(Exception): """Raised when a grammar can not be read, written or is ill-formed. Examples -------- - The set of nonterminal symbols, terminal symbols or production rules is empty. - A nonterminal symbol has no production rule where its right-hand side is defined. """
def raise_write_nonterminal_error(text): message = ( "Nonterminal symbol N({}) contains text that does not allow to " "write it with the available surrounding markers.".format(text) ) raise GrammarError(message) def raise_write_terminal_error(text): message = ( "Terminal symbol T({}) contains text that does not allow to " "write it with the available surrounding markers.".format(text) ) raise GrammarError(message) def raise_delimiting_symbol_error_1(): message = ( 'Either "start_terminal_symbol" and "end_terminal_symbol", ' 'or "start_terminal_symbol2" and "end_terminal_symbol2", or both need to ' "be nonempty strings in order to serve as indicators for terminal symbols." ) raise GrammarError(message) def raise_delimiting_symbol_error_2(): message = ( 'Either "start_terminal_symbol" and "end_terminal_symbol", ' 'or "start_terminal_symbol2" and "end_terminal_symbol2", or both need to ' "be nonempty strings in order to serve as indicators for terminal symbols." ) raise GrammarError(message) def raise_empty_productions_error(): message = "The set of production rules is empty." raise GrammarError(message) def raise_empty_nonterminals_error(): message = "The set of nonterminal symbols is empty." raise GrammarError(message) def raise_empty_terminals_error(): message = "The set of terminal symbols is empty." raise GrammarError(message) def raise_missing_nonterminals_error(missing_nonterminals): reported_text = _NEWLINE.join(missing_nonterminals) message = ( "Following nonterminals appeared on a right-hand side of a production rule " "but on no left-hand side:{}{}".format(_NEWLINE, reported_text) ) raise GrammarError(message) def raise_delimiter_symbol_error_1(): message = ( 'Please provide either both a nonempty "start_nonterminal_symbol" ' 'and "end_nonterminal_symbol" or none of them.' ) raise GrammarError(message) def raise_surrounding_symbol_error_2(): message = ( 'Please provide either both a nonempty "start_terminal_symbol" ' 'and "end_terminal_symbol" or none of them.' ) raise GrammarError(message) def raise_surrounding_symbol_error_3(): message = ( 'Please provide either both a nonempty "start_terminal_symbol2" ' 'and "end_terminal_symbol2" or none of them.' ) raise GrammarError(message) def raise_surrounding_symbol_error_4(): message = ( 'Either "start_nonterminal_symbol", ' '"start_terminal_symbol" or both need to be a nonempty string.' ) raise GrammarError(message) def raise_surrounding_symbol_error_5(listing): message = ( '"start_nonterminal_symbol" and "end_nonterminal_symbol" may not have ' 'an overlap with any of "start_terminal_symbol", "end_terminal_symbol", ' '"start_terminal_symbol2" and "end_terminal_symbol2". Following values ' "were found to cause a problem: {}".format(listing) ) raise GrammarError(message)
[docs]class ParserError(Exception): """Raised when parsing a string with a grammar fails. Examples -------- - The string contains characters that are not part of any terminal symbol. - The string does not belong to the language of the given grammar. - The chosen parser is not compatible with the given grammar. """
def raise_lark_parser_mult_error(): message = ( 'Getting multiple parse trees is currently only supported with parser="earley".' ) raise ParserError(message) def _raise_parser_creation_error(excp): message = ( "Parsing failed during creation of the chosen parser. Perhaps it is not " "compatible with the form of the provided grammar.{nl}{nl}" 'Lark raised the exception "{name}" with following message: ' "{text}".format(nl=_NEWLINE, name=type(excp).__name__, text=excp) ) raise ParserError(message) from None def _raise_parser_string_error(excp): message = ( "Parsing failed during analyzing the string.{nl}{nl}" 'Lark raised the exception "{name}" with following message: ' "{text}".format(nl=_NEWLINE, name=type(excp).__name__, text=excp) ) raise ParserError(message) from None def _raise_parser_node_error(node_label): message = ( "Discovered an unexpected symbol in the Lark tree that contains " 'all of the discovered parse trees: "{}"'.format(node_label) ) raise ParserError(message) from None
[docs]class ParameterError(Exception): """Raised when a parameter or collection of parameters is invalid. Examples -------- - An unknown parameter is provided or requested by a user. - All variation operators of an evolutionary algorithm are turned off. - All stop criteria of a search algorithm are inactive. """
def raise_unknown_parameter_error(parameter_name, default_parameters): message = ( "An unknown parameter was attempted to be used: {val}" "{nl}{nl}{defv}".format( val=repr(parameter_name), nl=_NEWLINE, defv=default_parameters ) ) raise ParameterError(message) from None def raise_initial_parameter_error(name): message = ( "Got an invalid parameter name, which is reserved as " "method name: {}".format(repr(name)) ) raise ParameterError(message) from None def raise_stop_parameter_error(): message = ( "No stop criterion was set, therefore a search would continue to run indefinitely. " "Please provide a value for one or more of the following parameters: " "max_generations, max_fitness_evaluations, max_runtime_in_seconds, max_or_min_fitness" ) raise ParameterError(message) from None def raise_missing_variation_error(): message = ( "Neither crossover nor mutation are activated. This means " "there is no variation, no new candidate solutions are generated" " and the optimization can not progress towards better " "objective function values." ) raise ParameterError(message) from None def raise_operator_lookup_error(description, name, location): try: available = ", ".join( name for name in dir(location) if not name.startswith("_") ) except Exception: available = "None" message = ( "{desc} operator is not known to the chosen algorithm in its current configuration. " "{nl}Given operator name: {name}" "{nl}Available operator names: {av}" "{nl}Lookup location: {loc}".format( desc=description, nl=_NEWLINE, name=str(name), av=available, loc=repr(location), ) ) raise ParameterError(message) from None def raise_no_genotype_error(): message = ( "No genotype was provided in the parameters dictionary " 'under the key "init_genotype".' ) raise ValueError(message) def raise_no_phenotype_error(): message = ( "No phenotype was provided in the parameters dictionary " 'under the key "init_phenotype".' ) raise ValueError(message) def raise_no_derivation_tree_error(): message = ( "No derivation tree was provided in the parameters dictionary " 'under the key "init_derivation_tree".' ) raise ValueError(message) def raise_no_genotypes_error(): message = ( "No genotypes were provided in the parameters dictionary " 'under the key "init_genotypes".' ) raise ValueError(message) def raise_no_phenotypes_error(): message = ( "No phenotypes were provided in the parameters dictionary " 'under the key "init_phenotypes".' ) raise ValueError(message) def raise_no_derivation_trees_error(): message = ( "No derivation trees were provided in the parameters dictionary " 'under the key "init_derivation_trees".' ) raise ValueError(message)
[docs]class GenotypeError(Exception): """Raised when a genotype is not well-formed or modified. Examples -------- - The genotype can not be interpreted as a list of integers for Grammatical Evolution (GE). - The genotype can not be interpreted as a derivation tree for Context-Free Grammar Genetic Programming (CFGGP). - A user tries to modify the immutable genotype attribute of an individual. """
def raise_data_write_error(): message = ( 'The attribute "data" is immutable. It can only be assigned during ' "object creation. Afterwards it is used to identify a genotype, " "e.g. when it is added to a set or used as a key in a dictionary, " "and therefore it may not be modified." ) raise GenotypeError(message) from None def raise_cfggp_genotype_error(data): message = ( "The given data could not be interpreted as a CFG-GP genotype. " "It needs to be a derivation tree or a serialized derivation tree. " "Given data: {}".format(repr(data)) ) raise GenotypeError(message) from None def raise_cfggpst_genotype_error(data): message = ( "The given data could not be interpreted as a CFG-GP-ST genotype. " "It needs to be a serialized derivation tree in form of " "two tuples of integers. " "Given data: {}".format(repr(data)) ) raise GenotypeError(message) from None def raise_dsge_genotype_error(data): message = ( "The given data could not be interpreted as a DSGE genotype. " "It needs to be a non-empty tuple of tuples of integers. " "Given data: {}".format(repr(data)) ) raise GenotypeError(message) from None def raise_dsge_genotype_repair_error(max_expansions): message = ( "The provided DSGE genotype could not be repaired, because the number of " "expansions reached the max_expansions limit: {}".format(max_expansions) ) raise GenotypeError(message) from None def raise_ge_genotype_error(data): message = ( "The given data could not be interpreted as a GE genotype. " "It needs to be a non-empty tuple of integers. " "Given data: {}".format(repr(data)) ) raise GenotypeError(message) from None def raise_pige_genotype_error(data): message = ( "The given data could not be interpreted as a piGE genotype. " "It needs to be a non-empty tuple of integers. " "Given data: {}".format(repr(data)) ) raise GenotypeError(message) from None def raise_whge_genotype_error(data): message = ( "The given data could not be interpreted as a WHGE genotype. " "It needs to be a non-empty bitarray. " "Given data: {}".format(repr(data)) ) raise GenotypeError(message) from None
[docs]class InitializationError(Exception): """Raised when intitialization of an individual or population fails. Examples -------- - A user-provided genotype is not well-formed. - A user-provided phenotype can not be parsed and reverse mapped to a genotype. """
def raise_init_ind_from_gt_error(genotype): message = ( "Initialization of an individual from following given genotype " "failed: {}".format(repr(genotype)) ) raise InitializationError(message) from None def raise_init_ind_from_dt_error(derivation_tree): message = ( "Initialization of an individual from following given derivaton tree " "failed: {}".format(repr(derivation_tree)) ) raise InitializationError(message) from None def raise_init_ind_from_phe_error(phenotype): message = ( "Initialization of an individual from following given phenotype " "failed: {}".format(repr(phenotype)) ) raise InitializationError(message) from None def raise_init_ind_rand_gt_error(): message = "Initialization of an individual from a random genotype failed." raise InitializationError(message) from None def raise_init_ind_valid_rand_gt_error(max_tries): message = ( "Initialization of a valid individual from a random genotype failed " "after {} tries.".format(max_tries) ) raise InitializationError(message) from None def raise_init_ind_grow_error(): message = 'Initialization of an individual with the "grow" method failed.' raise InitializationError(message) from None def raise_init_ind_full_error(): message = 'Initialization of an individual with the "full" method failed.' raise InitializationError(message) from None def raise_init_ind_pi_grow_error(): message = 'Initialization of an individual with the "pi grow" method failed.' raise InitializationError(message) from None def raise_init_ind_ptc2_error(): message = 'Initialization of an individual with the "PTC2" method failed.' raise InitializationError(message) from None def raise_init_pop_from_gt_error(): message = "Initialization of a population from given genotypes failed." raise InitializationError(message) def raise_init_pop_from_dt_error(): message = "Initialization of a population from given derivation trees failed." raise InitializationError(message) def raise_init_pop_from_phe_error(): message = "Initialization of a population from given phenotypes failed." raise InitializationError(message) def raise_init_pop_rand_gt_error(): message = "Initialization of a population from random genotypes failed." raise InitializationError(message) def raise_init_pop_gp_rhh_error(): message = 'Initialization of a population with gp_rhh (="ramped half and half" from Genetic Programming) failed.' raise InitializationError(message) def raise_init_pop_pi_rhh_error(): message = 'Initialization of a population with pi_rhh (="ramped half and half" with PI Grow from piGE) failed.' raise InitializationError(message) def raise_init_pop_ptc2_error(): message = "Initialization of a population with PTC2 (=probabilistic tree creation 2) failed." raise InitializationError(message) def raise_init_pop_unique_gen_error( num_found_genotypes, num_wanted_genotypes, num_tries ): message = "Found only {} of {} unique genotypes after {} tries.".format( num_found_genotypes, num_wanted_genotypes, num_tries ) raise InitializationError(message) def raise_init_pop_unique_phe_error( num_found_phenotypes, num_wanted_phenotypes, num_tries ): message = "Found only {} of {} unique phenotypes after {} tries.".format( num_found_phenotypes, num_wanted_phenotypes, num_tries ) raise InitializationError(message) def raise_pop_assignment_error(value): message = ( "Only an individual can be assigned to a population. Got an object of " "type {} instead.".format(type(value)) ) # TypeError is required here by Python raise TypeError(message)
[docs]class OperatorError(Exception): """Raised when a search operator fails. Examples -------- - A crossover operator that requires two parent genotypes of equal length fails because it gets two individuals with different genotype lengths as input. """
def raise_crossover_lp_error1(l1, l2): message = ( "The crossover operator is length preserving and requires that both " "parent genotypes have equal length. Instead they had following two " "different lengths: {}, {}".format(l1, l2) ) raise OperatorError(message) def raise_crossover_lp_error2(): message = ( "The crossover operator requires that each parent has a minimal " "genotype length of 2. This was not the case." ) raise OperatorError(message)
[docs]class MappingError(Exception): """Raised when a genotype-to-phenotype mapping or its inverse fails. Examples -------- - Forward mapping: No string of terminals could be found within the allowed number of expansions or wrappings. - Reverse mapping: The provided grammar contains no production rule that would match a production found in a given derivation tree. """
def raise_max_expansion_error(max_expansions): message = ( "No string of the grammar's language was found before reaching the provided " "maximum number of expansions: {}".format(max_expansions) ) raise MappingError(message) def raise_max_wraps_error(max_wraps): message = ( "No string of the grammar's language was found before reaching the provided " "maximum number of wraps: {}".format(max_wraps) ) raise MappingError(message) def raise_missing_nt_error(nonterminal_node): nt_text = "<{}>".format(nonterminal_node.symbol) message = ( "For a nonterminal, no production rule could be found in the " "grammar: {}".format(nt_text) ) raise MappingError(message) from None def raise_missing_rhs_error(nonterminal_node, rhs): message = ( "For a derivation step in the tree, no corresponding production rule " "could be found in the grammar: <{}> -> {}".format(nonterminal_node, rhs) ) raise MappingError(message) from None def raise_invalid_mapping_data1(data): message = ( "Reverse mapping got invalid input data. " "The given phenotype is a string that is not a " "member of the grammar's language: {}".format(repr(data)) ) raise MappingError(message) def raise_invalid_mapping_data2(data): message = ( "Reverse mapping got invalid input data. It is neither a phenotype " "nor derivation tree: {}".format(data) ) raise MappingError(message) def raise_limited_codon_size_error(chosen_rule_idx, max_int): message = ( "Reverse mapping could not encode a choice within the given codon size limit. " "The index of the chosen rule is {}, but the maximum integer that can be " "encoded is {}.".format(chosen_rule_idx, max_int) ) raise MappingError(message) def raise_dsge_mapping_error1(genotype, symbols): message = ( "The provided DSGE genotype is invalid, because the length of the " "genotype ({}) does not fit to the number of " "nonterminal symbols ({}).".format(len(genotype), len(symbols)) ) raise MappingError(message) from None def raise_dsge_mapping_error2(gene_index, symbol): message = ( "The provided DSGE genotype is invalid, because gene number {} (which corresponds to " "nonterminal symbol <{}>) does not contain enough integers to complete the " "mapping.".format(gene_index, symbol) ) raise MappingError(message) from None def raise_dsge_mapping_error3(gene_index, symbol, rules, chosen_rule_idx): message = ( "The provided DSGE genotype is invalid, because gene number {} (which corresponds to " "nonterminal symbol <{}>) contains the integer {} that can not be used to select " "a production out of {} available " "ones.".format(gene_index, symbol, chosen_rule_idx, len(rules)) ) raise MappingError(message) from None
[docs]class DatabaseError(Exception): """Raised when storing or retrieving data from a database fails. Examples -------- - The provided filepath does not point to a valid file-based database. - There is no database entry that matches to a user-provided value. """
def raise_generation_range_error(): message = ( 'The argument "generation_range" could not be interpreted as two integers.' ) raise ValueError(message) def raise_ind_max_id_error(): message = ( "The maximum individual id returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_pop_size_min_error(): message = ( "The minimum population size returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_pop_size_max_error(): message = ( "The maximum population size returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_generation_first_error(): message = ( "The first generation returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_generation_last_error(): message = ( "The last generation returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_fitness_min_error(): message = ( "The minimum fitness returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_fitness_max_error(): message = ( "The maximum fitness returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_fitness_min_n_error(): message = ( "The minimum fitness after n evaluations returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_fitness_max_n_error(): message = ( "The maximum fitness after n evaluations returned by the database is NaN. " "This could mean that there are no entries in the database yet." ) raise DatabaseError(message) def raise_no_database_export_error(): message = ( "There is no data to export. " 'The parameter "database_on" needs to be set to "True" in order ' "for a database to be created and filled with data " "by the evolutionary search." ) raise DatabaseError(message) from None def raise_reset_database_error(filepath, n_tries): message = ( "Tried to rename the database file located at {} during search reset. " "Could not find a new filepath after {} tries.".format(repr(filepath), n_tries) ) raise DatabaseError(message) from None def raise_import_database_error(filepath): message = ( "Could not load the database from filepath {} because it is " "not a file.".format(repr(filepath)) ) raise DatabaseError(message) from None def raise_load_population_error(generation): message = ( "Attempted to load generation {} from the database but could not find any " "entries for it. This population seems not to be stored in the " "database.".format(generation) ) raise DatabaseError(message) from None def raise_individual_clash_error(): message = ( "Failed to load an individual from the old database. There is " "another individual with the same id in the current database." ) raise DatabaseError(message) from None def raise_serialization_error(string): message = ( "Could not serialize the given string. " "It can not be enclosed in any quotes, since it contains " "all available variants: {}".format(string) ) raise DatabaseError(message) from None