Changeset 956


Ignore:
Timestamp:
06/25/20 03:03:28 (3 weeks ago)
Author:
Maciej Komosinski
Message:

Allowed multiple instances of FramsticksCLI class to safely use a single Framsticks CLI executable (no danger to use the same file names)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • framspy/FramsticksCLI.py

    r953 r956  
    22from enum import Enum
    33from typing import List
     4from itertools import count  # for tracking multiple instances
    45import json
    56import sys, os
     
    1112        """Runs Framsticks CLI (command-line) executable and communicates with it using standard input and output.
    1213        You can perform basic operations like mutation, crossover, and evaluation of genotypes.
    13         This way you can perform evolution controlled by python, access and manipulate genotypes.
     14        This way you can perform evolution controlled by python as well as access and manipulate genotypes.
    1415        You can even design and use in evolution your own genetic representation implemented entirely in python.
    1516
     
    3031        SETEXPEDEF_CMD = "expdef standard-eval" + "\n"
    3132        GETSIMPLEST_CMD = "getsimplest"
    32         GETSIMPLEST_FILE = FILE_PREFIX + "simplest.gen"
    33         EVALUATE_CMD = "evaluate eval-allcriteria.sim "
     33        GETSIMPLEST_FILE = "simplest.gen"
     34        EVALUATE_CMD = "evaluate eval-allcriteria.sim"
    3435        EVALUATE_FILE = "genos_eval.json"
    3536        CROSSOVER_CMD = "crossover"
    36         CROSSOVER_FILE = FILE_PREFIX + "child.gen"
     37        CROSSOVER_FILE = "child.gen"
    3738        DISSIMIL_CMD = "dissimil"
    38         DISSIMIL_FILE = FILE_PREFIX + "dissimilarity_matrix.gen"
     39        DISSIMIL_FILE = "dissimilarity_matrix.gen"
    3940        ISVALID_CMD = "isvalid"
    40         ISVALID_FILE = FILE_PREFIX + "validity.gen"
     41        ISVALID_FILE = "validity.gen"
    4142        MUTATE_CMD = "mutate"
    42         MUTATE_FILE = FILE_PREFIX + "mutant.gen"
    43 
    44         CLI_INPUT_FILE = FILE_PREFIX + "genotypes.gen"
     43        MUTATE_FILE = "mutant.gen"
     44
     45        CLI_INPUT_FILE = "genotypes.gen"
     46
     47        _last_instance_id = count(0)  # "static" counter incremented when a new instance is created. Used for unique filenames
    4548
    4649
    4750        def __init__(self, framspath, framsexe):
     51                self.id = next(FramsticksCLI._last_instance_id)
    4852                self.frams_path = framspath
    4953                self.frams_exe = framsexe if framsexe is not None else 'frams.exe' if os.name == "nt" else 'frams.linux'
     
    8993                print('OK.')
    9094                print('Performing a basic test 2/3... ', end='')
    91                 assert self.isValid("X[0:0]") == True
     95                assert self.isValid("X[0:0]") is True
    9296                print('OK.')
    9397                print('Performing a basic test 3/3... ', end='')
    94                 assert self.isValid("X[0:0],") == False
     98                assert self.isValid("X[0:0],") is False
    9599                print('OK.')
    96100                if not self.DETERMINISTIC:
     
    105109
    106110
    107         def __saveGenotypeToFile(self, genotype, name, mode):
    108                 outpath = os.path.join(self.writing_path, name)
    109                 outfile = open(outpath, mode)
    110                 outfile.write("org:\n")
    111                 outfile.write("genotype:~\n")
    112                 outfile.write(genotype + "~\n\n")  # TODO proper quoting of special characters in genotype...
    113                 outfile.close()
    114                 return name
    115 
    116 
    117         def __saveToFile(self, genotype, name, mode):
    118                 outpath = os.path.join(self.writing_path, name)
    119                 outfile = open(outpath, mode)
    120                 outfile.write(genotype)
    121                 outfile.close()
    122                 return name
    123 
    124 
    125         def __removeFile(self, path):
    126                 filepath = os.path.join(self.writing_path, path)
    127                 if os.path.exists(filepath):
    128                         os.remove(filepath)
     111        def __getPrefixedFilename(self, filename: str) -> str:
     112                # Returns filename with unique instance id appended so there is no clash when many instances of this class use the same Framsticks CLI executable
     113                return FramsticksCLI.FILE_PREFIX + str(chr(ord('A') + self.id)) + '_' + filename
     114
     115
     116        def __saveGenotypeToFile(self, genotype, name, mode, saveformat):
     117                relname = self.__getPrefixedFilename(name)
     118                absname = os.path.join(self.writing_path, relname)
     119                if mode == 'd':  # special mode, 'delete'
     120                        if os.path.exists(absname):
     121                                os.remove(absname)
     122                else:
     123                        outfile = open(absname, mode)
     124                        if saveformat == self.GENO_SAVE_FILE_FORMAT["RAWGENO"]:
     125                                outfile.write(genotype)
     126                        else:
     127                                outfile.write("org:\n")
     128                                outfile.write("genotype:~\n")
     129                                outfile.write(genotype + "~\n\n")  # TODO proper quoting of special characters in genotype...
     130                        outfile.close()
     131                return relname, absname
    129132
    130133
     
    140143
    141144        def __runCommand(self, command, genotypes, result_file_name, saveformat) -> List[str]:
    142                 filenames = []  # list of file names with input data for the command
     145                filenames_rel = []  # list of file names with input data for the command
     146                filenames_abs = []  # same list but absolute paths actually used
    143147                if saveformat == self.GENO_SAVE_FILE_FORMAT["RAWGENO"]:
    144148                        for i in range(len(genotypes)):
    145                                 filenames.append(self.__saveToFile(genotypes[i], "genotype" + str(i) + ".gen", "w"))  # plain text format = must have a separate file for each genotype
     149                                # plain text format = must have a separate file for each genotype
     150                                rel, abs = self.__saveGenotypeToFile(genotypes[i], "genotype" + str(i) + ".gen", "w", self.GENO_SAVE_FILE_FORMAT["RAWGENO"])
     151                                filenames_rel.append(rel)
     152                                filenames_abs.append(abs)
    146153                elif saveformat == self.GENO_SAVE_FILE_FORMAT["NATIVEFRAMS"]:
    147                         self.__removeFile(self.CLI_INPUT_FILE)  # ensure there is nothing left from the last run of the program because we "a"ppend to file in the loop below
     154                        self.__saveGenotypeToFile(None, self.CLI_INPUT_FILE, 'd', None)  # 'd'elete: ensure there is nothing left from the last run of the program because we "a"ppend to file in the loop below
    148155                        for i in range(len(genotypes)):
    149                                 outfilename = self.__saveGenotypeToFile(genotypes[i], self.CLI_INPUT_FILE, "a")
    150                         filenames.append(outfilename)  # since we use the same file in the loop above, add this file only once (i.e., outside of the loop)
    151 
    152                 if result_file_name != self.EVALUATE_FILE:  # all functions except for evaluate provide frams with the file name to write to
    153                         self.child.sendline(command + " " + " ".join(filenames) + " " + result_file_name + "\n")
    154                 else:
    155                         self.child.sendline(command + " " + " ".join(filenames) + "\n")
     156                                rel, abs = self.__saveGenotypeToFile(genotypes[i], self.CLI_INPUT_FILE, "a", self.GENO_SAVE_FILE_FORMAT["NATIVEFRAMS"])
     157                        #  since we use the same file in the loop above, add this file only once (i.e., outside of the loop)
     158                        filenames_rel.append(rel)
     159                        filenames_abs.append(abs)
     160
     161                result_file_name = self.__getPrefixedFilename(result_file_name)
     162                cmd = command + " " + " ".join(filenames_rel) + " " + result_file_name
     163                self.child.sendline(cmd + '\n')
    156164                self.__readFromFramsCLIUntil(self.STDOUT_ENDOPER_MARKER)
    157                 filenames.append(os.path.join(self.writing_path, self.OUTPUT_DIR, result_file_name))
    158                 return filenames  # last element is a path to the file containing results
    159 
    160 
    161         def __cleanUpCommandResults(self, filepaths):
    162                 """Deletes files with results created by the command."""
    163                 for i in filepaths:
    164                         if i == filepaths[-1]:
    165                                 os.remove(i)  # the result is written with its full path and we have used it before so the file surely exists
    166                         else:
    167                                 self.__removeFile(i)
     165                filenames_abs.append(os.path.join(self.writing_path, self.OUTPUT_DIR, result_file_name))
     166                return filenames_abs  # last element is a path to the file containing results
     167
     168
     169        def __cleanUpCommandResults(self, filenames):
     170                """Deletes files with results just created by the command."""
     171                for name in filenames:
     172                        os.remove(name)
    168173
    169174
     
    237242        parser.add_argument('-path', type=ensureDir, required=True, help='Path to Framsticks CLI without trailing slash.')
    238243        parser.add_argument('-exe', required=False, help='Executable name. If not given, "frams.exe" or "frams.linux" is assumed.')
    239         parser.add_argument('-genformat', required=False, help='Genetic format for the demo run, for example 4, 9, or B. If not given, f1 is assumed.')
     244        parser.add_argument('-genformat', required=False, help='Genetic format for the demo run, for example 4, 9, or S. If not given, f1 is assumed.')
    240245        return parser.parse_args()
    241246
Note: See TracChangeset for help on using the changeset viewer.