<commit_before>import networkx
import sympy
import re
import copy
import numpy
import sympy.parsing.sympy_parser
from pysb.integrate import odesolve
from collections    import Mapping

# Helper class to use evalf with a ndarray
class FilterNdarray(Mapping):
    def __init__(self, source):
        self.source = source
        self.t = 0

    def __getitem__(self, key):
        # WARNING: This is a monkey patch when evalf sends a number to getitem instead of a symbol
        try:
            return self.source[key.name][self.t]
        except:
            return key.name

    def __len__(self): return len(self.source)

    def __iter__(self):
        for key in self.source:
            yield key

    def set_time(self, t):
        self.t = t
        return self

class Tropical:
    def __init__(self, model):
        self.model            = model
        self.t                = None # timerange used
        self.y                = None # odes solution, numpy array
        self.slaves           = None
        self.graph            = None
        self.cycles           = []
        self.conservation     = None
        self.conserve_var     = None
        self.full_names       = {}

    def __repr__(self):
        return "<%s '%s' (slaves: %s, cycles: %d) at 0x%x>" % \
            (self.__class__.__name__, self.model.name,
             self.slaves.__repr__(),
             len(self.cycles),
             id(self))

    def tropicalize(self, t, ignore=1, epsilon=0.1, rho=1, verbose=False):
        if verbose: print "Solving Simulation"
        self.y = odesolve(self.model, t)
    
        # Only concrete species are considered, and the names must be made to match
        names           = [n for n in filter(lambda n: n.startswith('__'), self.y.dtype.names)]
        self.y          = self.y[names]
        names           = [n.replace('__','') for n in names]
        self.y.dtype    = [(n,'<f8') for n in names]
    
        if verbose: print "Computing Imposed Distances"
        # Not saving imposed distance, since it's shortcutting
        dist = self.imposed_distance(self.y[ignore:], verbose, epsilon)
        self.find_slaves(dist, epsilon)
        if verbose: print "Constructing Graph"
        self.construct_graph()
        if verbose: print "Computing Cycles"
        self.cycles = [c[0:(len(c)-1)]  for c in networkx.simple_cycles(self.graph) ]
        if verbose: print "Computing Conservation laws"
        (self.conservation, self.conserve_var) = self.mass_conserved()
        if verbose: print "Pruning Equations"
        self.pruned = self.pruned_equations(self.y[ignore:], rho)

# FIXME: THIS STEP IS BROKEN DUE TO THE ADDITION OF CYCLES
        #if verbose: print "Solving pruned equations"
        #self.sol_pruned = self.solve_pruned()
    
        return self

    # Compute imposed distances of a model
    def imposed_distance(self, y, verbose=False, epsilon=None):
        distances = []

        symy = FilterNdarray(y) # Create a filtered view of the ode solution, suitable for sympy

        # Loop through all equations (i is equation number)
        for i, eq in enumerate(self.model.odes):
            eq        = eq.subs('s%d' % i, 's%dstar' % i)
            sol       = sympy.solve(eq, sympy.Symbol('s%dstar' % i)) # Find equation of imposed trace
            max       = None # maximum for a single solution
            min       = None # Minimum between all possible solution

            # The minimum of the maximum of all possible solutions
            # Note: It should prefer real solutions over complex, but this isn't coded to do that 
            #       and the current test case is all complex
            for j in range(len(sol)):  # j is solution j for equation i (mostly likely never greater than 2)
                for p in self.model.parameters: sol[j] = sol[j].subs(p.name, p.value) # Substitute parameters
                trace = y['s%d' % i]
                for t in range(y.size):
		    current = abs(sol[j].evalf(subs=symy.set_time(t)) - trace[t])
                    if max == None or current > max:
                        max = current
                    if epsilon != None and current > epsilon:
                        break
                if j==0:
                    min = max
                else:
                    if max < min: min = max
            distances.append(min)
            if verbose: print "  * Equation",i," distance =",max
        return distances

    def find_slaves(self, distances, epsilon):<commit_msg>Fixed a small indentiation issue<commit_after>import networkx
import sympy
import re
import copy
import numpy
import sympy.parsing.sympy_parser
from pysb.integrate import odesolve
from collections    import Mapping

# Helper class to use evalf with a ndarray
class FilterNdarray(Mapping):
    def __init__(self, source):
        self.source = source
        self.t = 0

    def __getitem__(self, key):
        # WARNING: This is a monkey patch when evalf sends a number to getitem instead of a symbol
        try:
            return self.source[key.name][self.t]
        except:
            return key.name

    def __len__(self): return len(self.source)

    def __iter__(self):
        for key in self.source:
            yield key

    def set_time(self, t):
        self.t = t
        return self

class Tropical:
    def __init__(self, model):
        self.model            = model
        self.t                = None # timerange used
        self.y                = None # odes solution, numpy array
        self.slaves           = None
        self.graph            = None
        self.cycles           = []
        self.conservation     = None
        self.conserve_var     = None
        self.full_names       = {}

    def __repr__(self):
        return "<%s '%s' (slaves: %s, cycles: %d) at 0x%x>" % \
            (self.__class__.__name__, self.model.name,
             self.slaves.__repr__(),
             len(self.cycles),
             id(self))

    def tropicalize(self, t, ignore=1, epsilon=0.1, rho=1, verbose=False):
        if verbose: print "Solving Simulation"
        self.y = odesolve(self.model, t)
    
        # Only concrete species are considered, and the names must be made to match
        names           = [n for n in filter(lambda n: n.startswith('__'), self.y.dtype.names)]
        self.y          = self.y[names]
        names           = [n.replace('__','') for n in names]
        self.y.dtype    = [(n,'<f8') for n in names]
    
        if verbose: print "Computing Imposed Distances"
        # Not saving imposed distance, since it's shortcutting
        dist = self.imposed_distance(self.y[ignore:], verbose, epsilon)
        self.find_slaves(dist, epsilon)
        if verbose: print "Constructing Graph"
        self.construct_graph()
        if verbose: print "Computing Cycles"
        self.cycles = [c[0:(len(c)-1)]  for c in networkx.simple_cycles(self.graph) ]
        if verbose: print "Computing Conservation laws"
        (self.conservation, self.conserve_var) = self.mass_conserved()
        if verbose: print "Pruning Equations"
        self.pruned = self.pruned_equations(self.y[ignore:], rho)

# FIXME: THIS STEP IS BROKEN DUE TO THE ADDITION OF CYCLES
        #if verbose: print "Solving pruned equations"
        #self.sol_pruned = self.solve_pruned()
    
        return self

    # Compute imposed distances of a model
    def imposed_distance(self, y, verbose=False, epsilon=None):
        distances = []

        symy = FilterNdarray(y) # Create a filtered view of the ode solution, suitable for sympy

        # Loop through all equations (i is equation number)
        for i, eq in enumerate(self.model.odes):
            eq        = eq.subs('s%d' % i, 's%dstar' % i)
            sol       = sympy.solve(eq, sympy.Symbol('s%dstar' % i)) # Find equation of imposed trace
            max       = None # maximum for a single solution
            min       = None # Minimum between all possible solution

            # The minimum of the maximum of all possible solutions
            # Note: It should prefer real solutions over complex, but this isn't coded to do that 
            #       and the current test case is all complex
            for j in range(len(sol)):  # j is solution j for equation i (mostly likely never greater than 2)
                for p in self.model.parameters: sol[j] = sol[j].subs(p.name, p.value) # Substitute parameters
                trace = y['s%d' % i]
                for t in range(y.size):
                    current = abs(sol[j].evalf(subs=symy.set_time(t)) - trace[t])
                    if max == None or current > max:
                        max = current
                    if epsilon != None and current > epsilon:
                        break
                if j==0:
                    min = max
                else:
                    if max < min: min = max
            distances.append(min)
            if verbose: print "  * Equation",i," distance =",max
        return distances

    def find_slaves(self, distances, epsilon):
{'fraction_non_alphanumeric': 0.09166206515737162, 'fraction_numerical': 0.0024295969077857537, 'mean_word_length': 1.8612954186413901, 'pattern_counts': {'":': 0, '<': 9, '<?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': '741225', 'n_tokens_mistral': 2675, 'n_tokens_neox': 2539, 'n_words': 847}