def estimate_SI_continuous(cls, underlying: Network, theta: float, outcome: dict, initials: set, immunes: set = set(),
                    tmax=1000.0):
        time = .0
        my_outcome = outcome.copy()
        susceptible = set(underlying.nodes_ids)
        loglikelyhood = .0
        infected = set()
        infected |= initials
        susceptible -= immunes | infected

        prev_time = .0

        for newinf_id, inf_time in my_outcome.items():
            dt = inf_time - prev_time
            prev_time = inf_time
            neighbors = underlying.get_in_neighbors_for_node(newinf_id)
            n_infected = len(neighbors & infected)
            loglikelyhood += math.log(n_infected * theta)
            for sus_id in susceptible:
                sus_neighbors = underlying.get_in_neighbors_for_node(sus_id)
                sus_n_infected = len(sus_neighbors & infected)
                if sus_n_infected:
                    loglikelyhood += sus_n_infected*theta*dt
            infected.add(newinf_id)
            if newinf_id in susceptible:
                susceptible.remove(newinf_id)
        return loglikelyhood
 def estimate_SI(cls, underlying: Network, theta: float, outcome: dict, initials: set, immunes: set = set(),
                 tmax=1000.0, dt=1):
     time = .0
     my_outcome = outcome.copy()
     susceptible = set(underlying.nodes_ids)
     probability = 1.
     infected = set()
     infected |= initials
     susceptible -= immunes | infected
     while time < tmax:
         time += dt
         new_infected = set()
         for node_id in susceptible:
             neighbors = underlying.get_in_neighbors_for_node(node_id)
             n_infected = len(neighbors & infected)
             if n_infected > 0 or (node_id in my_outcome.keys() and my_outcome[node_id] < time):
                 p_not_infected = (1. - theta * dt) ** n_infected
                 if node_id in my_outcome.keys() and my_outcome[node_id] <= time:
                     probability *= 1.0 - p_not_infected
                     new_infected.add(node_id)
                 else:
                     probability *= p_not_infected
         for node_id in new_infected:
             my_outcome.pop(node_id)
         susceptible -= new_infected
         infected |= new_infected
         # print(str(probability) + ' ' + str(infected))
     return probability
    def simulate_SIR(cls, underlying: Network, theta: float, infected: set = None, immunes: set = None,
                              immune_p=.0, initial_p=.0, tmax=1000.0, dt=.1, recover_rate=.0, recover_time=.0):
        susceptible = set(underlying.nodes_ids)
        outcome_infected = {}
        outcome_recovered = {}
        time = .0
        rec_const = False
        if recover_time > 0:
            rec_const = True

        if infected is None:
            infected = set()
        if immunes is None:
            immunes = set()
        if len(infected) == 0 and initial_p > 0:
            for node_id in susceptible:
                if node_id not in immunes and random.uniform(0, 1.) < initial_p:
                    infected.add(node_id)
                    outcome_infected[node_id] = time
        if len(immunes) == 0 and immune_p > 0:
            for node_id in susceptible:
                if node_id not in infected and random.uniform(0, 1.) < immune_p:
                    immunes.add(node_id)
        susceptible -= immunes | infected
        while time < tmax:
            time += dt
            new_infected = set()
            new_recover = set()
            for node_id in susceptible:
                neighbors = underlying.get_in_neighbors_for_node(node_id)
                n_infected = len(neighbors & infected)
                if n_infected > 0:
                    if random.uniform(0, 1.) < (1. - (1. - theta*dt) ** n_infected):
                        new_infected.add(node_id)
                        outcome_infected[node_id] = time
            for node_id in infected:
                inf_time = outcome_infected[node_id]
                if rec_const:
                    if time - inf_time >= recover_time:
                        new_recover.add(node_id)
                        outcome_recovered[node_id] = (inf_time, time)
                        outcome_infected.pop(node_id)
                else:
                    if random.uniform(0, 1.) < recover_rate:
                        new_recover.add(node_id)
                        outcome_recovered[node_id] = (inf_time, time)
                        outcome_infected.pop(node_id)
            infected |= new_infected
            susceptible -= new_infected
            infected -= new_recover
        for node_id, inf_time in outcome_infected:
            outcome_recovered[node_id] = (inf_time, -1)
        return outcome_recovered
    def simulate_SI_halflife(cls, underlying: Network, theta: float, halflife: float, infected: set = None,
                            immunes: set = None, immune_p=.0, initial_p=.0, tmax=1000.0, dt=1):
        susceptible = set(underlying.nodes_ids)
        outcome_infected = {}
        time = .0
        probability = 1.
        my_infected = infected.copy()

        if my_infected is None:
            my_infected = set()
        if immunes is None:
            immunes = set()
        if len(my_infected) == 0 and initial_p > 0:
            for node_id in susceptible:
                if node_id not in immunes and random.uniform(0, 1.) < initial_p:
                    my_infected.add(node_id)
                    outcome_infected[node_id] = time
        if len(immunes) == 0 and immune_p > 0:
            for node_id in susceptible:
                if node_id not in my_infected and random.uniform(0, 1.) < immune_p:
                    immunes.add(node_id)
        susceptible -= immunes | my_infected
        while time < tmax:
            time += dt
            new_infected = set()
            for node_id in susceptible:
                neighbors = underlying.get_in_neighbors_for_node(node_id)
                infected_nbrs = neighbors & my_infected
                if len(infected_nbrs) > 0:
                    p_not_infected = 1.
                    for neighbor_id in infected_nbrs:
                        time_gone = outcome_infected.get(neighbor_id)
                        if time_gone:
                            time_gone = time - time_gone
                        else:  # initialy infected
                            time_gone = time
                        theta_r = theta
                        t = time_gone - halflife
                        while t > 0:
                            theta_r /= 2
                            t -= halflife
                        p_not_infected *= 1. - theta_r * dt
                    if random.uniform(0, 1.) < (1. - p_not_infected):
                        new_infected.add(node_id)
                        outcome_infected[node_id] = time
                        probability *= 1. - p_not_infected
                    else:
                        probability *= p_not_infected
            my_infected |= new_infected
            susceptible -= new_infected
            #print(str(probability) + ' ' + str(my_infected) + ' ' + str(time))
        return {'outcome': outcome_infected, 'p': probability}
    def simulate_SI_confirm(cls, underlying: Network, theta: float, confirm: float, infected: set = None,
                          immunes: set = None, immune_p=.0, initial_p=.0, tmax=1000.0, dt=1):
        susceptible = set(underlying.nodes_ids)
        outcome_infected = {}
        time = .0
        probability = 1.
        my_infected = infected.copy()

        if my_infected is None:
            my_infected = set()
        if immunes is None:
            immunes = set()
        if len(my_infected) == 0 and initial_p > 0:
            for node_id in susceptible:
                if node_id not in immunes and random.uniform(0, 1.) < initial_p:
                    my_infected.add(node_id)
                    outcome_infected[node_id] = time
        if len(immunes) == 0 and immune_p > 0:
            for node_id in susceptible:
                if node_id not in my_infected and random.uniform(0, 1.) < immune_p:
                    immunes.add(node_id)
        susceptible -= immunes | my_infected
        while time < tmax:
            time += dt
            new_infected = set()
            for node_id in susceptible:
                neighbors = underlying.get_in_neighbors_for_node(node_id)
                n_infected = len(neighbors & my_infected)
                if n_infected > 0:
                    if confirm >= .0:
                        if n_infected / len(neighbors) >= confirm:
                            p_not_infected = (1. - theta * dt) ** n_infected
                        else:
                            p_not_infected = (1. - theta / 10. * dt) ** n_infected
                    elif confirm < .0:
                        if n_infected / len(neighbors) < -confirm:
                            p_not_infected = (1. - theta * dt) ** n_infected
                        else:
                            p_not_infected = (1. - theta / 10. * dt) ** n_infected
                    # p_not_infected = (1. - theta * dt) ** (n_infected ** (confirm+1))
                    if random.uniform(0, 1.) < (1. - p_not_infected):
                        new_infected.add(node_id)
                        outcome_infected[node_id] = time
                        probability *= 1. - p_not_infected
                    else:
                        probability *= p_not_infected
            my_infected |= new_infected
            susceptible -= new_infected
            # print(str(probability) + ' ' + str(infected))
        return {'outcome': outcome_infected, 'p': probability}
    def simulate_ICM(cls, underlying: Network, theta: float, relic: float = .0,
                     infected: set = None, immunes: set = None,
                     immune_p=.0, initial_p=.0):
        susceptible = set(underlying.nodes_ids)
        outcome_infected = {}
        time = .0
        probability = 1.
        my_infected = infected.copy()

        if my_infected is None:
            my_infected = set()
        if immunes is None:
            immunes = set()
        if len(my_infected) == 0 and initial_p > 0:
            for node_id in susceptible:
                if node_id not in immunes and random.uniform(0, 1.) < initial_p:
                    my_infected.add(node_id)
                    outcome_infected[node_id] = time
        if len(immunes) == 0 and immune_p > 0:
            for node_id in susceptible:
                if node_id not in my_infected and random.uniform(0, 1.) < immune_p:
                    immunes.add(node_id)
        susceptible -= immunes | my_infected

        fresh_infected = my_infected.copy()

        while len(fresh_infected):
            new_infected = set()
            time = time + 1.
            for node_id in susceptible:
                neighbors = underlying.get_in_neighbors_for_node(node_id)
                n_infected = len(neighbors & fresh_infected)
                if n_infected > 0:
                    p_not_infected = (1. - theta) ** n_infected * (1. - relic) ** len(fresh_infected)
                    if random.uniform(0, 1.) < (1. - p_not_infected):
                        new_infected.add(node_id)
                        outcome_infected[node_id] = time
                        probability *= 1. - p_not_infected
                    else:
                        probability *= p_not_infected
            my_infected |= new_infected
            fresh_infected = new_infected
            susceptible -= new_infected
            # print(str(probability) + ' ' + str(infected))
        return {'outcome': outcome_infected, 'p': probability}
 def estimate_SI_halflife(cls, underlying: Network, theta: float, halflife: float, outcome: dict, initials: set,
                         immunes: set = set(), tmax=1000.0, dt=1):
     time = .0
     my_outcome = outcome.copy()
     susceptible = set(underlying.nodes_ids)
     probability = 1.
     infected = set()
     infected |= initials
     susceptible -= immunes | infected
     while time < tmax:
         time += dt
         new_infected = set()
         for node_id in susceptible:
             neighbors = underlying.get_in_neighbors_for_node(node_id)
             infected_nbrs = neighbors & infected
             if len(infected_nbrs) > 0 or (node_id in my_outcome.keys() and my_outcome[node_id] == time):
                 p_not_infected = 1.
                 for neighbor_id in infected_nbrs:
                     time_gone = my_outcome.get(neighbor_id)
                     if time_gone:
                         time_gone = time - time_gone
                     else:  # initialy infected
                         time_gone = time
                     theta_r = theta
                     t = time_gone - halflife
                     while t > 0:
                         theta_r /= 2
                         t -= halflife
                     p_not_infected *= 1. - theta_r * dt
                 if node_id in my_outcome.keys() and my_outcome[node_id] <= time:
                     probability *= 1.0 - p_not_infected
                     new_infected.add(node_id)
                 else:
                     probability *= p_not_infected
         #for node_id in new_infected:
         #    my_outcome.pop(node_id)
         susceptible -= new_infected
         infected |= new_infected
         #print(str(probability) + ' ' + str(infected) + ' ' + str(time))
     return probability
    def estimate_SI_decay_confirm_relicexp_hetero(cls, underlying: Network, theta: float,
                                                  decay: float, confirm: float, relic: float, outcome: dict,
                                                  thetas: dict = None, initials: set = None, immunes: set = set(),
                                                  immune_p=.0, initial_p=.0, tmax=1000.0, dt=1, echo=False):
        time = .0
        my_outcome = outcome.copy()
        susceptible = set(underlying.nodes_ids)
        probability = 1.
        infected = set()
        infected |= initials
        susceptible -= immunes | infected
        i = 0
        dprob_not = 1.
        while time < tmax and len(susceptible) > 0:
            time += dt
            new_infected = set()
            dprob_inf = 1.
            totrel=0
            for node_id in susceptible:
                neighbors = underlying.get_in_neighbors_for_node(node_id)

                infected_nbrs = neighbors & infected
                p_not_infected = 1.
                relic_total = .0
                for infected_id in infected:
                    if outcome.get(infected_id):
                        relic_total += math.exp(-decay * (time - outcome[infected_id]))
                    else:
                        relic_total += math.exp(-decay * time)
                relic_total *= relic
                totrel = relic_total
                if len(infected_nbrs) > 0:
                    for neighbor_id in infected_nbrs:
                        time_gone = outcome.get(neighbor_id)
                        if time_gone:
                            time_gone = time - time_gone
                        else:  # initialy infected
                            time_gone = time
                        if thetas and thetas.get(neighbor_id):
                            p_not_infected *= (1. - thetas[neighbor_id] * dt * (math.exp(-time_gone * decay)))
                        else:
                            p_not_infected *= (1. - theta * dt * (math.exp(-time_gone * decay)))
                p_not_infected *= (1. - relic_total * dt)
                if node_id in my_outcome.keys() and my_outcome[node_id] <= time:
                    probability *= 1.0 - p_not_infected
                    dprob_inf *= 1.0 - p_not_infected
                    new_infected.add(node_id)
                else:
                    probability *= p_not_infected
                    dprob_not *= p_not_infected
            for node_id in new_infected:
                my_outcome.pop(node_id)
            susceptible -= new_infected
            infected |= new_infected
            # print(str(probability) + ' ' + str(infected))
            if echo and len(new_infected):
                #print(str(list(new_infected)[0]) + ' ' + str(time) + ' ' + str(math.log(probability/(dt**(len(outcome)-1)))))
                #print(str(list(new_infected)[0]) + ' ' + str(time) + ' ' + str(math.log(dprob_inf / dt)))
                print(str(list(new_infected)[0]) + ' ' + str(time) + ' ' + str(math.log(dprob_not)) + ' ' + str(math.log(dprob_inf / dt)) + ' ' + str(math.log(probability/(dt**(len(infected-initials))))))
                print('nr'+str(len(susceptible|new_infected)) + ' ' + str(totrel))
                dprob_not = 1.
        #print('tail ' + str(time) + ' ' + str(math.log(dprob_not)) + ' ' + str(math.log(probability/(dt**(len(infected-initials))))))
        #print(time)
        return probability
    def simulate_SI_decay_confirm_relicexp_hetero(cls, underlying: Network, theta: float,
                                                  decay: float, confirm: float, relic: float, thetas: dict = None,
                                                  infected: set = None, immunes: set = None, immune_p=.0,
                                                  initial_p=.0, tmax=1000.0, dt=1, echo=False):
        susceptible = set(underlying.nodes_ids)
        outcome_infected = {}
        time = .0
        probability = 1.
        my_infected = infected.copy()

        if my_infected is None:
            my_infected = set()
        if immunes is None:
            immunes = set()
        if len(my_infected) == 0 and initial_p > 0:
            for node_id in susceptible:
                if node_id not in immunes and random.uniform(0, 1.) < initial_p:
                    my_infected.add(node_id)
                    outcome_infected[node_id] = time
        if len(immunes) == 0 and immune_p > 0:
            for node_id in susceptible:
                if node_id not in my_infected and random.uniform(0, 1.) < immune_p:
                    immunes.add(node_id)
        susceptible -= immunes | my_infected
        while time < tmax:
            time += dt
            new_infected = set()
            for node_id in susceptible:
                neighbors = underlying.get_in_neighbors_for_node(node_id)
                infected_nbrs = neighbors & my_infected
                p_not_infected = 1.
                relic_total = .0
                for infected_id in my_infected:
                    if outcome_infected.get(infected_id):
                        relic_total += math.exp(-decay * (time - outcome_infected[infected_id]))
                    else:
                        relic_total += math.exp(-decay * time)
                relic_total *= relic
                if len(infected_nbrs) > 0:
                    for neighbor_id in infected_nbrs:
                        time_gone = outcome_infected.get(neighbor_id)
                        if time_gone:
                            time_gone = time - time_gone
                        else:  # initialy infected
                            time_gone = time
                        if thetas and thetas.get(neighbor_id):
                            p_not_infected *= (1. - thetas[neighbor_id] * dt * (math.exp(-time_gone * decay)))
                        else:
                            p_not_infected *= (1. - theta * dt * (math.exp(-time_gone * decay)))
                p_not_infected *= (1. - relic_total * dt)
                if random.uniform(0, 1.) < (1. - p_not_infected):
                    new_infected.add(node_id)
                    outcome_infected[node_id] = time
                    probability *= 1. - p_not_infected
                else:
                    probability *= p_not_infected
            my_infected |= new_infected
            susceptible -= new_infected
            if echo:
                print(str(time) + ' ' + str(probability) + ' ' + str(my_infected))
        return {'outcome': outcome_infected, 'p': probability}
    def estimate_SI_relic_decay_confirm_continuous(cls, underlying: Network, theta: float, relic: float, decay: float,
                                                    confirm: .0, confirm_drop: 0.1,
                                                    outcome: dict, initials: set, immunes: set = set(),
                                                    tmax=1000.0, echo=False, leafs_degrees={}):
        time = .0
        my_outcome = []
        for key, value in outcome.items():
            my_outcome.append((key, value))
        my_outcome.sort(key=lambda x: x[1])

        susceptible = set(underlying.nodes_ids)
        loglikelyhood = .0
        infected = set()
        infected |= initials
        susceptible -= immunes | infected
        leafs_dict = {}
        n_no_inf_neighbors = 0
        virulences = {}
        for inf_id in infected:
            virulences[inf_id] = [0, .0, theta] #[start time, integrated, last point]

        nn = 0
        for node_id in underlying.nodes_ids:  # make separate storage for leafs never infected (>99% of all nodes)
            neighbors = underlying.get_in_neighbors_for_node(node_id)
            if len(neighbors) == 1 and node_id not in outcome.keys():
                for n in neighbors:
                    if n in (outcome.keys() | initials):
                        if n not in leafs_dict.keys():
                            leafs_dict[n] = 0
                        leafs_dict[n] += 1
                    else:
                        n_no_inf_neighbors += 1  # leafs having no infected neighbors (>80% of all nodes)
                    nn += 1
                    if node_id in susceptible:
                        susceptible.remove(node_id)
                    break
        prev_time = .0

        for newinf_id, inf_time in my_outcome:
            if inf_time == 0.0:
                continue
            # calculating integrated infection rates
            for inf_id, value in virulences.items():
                value[1] = theta/decay*(math.exp(decay*(value[0] - prev_time)) - math.exp(decay*(value[0] - inf_time)))
                value[2] = theta*math.exp(decay*(value[0] - inf_time))
            dt = inf_time - prev_time
            prev_time = inf_time

            # calculating multipliers for no infection time
            for sus_id in susceptible:
                sus_neighbors = underlying.get_in_neighbors_for_node(sus_id)
                sus_inf_neighbors = sus_neighbors & infected
                if len(sus_inf_neighbors) > 0:
                    inf_rate = 0
                    cd = 1.
                    n_neighbors = len(sus_neighbors)
                    if sus_id in leafs_degrees.keys():
                        n_neighbors = leafs_degrees[sus_id]
                        if n_neighbors == 0:
                            n_neighbors = 100
                    else:
                        n_neighbors = len(sus_neighbors)
                    if confirm > .0:
                        if len(sus_inf_neighbors)/n_neighbors < confirm:
                            cd = confirm_drop
                    elif confirm < .0:
                        if len(sus_inf_neighbors)/n_neighbors > 1. + confirm:
                            cd = confirm_drop
                    for inf_id in sus_inf_neighbors:  # calculate total infection rate
                        inf_rate += virulences[inf_id][1]
                    loglikelyhood -= cd * inf_rate + relic * dt
                else:
                    loglikelyhood -= relic * dt
            for nonleaf_id, n_leafs in leafs_dict.items():
                if nonleaf_id in infected:
                    #assume that all leafs have enough uninfected friends to not beat the treshold
                    cd = 1.
                    if confirm != 0:
                        cd = confirm_drop
                    loglikelyhood -= n_leafs * (cd * virulences[nonleaf_id][1] + relic * dt)
                else:
                    loglikelyhood -= n_leafs * relic * dt
            loglikelyhood -= n_no_inf_neighbors * relic * dt

            # calculating multiplier for infection
            neighbors = underlying.get_in_neighbors_for_node(newinf_id)
            infected_nbrs = neighbors & infected
            inf_rate = 0
            cd = 1.
            if confirm > .0:
                if len(infected_nbrs) / len(neighbors) < confirm:
                    cd = confirm_drop
            elif confirm < .0:
                if len(infected_nbrs) / len(neighbors) > -confirm:
                    cd = confirm_drop
            for inf_id in infected_nbrs:  # calculate total infection rate
                inf_rate += virulences[inf_id][2]
            loglikelyhood += math.log(cd * inf_rate + relic)

            infected.add(newinf_id)
            virulences[newinf_id] = [inf_time, .0, theta]
            if newinf_id in susceptible:
                susceptible.remove(newinf_id)
            if echo:
                print(str(time) + ' ' + str(loglikelyhood))
        return loglikelyhood
    def estimate_SI_relic_halflife_continuous(cls, underlying: Network, theta: float, relic: float, halflife: float,
                                              outcome: dict, initials: set, immunes: set = set(),
                                              tmax=1000.0, echo=False):
        time = .0
        my_outcome = []
        for key, value in outcome.items():
            my_outcome.append((key, value))
        my_outcome.sort(key=lambda x: x[1])

        susceptible = set(underlying.nodes_ids)
        loglikelyhood = .0
        infected = set()
        infected |= initials
        susceptible -= immunes | infected
        leafs_dict = {}
        n_no_inf_neighbors = 0
        virulences = {}
        for inf_id in infected:
            virulences[inf_id] = [halflife, theta, 0.]

        nn = 0
        for node_id in underlying.nodes_ids:  # make separate storage for leafs never infected (>99% of all nodes)
            neighbors = underlying.get_in_neighbors_for_node(node_id)
            if len(neighbors) == 1 and node_id not in outcome.keys():
                for n in neighbors:
                    if n in (outcome.keys() | initials):
                        if n not in leafs_dict.keys():
                            leafs_dict[n] = 0
                        leafs_dict[n] += 1
                    else:
                        n_no_inf_neighbors += 1 #leafs having no infected neighbors (>80% of all nodes)
                    nn += 1
                    if node_id in susceptible:
                        susceptible.remove(node_id)
                    break
        prev_time = .0

        for newinf_id, inf_time in my_outcome:
            if inf_time == 0.0:
                continue
            dt = inf_time - prev_time
            prev_time = inf_time
            #calculating integrated infection rates
            for inf_id, value in virulences.items():
                if dt > value[0]: #dt * theta
                    value[2] = value[0] * value[1]
                else:
                    value[2] = dt * value[1]
                value[0] -= dt
                while value[0] < .0:
                    value[1] /= 2  # halflifing
                    value[0] += halflife
                    if value[0] < .0:
                        value[2] += value[1] * halflife
                    else:
                        value[2] += value[1] * (halflife - value[0])

            #calculating multipliers for no infection time
            for sus_id in susceptible:
                sus_neighbors = underlying.get_in_neighbors_for_node(sus_id)
                sus_inf_neighbors = sus_neighbors & infected
                if len(sus_inf_neighbors) > 0:
                    inf_rate = 0
                    for inf_id in sus_inf_neighbors:  # calculate total infection rate
                        inf_rate += virulences[inf_id][2]
                    loglikelyhood -= inf_rate + relic * dt
                else:
                    loglikelyhood -= relic * dt
            for nonleaf_id, n_leafs in leafs_dict.items():
                if nonleaf_id in infected:
                    loglikelyhood -= n_leafs * (virulences[nonleaf_id][2] + relic * dt)
                else:
                    loglikelyhood -= n_leafs * relic * dt
            loglikelyhood -= n_no_inf_neighbors * relic * dt

            #calculating multiplier for infection
            neighbors = underlying.get_in_neighbors_for_node(newinf_id)
            inf_rate = 0
            for inf_id in neighbors & infected: #calculate total infection rate
                inf_rate += virulences[inf_id][1]
            loglikelyhood += math.log(inf_rate + relic)

            infected.add(newinf_id)
            virulences[newinf_id] = [halflife, theta, 0.]
            if newinf_id in susceptible:
                susceptible.remove(newinf_id)
            if echo:
                print(str(time) + ' ' + str(loglikelyhood))
        return loglikelyhood
    def estimate_SI_relic_continuous(cls, underlying: Network, theta: float, relic: float, outcome: dict,
                                     initials: set, immunes: set = set(), tmax=1000.0, echo = False):
        time = .0
        my_outcome = []
        for key, value in outcome.items():
            my_outcome.append((key, value))
        my_outcome.sort(key=lambda x: x[1])

        susceptible = set(underlying.nodes_ids)
        loglikelyhood = .0
        infected = set()
        infected |= initials
        susceptible -= immunes | infected
        leafs_dict = {}
        n_no_inf_neighbors = 0

        nn = 0
        for node_id in underlying.nodes_ids: #make separate storage for leafs never infected (>99% of al nodes)
            neighbors = underlying.get_in_neighbors_for_node(node_id)
            if len(neighbors) == 1 and node_id not in outcome.keys():
                for n in neighbors:
                    if n in (outcome.keys() | initials):
                        if n not in leafs_dict.keys():
                            leafs_dict[n] = 0
                        leafs_dict[n] += 1
                    else:
                        n_no_inf_neighbors += 1  #leafs having no infected neighbors (>80% of all nodes)
                    nn += 1
                    if node_id in susceptible:
                        susceptible.remove(node_id)
                    break
        prev_time = .0

        for newinf_id, inf_time in my_outcome:
           # print('PyNInfTot: ' + str(len(infected)))
            if inf_time == 0.0:
                continue
            dt = inf_time - prev_time
            prev_time = inf_time
            neighbors = underlying.get_in_neighbors_for_node(newinf_id)
            n_infected = len(neighbors & infected)
            loglikelyhood += math.log(n_infected * theta + len(infected) * relic)
            for sus_id in susceptible:
                sus_neighbors = underlying.get_in_neighbors_for_node(sus_id)
                sus_n_infected = len(sus_neighbors & infected)
                if sus_n_infected > 0:
                    loglikelyhood -= (sus_n_infected * theta + len(infected) * relic) * dt
                else:
                    loglikelyhood -= len(infected) * relic * dt
            for nonleaf_id, n_leafs in leafs_dict.items():
                if nonleaf_id in infected:
                    loglikelyhood -= n_leafs * (theta + len(infected) * relic) * dt
                else:
                    loglikelyhood -= n_leafs * len(infected) * relic * dt
            loglikelyhood -= n_no_inf_neighbors * len(infected) * relic * dt

            infected.add(newinf_id)
            if newinf_id in susceptible:
                susceptible.remove(newinf_id)
            if echo:
                print(str(prev_time) + ' ' + str(loglikelyhood))
        return loglikelyhood