<commit_before>#!/usr/bin/python3

import collections  # for named tuple && Counter (multisets)
import re  # for regex
from enum import IntEnum # for enumerations (enum from C)
import logging # for logging functions

##########################################################################
# auxiliary definitions
class OperatorType(IntEnum):

    """Enumeration of operator types, ordered by precedence in calculus"""

    left_brace   = 1
    # right brace is never added to the postfix stack
    add          = 2
    subtract     = 3
    multiply     = 4
    divide       = 5
    power        = 6

# end class OperatorType

dictOperatorTypes = {
        'OPERATOR_ADD': OperatorType.add,
        'OPERATOR_SUBTRACT': OperatorType.subtract,
        'OPERATOR_MULTIPLY': OperatorType.multiply,
        'OPERATOR_DIVIDE': OperatorType.divide,
        'OPERATOR_POWER': OperatorType.power}

# tuple used to describe parsed data
Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])

##########################################################################
# class definitions

class NumericalPsystem():

    """Numerical P systems class"""

    def __init__(self):
        self.H = [] # array of strings (names of membranes)
        self.membranes = {} # map (dictioanry) between String membrane_name: Membrane object
        self.structure = None # MembraneStructure object (list of structural elements) [1 [2 ]2 ]1
        self.variables = [] # list of Pobjects that appear throughtout the P system


    def print(self, indentSpaces = 2, toString = False) :
        """Print a membrane with a given indentation level

        :indentSpaces: number of spaces used for indentation
        :toString: write to a string instead of stdout
        :returns: string print of the membrane if toString = True otherwise returns None """

        result = ""

        result += "var = {\n"
        for membraneName, membrane in self.membranes.items():
            result += " " * indentSpaces + "%s:\n %s" % (membraneName, membrane.print(indentSpaces + 2, toString=True))
        result += "}\n"

        if (toString):
            return result
        else:
            print(result)
    # end print()
# end class NumericalPsystem

class MembraneStructure(list):

    """P system membrane structure (list of structural elements)"""

    def __init__(self):
        list.__init__(self)
# end class MembraneStructure

class Membrane():

    """Membrane class, that can contain other membranes or be part of another membrane"""

    def __init__(self, parentMembrane = None):
        self.variables = [] # array of P objects
        self.programs = [] # list of Program objects

        self.enzymes = [] # array of P objects
        self.parent = parentMembrane # parent membrane (Membrane object)
        self.children = {} # map (dictioanry) between String membrane_name: Membrane object


    def print(self, indentSpaces = 2, toString = False) :
        """Print a membrane with a given indentation level

        :indentSpaces: number of spaces used for indentation
        :toString: write to a string instead of stdout
        :returns: string print of the membrane if toString = True otherwise returns None """

        result = " " * indentSpaces

        result += "var = {"
        for var in self.variables:
            result += "  %s = %s  " % (var.name, var.value)
        result += "}\n"

        if (toString):
            return result
        else:
            print(result)
    # end print()
# end class Membrane

class Program():

    """Program class"""

    def __init__(self):
        self.prodFunction = None # ProductionFunction object
        self.distribFunction = None # DistributionFunction object

    def print(self, indentSpaces = 2, toString = False) :
        """Print a program with a given indentation level

        :indentSpaces: number of spaces used for indentation
        :toString: write to a string instead of stdout
        :returns: string print of the rule if toString = True otherwise returns None """

        result = " " * indentSpaces

        # production section
        for prod in self.prodFunction:
            result += " " + prod.print(toString = True)

        result += " -> "

        # distribution section
        for i, distrib in enumerate(self.distribFunction):
            if (i == 0):
                result += distrib.print(toString = True)
            else:
                result += " + " + distrib.print(toString = True)

        if (toString):
            return result
        else:
            print(result)
    # end print()
# end class Program

class ProductionFunction():

    """Production function class that stores expressions using the postfix (reversed polish) form"""

    def __init__(self):
        self.postfixStack = [] # stack of operands and operators (auxiliary for postfix form)
        self.items = [] # list of operands and operators written in postfix (reverse polish) form

    def evaluate(self):
        """Evaluates the postfix form of a production function and returns the computed value.
        During the evaluation, Pobject references are replaced with their value.
        :returns: the computed value of the production function, as a numeric value"""<commit_msg>Implemented P system simulation functions:     * NumericalPsystem.runSimulationStep()     * NumericalPsystem.simulate()<commit_after>#!/usr/bin/python3

import collections  # for named tuple && Counter (multisets)
import re  # for regex
from enum import IntEnum # for enumerations (enum from C)
import logging # for logging functions
import random # for stochastic chosing of programs
import time # for time.time()

##########################################################################
# auxiliary definitions
class OperatorType(IntEnum):

    """Enumeration of operator types, ordered by precedence in calculus"""

    left_brace   = 1
    # right brace is never added to the postfix stack
    add          = 2
    subtract     = 3
    multiply     = 4
    divide       = 5
    power        = 6

# end class OperatorType

dictOperatorTypes = {
        'OPERATOR_ADD': OperatorType.add,
        'OPERATOR_SUBTRACT': OperatorType.subtract,
        'OPERATOR_MULTIPLY': OperatorType.multiply,
        'OPERATOR_DIVIDE': OperatorType.divide,
        'OPERATOR_POWER': OperatorType.power}

# tuple used to describe parsed data
Token = collections.namedtuple('Token', ['type', 'value', 'line', 'column'])

##########################################################################
# class definitions

class NumericalPsystem():

    """Numerical P systems class"""

    def __init__(self):
        self.H = [] # array of strings (names of membranes)
        self.membranes = {} # map (dictioanry) between String membrane_name: Membrane object
        self.structure = None # MembraneStructure object (list of structural elements) [1 [2 ]2 ]1
        self.variables = [] # list of Pobjects that appear throughtout the P system

    def runSimulationStep(self):
        """Runs 1 simulation step consisting of executing one program (production & dispersion functions) for all membranes that have programs
        If a membrane has more than one program, one is chosen randomly for execution"""

        # production phase for all membranes
        for membraneName, membrane in self.membranes.items():
            if (len(membrane.programs) < 1):
                continue
            logging.debug("Production for membrane %s" % membraneName)

            membrane.chosenProgramNr = 0 if len(membrane.programs) == 1 else random.randint(0, len(membrane.programs) - 1)
            try:
                # produce a new value
                membrane.newValue = membrane.programs[membrane.chosenProgramNr].prodFunction.evaluate()
            except RuntimeError:
                logging.error("Error encountered during production function of membrane %s, program %d" % (membraneName, membrane.chosenProgramNr))
                # re-raise the exception to stop the simulator
                raise

        ## reset variable phase
        logging.debug("Resetting all variables to 0")
        for variable in self.variables:
            variable.value = 0

        # distribution phase for all membranes
        for membraneName, membrane in self.membranes.items():
            if (len(membrane.programs) < 1):
                continue
            logging.debug("Distribution for membrane %s of unitary value %.02f" % (
                membraneName,
                membrane.newValue / membrane.programs[membrane.chosenProgramNr].distribFunction.proportionTotal))
            # distribute the previously produced value
            membrane.programs[membrane.chosenProgramNr].distribFunction.distribute(membrane.newValue)
        logging.info("Simulation step finished succesfully")
    # end runSimulationStep()

    def simulate(self, stepByStepConfirm = False, printEachSystemState = True, maxSteps = -1, maxTime = -1):
        """Simulates the numericP system until one of the imposed limits is reached

        :stepByStepConfirm: True / False - whether or not to wait for confirmation before starting the next simulation step
        :printEachSystemState: True / False - whether or not to print the P system state after the execution ofeach simulation step
        :maxSteps: The maximmum number of simulation steps to run
        :maxTime: The maximum time span that the entire simulation can last"""

        currentStep = 0;
         # time.time() == time in seconds since the Epoch
        startTime = currentTime = time.time();
        finalTime = currentTime + maxTime

        while (True):
            logging.info("Starting simulation step %d", currentStep)

            self.runSimulationStep()
            currentTime = time.time()

            if (printEachSystemState):
                self.print_system_components()

            if (stepByStepConfirm):
                input("Press ENTER to continue")

            # if there is a maximum time limit set and it was exceded
            if ((currentTime >= finalTime) and (maxTime > 0)):
                logging.warning("Maximum time limit exceeded; Simulation stopped")
                break # stop the simulation

            # if there is a maximum step limit set and it was exceded
            if ((currentStep >= maxSteps) and (maxSteps > 0)):
                logging.warning("Maximum number of simulation steps exceeded; Simulation stopped")
                break # stop the simulation

            currentStep += 1
        #end while loop

        logging.info("Simulation finished succesfully after %d steps and %f seconds; End state below:" % (currentStep, currentTime - startTime))
        self.print_colony_components()

    # end simulate()

    def print(self, indentSpaces = 2, toString = False) :
        """Print a membrane with a given indentation level

        :indentSpaces: number of spaces used for indentation
        :toString: write to a string instead of stdout
        :returns: string print of the membrane if toString = True otherwise returns None """

        result = ""

        result += "var = {\n"
        for membraneName, membrane in self.membranes.items():
            result += " " * indentSpaces + "%s:\n %s" % (membraneName, membrane.print(indentSpaces + 2, toString=True))
        result += "}\n"

        if (toString):
            return result
        else:
            print(result)
    # end print()
# end class NumericalPsystem

class MembraneStructure(list):

    """P system membrane structure (list of structural elements)"""

    def __init__(self):
        list.__init__(self)
# end class MembraneStructure

class Membrane():

    """Membrane class, that can contain other membranes or be part of another membrane"""

    def __init__(self, parentMembrane = None):
        self.variables = [] # array of P objects
        self.programs = [] # list of Program objects
        self.chosenProgramNr = 0 # the program nr that was chosen for execution
        self.newValue = 0 # the value that was produced during the previous production phase
        self.enzymes = [] # array of P objects
        self.parent = parentMembrane # parent membrane (Membrane object)
        self.children = {} # map (dictioanry) between String membrane_name: Membrane object


    def print(self, indentSpaces = 2, toString = False) :
        """Print a membrane with a given indentation level

        :indentSpaces: number of spaces used for indentation
        :toString: write to a string instead of stdout
        :returns: string print of the membrane if toString = True otherwise returns None """

        result = " " * indentSpaces

        result += "var = {"
        for var in self.variables:
            result += "  %s = %s  " % (var.name, var.value)
        result += "}\n"

        if (toString):
            return result
        else:
            print(result)
    # end print()
# end class Membrane

class Program():

    """Program class"""

    def __init__(self):
        self.prodFunction = None # ProductionFunction object
        self.distribFunction = None # DistributionFunction object

    def print(self, indentSpaces = 2, toString = False) :
        """Print a program with a given indentation level

        :indentSpaces: number of spaces used for indentation
        :toString: write to a string instead of stdout
        :returns: string print of the rule if toString = True otherwise returns None """

        result = " " * indentSpaces

        # production section
        for prod in self.prodFunction:
            result += " " + prod.print(toString = True)

        result += " -> "

        # distribution section
        for i, distrib in enumerate(self.distribFunction):
            if (i == 0):
                result += distrib.print(toString = True)
            else:
                result += " + " + distrib.print(toString = True)

        if (toString):
            return result
        else:
            print(result)
    # end print()

    def execute(self):
        """Executes the production function and distributes the result to the specified variables according to the distribution function"""

        newValue = self.prodFunction.evaluate()
        logging.debug("Obtained new value %d after production function" % newValue)
        self.distribFunction.distribute(newValue)
    # end execute()
# end class Program

class ProductionFunction():

    """Production function class that stores expressions using the postfix (reversed polish) form"""

    def __init__(self):
        self.postfixStack = [] # stack of operands and operators (auxiliary for postfix form)
        self.items = [] # list of operands and operators written in postfix (reverse polish) form

    def evaluate(self):
        """Evaluates the postfix form of a production function and returns the computed value.
        During the evaluation, Pobject references are replaced with their value.
        :returns: the computed value of the production function, as a numeric value"""
{'fraction_non_alphanumeric': 0.10259809119830329, 'fraction_numerical': 0.00338016967126193, 'mean_word_length': 2.6029130850047757, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '990342', 'n_tokens_mistral': 3950, 'n_tokens_neox': 3650, 'n_words': 1413}