Changeset 1310


Ignore:
Timestamp:
06/26/24 04:52:55 (5 months ago)
Author:
Maciej Komosinski
Message:

Introduced GENOTYPE_INVALID_OFFSPRING_SUBSTITUTE_ORIGINAL (True by default) for developers who do not specifically detect and handle returned invalid genotypes after mutate() and crossOver()

File:
1 edited

Legend:

Unmodified
Added
Removed
  • framspy/FramsticksLib.py

    r1309 r1310  
    44import sys, os
    55import argparse
     6import random
    67import numpy as np
    78import frams
     
    1920
    2021
    21 
    2222class FramsticksLib:
    2323        """Communicates directly with Framsticks library (.dll or .so or .dylib).
     
    3636        DETERMINISTIC: bool = False  # set to True to have the same results in each run
    3737
    38         GENOTYPE_INVALID = "/*invalid*/"  # this is how genotype invalidity is represented in Framsticks
     38        GENOTYPE_INVALID = "/*invalid*/"  # this is how genotype invalidity is represented in Framsticks (Geno.format is 'invalid'). Mutation and crossover operators return such a genotype if they were unable to perform their operation (information about the cause is stored in the Geno.info field - see GenMan.cpp)
     39        GENOTYPE_INVALID_OFFSPRING_SUBSTITUTE_ORIGINAL = True  # if True, when mutation or crossover is unable to perform their operation for the provided genotype(s), return the original genotype (and print a warning). If this happens extremely rarely, it may be ignored - but if not, you need to identify the reason (e.g., particular genotypes that cause the problem), fix it or change the logic of your algorithm. A more strict approach is to keep this field False - then you must always check if GENOTYPE_INVALID was returned by mutate() or crossOver(), and handle this situation properly (e.g., choose different parent(s) for mutate() or crossOver() and repeat until you get a valid offspring).
     40
    3941        EVALUATION_SETTINGS_FILE = [  # all files MUST be compatible with the standard-eval expdef. The order they are loaded in is important!
    4042                "eval-allcriteria.sim",  # a good trade-off in performance sampling period ("perfperiod") for vertpos and velocity
     
    8890
    8991
     92        @staticmethod
     93        def shortGenotype(genotype: str) -> str:
     94                """
     95                Returns a few initial characters of the genotype, just for information/identifying the genotype.
     96                """
     97                return repr(genotype) if len(genotype) <= 10 else repr(genotype[:10] + "...")
     98
     99
    90100        def getSimplest(self, genetic_format: str) -> str:
    91101                return frams.GenMan.getSimplest(genetic_format).genotype._string()
     
    168178                        ec.close()
    169179                        if ec.error_count._value() > 0:
    170                                 print('\nErrors while evaluating this genotype list:\n',genotype_list,sep='\t')
     180                                print('\nErrors while evaluating this genotype list:\n', genotype_list, sep='\t')
    171181                                print(ec.messages)  # if errors occurred, output all caught messages for debugging
    172182                                raise RuntimeError("[ERROR] %d error(s) and %d warning(s) while evaluating %d genotype(s)" % (ec.error_count._value(), ec.warning_count._value(), len(genotype_list)))  # make errors fatal; by default they stop the simulation anyway so let's not use potentially incorrect or partial results and fix the cause first.
     
    191201
    192202                mutated = []
    193                 for g in genotype_list:
    194                         mutated.append(frams.GenMan.mutate(frams.Geno.newFromString(g)).genotype._string())
     203                for genotype_parent in genotype_list:
     204                        offspring = frams.GenMan.mutate(frams.Geno.newFromString(genotype_parent))
     205                        offspring_genotype = offspring.genotype._string()
     206                        if offspring_genotype == self.GENOTYPE_INVALID and self.GENOTYPE_INVALID_OFFSPRING_SUBSTITUTE_ORIGINAL:
     207                                print('[WARN] mutate(%s) failed but you requested GENOTYPE_INVALID_OFFSPRING_SUBSTITUTE_ORIGINAL, so returning the original genotype instead. Reason for failure: %s' % (self.shortGenotype(genotype_parent), offspring.info._string()))
     208                                offspring_genotype = genotype_parent
     209                        mutated.append(offspring_genotype)
    195210                if len(genotype_list) != len(mutated):
    196211                        raise RuntimeError("Submitted %d genotypes, received %d mutants" % (len(genotype_list), len(mutated)))
     
    203218                        The genotype of the offspring. self.GENOTYPE_INVALID if the crossing over failed.
    204219                """
    205                 return frams.GenMan.crossOver(frams.Geno.newFromString(genotype_parent1), frams.Geno.newFromString(genotype_parent2)).genotype._string()
     220                offspring = frams.GenMan.crossOver(frams.Geno.newFromString(genotype_parent1), frams.Geno.newFromString(genotype_parent2))
     221                offspring_genotype = offspring.genotype._string()
     222                if offspring_genotype == self.GENOTYPE_INVALID and self.GENOTYPE_INVALID_OFFSPRING_SUBSTITUTE_ORIGINAL:
     223                        print('[WARN] crossOver(%s, %s) failed but you requested GENOTYPE_INVALID_OFFSPRING_SUBSTITUTE_ORIGINAL, so returning a random parent instead. Reason for failure: %s' % (self.shortGenotype(genotype_parent1), self.shortGenotype(genotype_parent2), offspring.info._string()))
     224                        offspring_genotype = random.choice([genotype_parent1, genotype_parent2])
     225                return offspring_genotype
    206226
    207227
Note: See TracChangeset for help on using the changeset viewer.