Skip to content

Take contributions

This function fills up the combination_space with the game you define (objective function). There is an important point to keep in mind, Shapley values are the added contributions of elements while in MSA we calculate them by perturbation so although it's intuitive to think the combination in combination space is the element that will be lesioned, it is not the case, it will be everything else but the coalition, i.e., the target coalition are the only intact elements. This function takes care of this by passing the complement of each coalition to the game while assigning the results to the target coalition, just keep the logic in mind.

A second point is that this function returns a filled combination_space, it is not filling it in-place for the sake of purity.


Note on returns: Contributions and lesion effects are virtually the same thing it's just about how you're looking at them. For example, you might want to use lesion effects by conditioning elements' length and see the effect of single lesions, dual, triple,... so, for contributions we have a value contributed by the intact coalition, the same result can be compared to the intact system to see how big was the impact of lesioning the complements. "Same same, but different, but still same!" - James Franco

Parameters:

Name Type Description Default
elements list

List of the players. Obviously, should be the same passed to make_permutation.

required
complement_space OrderedSet

The actual targets for lesioning. Shapley values are the added contributions of elements while in MSA we calculate them by perturbation so although it's intuitive to think the combination in combination space is the element that will be lesioned, it is not the case, it will be everything else but the coalition, i.e., the target coalition are the only intact elements.

required
combination_space OrderedSet

The template, will be copied, filled by the objective_function, and returned.

required
objective_function Callable

The game, it should get the complement set and return one numeric value either int or float. This function is just calling it as: objective_function(complement, **objective_function_params) so design accordingly.

An example using networkx with some tips: (you sometimes need to specify what should happen during edge-cases like an all-lesioned network)

def local_efficiency(complements, graph): if len(complements) < 0: # the network is intact so: return nx.local_efficiency(graph)

elif len(complements) == len(graph):
    # the network is fully lesioned so:
    return 0.0

else:
    # lesion the system, calculate things
    lesioned = graph.copy()
    lesioned.remove_nodes_from(complements)
    return nx.local_efficiency(lesioned)
required
objective_function_params Optional[Dict]

Kwargs for the objective_function.

None

Returns:

Type Description
Dict

A dictionary of combinations:results

Source code in msapy/msa.py
@typechecked
def take_contributions(*,
                       elements: list,
                       complement_space: OrderedSet,
                       combination_space: OrderedSet,
                       objective_function: Callable,
                       objective_function_params: Optional[Dict] = None,
                       mbar: Optional[MasterBar] = None) -> Tuple[Dict, Dict]:
    """
    This function fills up the combination_space with the game you define (objective function). There is an important
    point to keep in mind, Shapley values are the added contributions of elements while in MSA we calculate them by
    perturbation so although it's intuitive to think the combination in combination space is the element that will be
    lesioned, it is not the case, it will be everything else but the coalition, i.e., the target coalition are the
    only intact elements. This function takes care of this by passing the complement of each coalition to the
    game while assigning the results to the target coalition, just keep the logic in mind.

    A second point is that this function returns a filled combination_space, it is not filling it in-place for the
    sake of purity.

    ---------------
    Note on returns:
        Contributions and lesion effects are virtually the same thing it's just about how you're looking at them.
        For example, you might want to use lesion effects by conditioning elements' length and see the effect of
        single lesions, dual, triple,... so, for contributions we have a value contributed by the intact coalition,
        the same result can be compared to the intact system to see how big was the impact of lesioning the complements.
        "Same same, but different, but still same!" - James Franco

    Args:
        elements (list):
            List of the players. Obviously, should be the same passed to make_permutation.

        complement_space (OrderedSet):
            The actual targets for lesioning. Shapley values are the added contributions of elements
            while in MSA we calculate them by perturbation so although it's intuitive to think the combination
            in combination space is the element that will be lesioned, it is not the case,
            it will be everything else but the coalition, i.e., the target coalition are the only intact elements.

        combination_space (OrderedSet):
            The template, will be copied, filled by the objective_function, and returned.

        objective_function (Callable):
            The game, it should get the complement set and return one numeric value either int or float.
            This function is just calling it as: objective_function(complement, **objective_function_params)
            so design accordingly.

            An example using networkx with some tips:
            (you sometimes need to specify what should happen during edge-cases like an all-lesioned network)

            def local_efficiency(complements, graph):
                if len(complements) < 0:
                    # the network is intact so:
                    return nx.local_efficiency(graph)

                elif len(complements) == len(graph):
                    # the network is fully lesioned so:
                    return 0.0

                else:
                    # lesion the system, calculate things
                    lesioned = graph.copy()
                    lesioned.remove_nodes_from(complements)
                    return nx.local_efficiency(lesioned)

        objective_function_params (Optional[Dict]):
            Kwargs for the objective_function.

    Returns:
        (Dict): A dictionary of combinations:results
    """

    elements = frozenset(elements)
    contributions = dict.fromkeys(combination_space)
    lesion_effects = dict.fromkeys(complement_space)
    objective_function_params = objective_function_params if objective_function_params else {}

    # ------------------------------#
    if len(complement_space.items[1]) == 0:
        warnings.warn("Are you sure you're not mistaking complement and combination spaces?"
                      "Length of the first element in complement space (really, complement_space[1]) is 0. "
                      "It should be equal to the number of elements.",
                      stacklevel=2)
    # ------------------------------#

    # run the objective function over all complement space
    for combination, complement in progress_bar(zip(combination_space, complement_space), parent=mbar, total=len(combination_space), leave=False):
        result = objective_function(complement, **objective_function_params)

        contributions[combination] = result
        lesion_effects[complement] = result
    return contributions, lesion_effects