def Func(arg_0=None,
                         arg_1=None, arg_2=None):
    """Delete computed aggregations."""
    arg_0 = (arg_0 or
                         list(current_stats.enabled_aggregations))
    for arg_3 in arg_0:
        arg_4 = current_stats.aggregations[arg_3]
        arg_5 = arg_4.aggregator_class(
            name=arg_4.name, **arg_4.aggregator_config)
        arg_5.delete(arg_1, arg_2)


def Func(arg_0):
        """Load events configuration."""
        # import iter_entry_points here so that it can be mocked in tests
        arg_1 = {}
        for arg_2 in iter_entry_points(
                group=arg_0.entry_point_group_events):
            for arg_3 in arg_2.load()():
                if arg_3['event_type'] not in arg_0.enabled_events:
                    continue
                elif arg_3['event_type'] in arg_1:
                    raise DuplicateEventError(
                        'Duplicate event {0} in entry point '
                        '{1}'.format(arg_3['event_type'], arg_2.name))
                # Update the default configuration with env/overlay config.
                arg_3.update(
                    arg_0.enabled_events[arg_3['event_type']] or {}
                )
                arg_1[arg_3['event_type']] = arg_3
        return arg_1


def Func(arg_0):
        """Load queries configuration."""
        arg_1 = {}
        for arg_2 in iter_entry_points(group=arg_0.entry_point_group_queries):
            for arg_3 in arg_2.load()():
                if arg_3['query_name'] not in arg_0.enabled_queries:
                    continue
                elif arg_3['query_name'] in arg_1:
                    raise DuplicateQueryError(
                        'Duplicate query {0} in entry point '
                        '{1}'.format(arg_3['query'], arg_2.name))
                # Update the default configuration with env/overlay config.
                arg_3.update(
                    arg_0.enabled_queries[arg_3['query_name']] or {}
                )
                arg_1[arg_3['query_name']] = arg_3
        return arg_1


def Func(arg_0, arg_1, arg_2=True, arg_3=True):
        """Comsume all pending events."""
        assert arg_1 in arg_0.events
        return current_queues.queues['stats-{}'.format(arg_1)].Func(
            arg_3=arg_3)


def Func(arg_0, arg_1, arg_2=arg_3):
        """ Send a message to this actor. Asynchronous fire-and-forget.

        :param message: The message to send.
        :type message: Any

        :param sender: The sender of the message. If provided it will be made
            available to the receiving actor via the :attr:`Actor.sender` attribute.
        :type sender: :class:`Actor`
        """
        if arg_2 is not arg_3 and not isinstance(arg_2, ActorRef):
            raise ValueError("Sender must be actor reference")

        arg_0._cell.send_message(arg_1, arg_2)


def Func(arg_0):
    """Get the anonymization salt based on the event timestamp's day."""
    arg_1 = 'stats:salt:{}'.format(arg_0.date().isoformat())
    arg_2 = current_cache.get(arg_1)
    if not arg_2:
        arg_3 = os.urandom(32)
        arg_2 = b64encode(arg_3).decode('utf-8')
        current_cache.set(arg_1, arg_2, timeout=60 * 60 * 24)
    return arg_2


def Func(arg_0):
    """Lookup country for IP address."""
    arg_1 = geolite2.reader()
    arg_2 = arg_1.get(arg_0) or {}
    return arg_2.get('country', {}).get('iso_code')


def Func():
    """User information.

    .. note::

       **Privacy note** A users IP address, user agent string, and user id
       (if logged in) is sent to a message queue, where it is stored for about
       5 minutes. The information is used to:

       - Detect robot visits from the user agent string.
       - Generate an anonymized visitor id (using a random salt per day).
       - Detect the users host contry based on the IP address.

       The information is then discarded.
    """
    return dict(
        ip_address=request.remote_addr,
        user_agent=request.user_agent.string,
        user_id=(
            current_user.get_id() if current_user.is_authenticated else None
        ),
        session_id=session.get('sid_s')
    )


def Func(arg_0, arg_1=None, arg_2=None,
                     arg_3=True):
    """Aggregate indexed events."""
    arg_1 = dateutil_parse(arg_1) if arg_1 else None
    arg_2 = dateutil_parse(arg_2) if arg_2 else None
    arg_4 = []
    for arg_5 in arg_0:
        arg_6 = current_stats.aggregations[arg_5]
        arg_7 = arg_6.aggregator_class(
            name=arg_6.name, **arg_6.aggregator_config)
        arg_4.append(arg_7.run(arg_1, arg_2, arg_3))
    return arg_4


def Func(arg_0, arg_1, arg_2, arg_3, arg_4, arg_5=None, arg_6="GET"):
        """
        Run the actual request
        """
        arg_7 = "{}://{}{}".format(arg_1, arg_2, arg_3)
        try:
            arg_8 = arg_0.http_request.request(arg_7, arg_6=arg_6, arg_5=arg_5, arg_4=dict(arg_4))
            arg_0._return_response(arg_8)
        except Exception as e:
            arg_5 = "Invalid response from backend: '{}' Server might be busy".format(e.message)
            logging.debug(arg_5)
            arg_0.send_error(httplib.SERVICE_UNAVAILABLE, arg_5)


def Func(arg_0):
    """Preprocess an event by anonymizing user information.

    The anonymization is done by removing fields that can uniquely identify a
    user, such as the user's ID, session ID, IP address and User Agent, and
    hashing them to produce a ``visitor_id`` and ``unique_session_id``. To
    further secure the method, a randomly generated 32-byte salt is used, that
    expires after 24 hours and is discarded. The salt values are stored in
    Redis (or whichever backend Invenio-Cache uses). The ``unique_session_id``
    is calculated in the same way as the ``visitor_id``, with the only
    difference that it also takes into account the hour of the event . All of
    these rules effectively mean that a user can have a unique ``visitor_id``
    for each day and unique ``unique_session_id`` for each hour of a day.

    This session ID generation process was designed according to the `Project
    COUNTER Code of Practice <https://www.projectcounter.org/code-of-
    practice-sections/general-information/>`_.

    In addition to that the country of the user is extracted from the IP
    address as a ISO 3166-1 alpha-2 two-letter country code (e.g. "CH" for
    Switzerland).
    """
    arg_1 = arg_0.pop('ip_address', None)
    if arg_1:
        arg_0.update({'country': get_geoip(arg_1)})

    arg_2 = arg_0.pop('user_id', '')
    arg_3 = arg_0.pop('session_id', '')
    arg_4 = arg_0.pop('user_agent', '')

    # A 'User Session' is defined as activity by a user in a period of
    # one hour. timeslice represents the hour of the day in which
    # the event has been generated and together with user info it determines
    # the 'User Session'
    arg_5 = arrow.get(arg_0.get('timestamp'))
    arg_6 = arg_5.strftime('%Y%m%d%H')
    arg_7 = get_anonymization_salt(arg_5)

    arg_8 = hashlib.sha224(arg_7.encode('utf-8'))
    # TODO: include random salt here, that changes once a day.
    # m.update(random_salt)
    if arg_2:
        arg_8.update(arg_2.encode('utf-8'))
    elif arg_3:
        arg_8.update(arg_3.encode('utf-8'))
    elif arg_1 and arg_4:
        arg_9 = '{}|{}|{}'.format(arg_1, arg_4, arg_6)
        arg_8.update(arg_9.encode('utf-8'))
    else:
        # TODO: add random data?
        pass

    arg_10 = hashlib.sha224(arg_7.encode('utf-8'))
    if arg_2:
        arg_11 = '{}|{}'.format(arg_2, arg_6)
        arg_10.update(arg_11.encode('utf-8'))
    elif arg_3:
        arg_11 = '{}|{}'.format(arg_3, arg_6)
        arg_10.update(arg_11.encode('utf-8'))
    elif arg_1 and arg_4:
        arg_11 = '{}|{}|{}'.format(arg_1, arg_4, arg_6)
        arg_10.update(arg_11.encode('utf-8'))

    arg_0.update(dict(
        arg_8=arg_8.hexdigest(),
        arg_10=arg_10.hexdigest()
    ))

    return arg_0


def Func(arg_0, arg_1):
    """Generate event id, optimized for ES."""
    return '{0}-{1}'.format(arg_0,
                            hashlib.sha1(
                                arg_1.get('unique_id').encode('utf-8') +
                                str(arg_1.get('visitor_id')).
                                encode('utf-8')).
                            hexdigest())


async def Func(arg_0) -> bytes:
        """
        Reads one line

        >>> # Keeps waiting for a linefeed incase there is none in the buffer
        >>> await test.Func()

        :returns: bytes forming a line
        """
        while True:
            arg_1 = arg_0._serial_instance.Func()
            if not arg_1:
                await asyncio.sleep(arg_0._asyncio_sleep_time)
            else:
                return arg_1


def Func():
    """Register sample events."""
    return [
        dict(
            event_type='file-download',
            templates='invenio_stats.contrib.file_download',
            processor_class=EventsIndexer,
            processor_config=dict(
                preprocessors=[
                    flag_robots,
                    anonymize_user,
                    build_file_unique_id
                ])),
        dict(
            event_type='record-view',
            templates='invenio_stats.contrib.record_view',
            processor_class=EventsIndexer,
            processor_config=dict(
                preprocessors=[
                    flag_robots,
                    anonymize_user,
                    build_record_unique_id
                ]))
    ]


def Func():
    """Register queries."""
    return [
        dict(
            query_name='bucket-file-download-histogram',
            query_class=ESDateHistogramQuery,
            query_config=dict(
                index='stats-file-download',
                doc_type='file-download-day-aggregation',
                copy_fields=dict(
                    bucket_id='bucket_id',
                    file_key='file_key',
                ),
                required_filters=dict(
                    bucket_id='bucket_id',
                    file_key='file_key',
                )
            )
        ),
        dict(
            query_name='bucket-file-download-total',
            query_class=ESTermsQuery,
            query_config=dict(
                index='stats-file-download',
                doc_type='file-download-day-aggregation',
                copy_fields=dict(
                    # bucket_id='bucket_id',
                ),
                required_filters=dict(
                    bucket_id='bucket_id',
                ),
                aggregated_fields=['file_key']
            )
        ),
    ]


def Func(arg_0, arg_1):
        """Extract date from string if necessary.

        :returns: the extracted date.
        """
        if isinstance(arg_1, six.string_types):
            try:
                arg_1 = dateutil.parser.parse(arg_1)
            except ValueError:
                raise ValueError(
                    'Invalid date format for statistic {}.'
                ).format(arg_0.query_name)
        if not isinstance(arg_1, datetime):
            raise TypeError(
                'Invalid date type for statistic {}.'
            ).format(arg_0.query_name)
        return arg_1


def Func(arg_0, arg_1=None, arg_2=None, **arg_3):
        """Run the query."""
        arg_1 = arg_0.extract_date(arg_1) if arg_1 else None
        arg_2 = arg_0.extract_date(arg_2) if arg_2 else None
        arg_0.validate_arguments(arg_1, arg_2, **arg_3)

        arg_4 = arg_0.build_query(arg_1, arg_2, **arg_3)
        arg_5 = arg_4.execute().to_dict()
        arg_6 = arg_0.process_query_result(arg_5, arg_1, arg_2)
        return arg_6


def Func(arg_0, arg_1, arg_2=None, **arg_3):
    """Build a file-download event."""
    arg_0.update(dict(
        # When:
        timestamp=datetime.datetime.utcnow().isoformat(),
        # What:
        bucket_id=str(arg_2.bucket_id),
        file_id=str(arg_2.file_id),
        file_key=arg_2.key,
        size=arg_2.file.size,
        referrer=request.referrer,
        # Who:
        **get_user()
    ))
    return arg_0


def Func(arg_0, arg_1):
        """Verifies and Funcs message.

        :param message: Message instance.
        :param envelope_from: Email address to be used in MAIL FROM command.
        """
        assert arg_1.Func_to, "No recipients have been added"

        if arg_1.has_bad_headers(arg_0.mail.default_Funcer):
            raise BadHeaderError

        if arg_1.date is None:
            arg_1.date = time.time()

        arg_3 = arg_1.Funcer or arg_0.mail.default_Funcer
        if arg_0.host:
            arg_0.host.Funcmail(sanitize_address(arg_3) if arg_3 is not None else None,
                               arg_1.Func_to,
                               arg_1.as_string(arg_0.mail.default_Funcer),
                               arg_1.mail_options,
                               arg_1.rcpt_options)

        email_dispatched.Func(arg_1, mail=arg_0.mail)

        arg_0.num_emails += 1

        if arg_0.num_emails == arg_0.mail.max_emails:
            arg_0.num_emails = 0
            if arg_0.host:
                arg_0.host.quit()
                arg_0.host = arg_0.configure_host()


def Func(arg_0, arg_1=None):
        """Checks for bad headers i.e. newlines in subject, sender or recipients.
        """

        arg_2 = arg_0.sender or arg_1
        arg_3 = arg_0.reply_to or ''
        for arg_4 in [arg_0.subject, arg_2, arg_3] + arg_0.recipients:
            for arg_5 in '\r\n':
                if arg_5 in arg_4:
                    return True
        return False


def Func():
    """Checks if the user is rooted."""
    import os
    import ctypes
    try:
        return os.geteuid() == 0
    except AttributeError:
        return ctypes.windll.shell32.IsUserAnAdmin() != 0
    return False


def Func(arg_0, **arg_1):
        """
        Register resources with the ResourceManager.
        """
        for arg_2, arg_3 in arg_1.items():
            if arg_2 in arg_0._resources:
                raise AlreadyExistsException('A Service for {} is already registered.'.format(arg_2))

            arg_0._init_resource(arg_2, arg_3)


def Func(arg_0, arg_1):
        """
        Raises an exception if value for ``key`` is empty.
        """
        arg_2 = arg_0.get(arg_1)
        if not arg_2:
            raise ValueError('"{}" is empty.'.format(arg_1))
        return arg_2


def Func(arg_0, arg_1, arg_2, arg_3, arg_4):
        """
        Teardown a Resource or Middleware.
        """
        if arg_2 is None:
            # No in-context exception occurred
            try:
                arg_1.next()
            except StopIteration:
                # Resource closed as expected
                return
            else:
                raise RuntimeError('{} yielded more than once.'.format(arg_1))
        else:
            # In-context exception occurred
            try:
                arg_1.throw(arg_2, arg_3, arg_4)
                raise RuntimeError('{} did not close after throw()'.format(arg_1))
            except StopIteration as exc:
                # Suppress the exception *unless* it's the same exception that
                # was passed to throw().  This prevents a StopIteration
                # raised inside the "with" statement from being suppressed
                return exc is not arg_3
            except:
                # only re-raise if it's *not* the exception that was
                # passed to throw(), because _Func__() must not raise
                # an exception unless _Func__() itself failed.  But
                # resource.throw() will raise the exception to signal propagation,
                # so this fixes the impedance mismatch between the throw() protocol
                # and the _Func__() protocol.
                #
                # Middleware or Resources that throw exceptions before yielding
                # will just rethrow the same exception here which is expected. They
                # won't have a chance to do anything about the exception though which
                # seems OK since they never got to the point of being ready anyway.
                if sys.exc_info()[1] is not arg_3:
                    raise


def Func(arg_0, arg_1):
        """
        Hook to Func this service with a specific DataManager.

        Will recursively Func sub-services.
        """
        arg_0._data_manager = arg_1
        if arg_0._data_manager:
            arg_0._dal = arg_0._data_manager.get_dal()
        else:
            arg_0._dal = None

        for arg_4, arg_5 in arg_0._services.items():
            arg_5.Func(arg_0._data_manager)


def Func(
        arg_0: arg_1,
        arg_2: arg_3 = 'Subgraph'
) -> Tuple[
    Mapping[arg_3, EdgeSet],
    Mapping[arg_3, Mapping[arg_3, EdgeSet]],
    Mapping[arg_3, Mapping[arg_3, EdgeSet]],
    Mapping[arg_3, Mapping[arg_3, float]],
]:
    """Build a DatafFame to show the overlap between different sub-graphs.

    Options:
    1. Total number of edges overlap (intersection)
    2. Percentage overlap (tanimoto similarity)

    :param graph: A BEL graph
    :param annotation: The annotation to group by and compare. Defaults to 'Subgraph'
    :return: {subgraph: set of edges}, {(subgraph 1, subgraph2): set of intersecting edges},
            {(subgraph 1, subgraph2): set of unioned edges}, {(subgraph 1, subgraph2): tanimoto similarity},
    """
    arg_4 = defaultdict(set)

    for arg_5, arg_6, arg_7 in arg_0.edges(data=True):
        if not edge_has_annotation(arg_7, arg_2):
            continue
        arg_4[arg_7[ANNOTATIONS][arg_2]].add((arg_5, arg_6))

    arg_8 = defaultdict(dict)
    arg_9 = defaultdict(dict)
    arg_10 = defaultdict(dict)

    for arg_11, arg_12 in itt.product(arg_4, repeat=2):
        arg_8[arg_11][arg_12] = arg_4[arg_11] & arg_4[arg_12]
        arg_9[arg_11][arg_12] = arg_4[arg_11] | arg_4[arg_12]
        arg_10[arg_11][arg_12] = len(arg_8[arg_11][arg_12]) / len(arg_9[arg_11][arg_12])

    return arg_4, arg_8, arg_9, arg_10


def Func(arg_0: arg_1, arg_2=None, arg_3: arg_4 = 'Subgraph'):
    """Calculate the subgraph similarity tanimoto similarity in nodes passing the given filter.

    Provides an alternate view on subgraph similarity, from a more node-centric view
    """
    arg_5 = group_nodes_by_annotation_filtered(arg_0, arg_2=arg_2, arg_3=arg_3)
    return calculate_tanimoto_set_distances(arg_5)


def Func(arg_0: arg_1, arg_2: arg_3, arg_4: arg_5) -> None:
    """Remove nodes with the given function and namespace.

    This might be useful to exclude information learned about distant species, such as excluding all information
    from MGI and RGD in diseases where mice and rats don't give much insight to the human disease mechanism.
    """
    remove_filtered_nodes(arg_0, function_namespace_inclusion_builder(arg_2, arg_4))


def Func(arg_0):
    """Preprocess the excel sheet

    :param filepath: filepath of the excel data
    :return: df: pandas dataframe with excel data
    :rtype: pandas.DataFrame
    """
    if not os.path.exists(arg_0):
        raise ValueError("Error: %s file not found" % arg_0)

    # Import Models from Excel sheet, independent for AD and PD
    arg_1 = pd.read_excel(arg_0, sheetname=0, header=0)

    # Indexes and column name
    # [log.info(str(x)+': '+str((df.columns.values[x]))) for x in range (0,len(df.columns.values))]

    # Starting from 4: Pathway Name

    # Fill Pathway cells that are merged and are 'NaN' after deleting rows where there is no genes
    arg_1.iloc[:, 0] = pd.Series(arg_1.iloc[:, 0]).fillna(method='ffill')

    # Number of gaps
    # log.info(df.ix[:,6].isnull().sum())

    arg_1 = arg_1[arg_1.ix[:, 1].notnull()]
    arg_1 = arg_1.reset_index(drop=True)

    # Fill NaN to ceros in PubmedID column
    arg_1.ix[:, 2].fillna(0, inplace=True)

    # Number of gaps in the gene column should be already zero
    if (arg_1.ix[:, 1].isnull().sum()) != 0:
        raise ValueError("Error: Empty cells in the gene column")

    # Check current state
    # df.to_csv('out.csv')

    return arg_1


def Func(arg_0: arg_1) -> Set[NodePair]:
    """Find pairs of nodes that have mutual causal edges that are regulating each other such that ``A -> B`` and
    ``B -| A``.

    :return: A set of pairs of nodes with mutual causal edges
    """
    arg_2 = get_causal_subgraph(arg_0)

    arg_3 = set()

    for arg_4, arg_5, arg_6 in arg_2.edges(data=True):
        if arg_6[RELATION] not in CAUSAL_INCREASE_RELATIONS:
            continue

        if arg_2.has_edge(arg_5, arg_4) and any(arg_7[RELATION] in CAUSAL_DECREASE_RELATIONS for arg_7 in arg_2[arg_5][arg_4].values()):
            arg_3.add((arg_4, arg_5))

    return arg_3


def Func(arg_0: arg_1) -> SetOfNodePairs:
    """Find pairs of nodes that have mutual causal edges that are increasing each other such that ``A -> B`` and
    ``B -> A``.

    :return: A set of pairs of nodes with mutual causal edges
    """
    arg_2 = get_causal_subgraph(arg_0)

    arg_3 = set()

    for arg_4, arg_5, arg_6 in arg_2.edges(data=True):
        if arg_6[RELATION] not in CAUSAL_INCREASE_RELATIONS:
            continue

        if arg_2.has_edge(arg_5, arg_4) and any(arg_7[RELATION] in CAUSAL_INCREASE_RELATIONS for arg_7 in arg_2[arg_5][arg_4].values()):
            arg_3.add(tuple(sorted([arg_4, arg_5], key=str)))

    return arg_3


def Func(arg_0: arg_1) -> Graph:
    """Extract an undirected graph of only correlative relationships."""
    arg_2 = Graph()

    for arg_3, arg_4, arg_5 in arg_0.edges(data=True):
        if arg_5[arg_6] not in CORRELATIVE_RELATIONS:
            continue

        if not arg_2.has_edge(arg_3, arg_4):
            arg_2.add_edge(arg_3, arg_4, **{arg_5[arg_6]: True})

        elif arg_5[arg_6] not in arg_2[arg_3][arg_4]:
            log.log(5, 'broken correlation relation for %s, %s', arg_3, arg_4)
            arg_2[arg_3][arg_4][arg_5[arg_6]] = True
            arg_2[arg_4][arg_3][arg_5[arg_6]] = True

    return arg_2


def Func(arg_0: arg_1) -> SetOfNodeTriples:
    """Return a set of all triangles pointed by the given node."""
    return {
        tuple(sorted([arg_2, arg_3, arg_4], key=str))
        for arg_2 in arg_0
        for arg_3, arg_4 in itt.combinations(arg_0[arg_2], 2)
        if arg_0.has_edge(arg_3, arg_4)
    }


def Func(arg_0: arg_1) -> Iterable[NodeTriple]:
    """Yield all triples of nodes A, B, C such that ``A pos B``, ``A pos C``, and ``B neg C``.

    :return: An iterator over triples of unstable graphs, where the second two are negative
    """
    arg_2 = get_correlation_graph(arg_0)

    for arg_3, arg_4, arg_5 in get_correlation_triangles(arg_2):
        if POSITIVE_CORRELATION in arg_2[arg_3][arg_4] and POSITIVE_CORRELATION in arg_2[arg_4][arg_5] and NEGATIVE_CORRELATION in \
                arg_2[arg_3][arg_5]:
            yield arg_4, arg_3, arg_5
        if POSITIVE_CORRELATION in arg_2[arg_3][arg_4] and NEGATIVE_CORRELATION in arg_2[arg_4][arg_5] and POSITIVE_CORRELATION in \
                arg_2[arg_3][arg_5]:
            yield arg_3, arg_4, arg_5
        if NEGATIVE_CORRELATION in arg_2[arg_3][arg_4] and POSITIVE_CORRELATION in arg_2[arg_4][arg_5] and POSITIVE_CORRELATION in \
                arg_2[arg_3][arg_5]:
            yield arg_5, arg_3, arg_4


def Func(arg_0: arg_1) -> Mapping[str, int]:
    """Summarize the stability of the graph."""
    arg_2 = get_regulatory_pairs(arg_0)
    arg_3 = get_chaotic_pairs(arg_0)
    arg_4 = get_dampened_pairs(arg_0)
    arg_5 = get_contradiction_summary(arg_0)
    arg_6 = get_separate_unstable_correlation_triples(arg_0)
    arg_7 = get_mutually_unstable_correlation_triples(arg_0)
    arg_8 = get_jens_unstable(arg_0)
    arg_9 = get_increase_mismatch_triplets(arg_0)
    arg_10 = get_decrease_mismatch_triplets(arg_0)
    arg_11 = get_chaotic_triplets(arg_0)
    arg_12 = get_dampened_triplets(arg_0)

    return {
        'Regulatory Pairs': _count_or_len(arg_2),
        'Chaotic Pairs': _count_or_len(arg_3),
        'Dampened Pairs': _count_or_len(arg_4),
        'Contradictory Pairs': _count_or_len(arg_5),
        'Separately Unstable Triples': _count_or_len(arg_6),
        'Mutually Unstable Triples': _count_or_len(arg_7),
        'Jens Unstable Triples': _count_or_len(arg_8),
        'Increase Mismatch Triples': _count_or_len(arg_9),
        'Decrease Mismatch Triples': _count_or_len(arg_10),
        'Chaotic Triples': _count_or_len(arg_11),
        'Dampened Triples': _count_or_len(arg_12)
    }


def Func(arg_0: arg_1) -> arg_1:
    """Flattens the complex or composite abundance."""
    return arg_0.__class__(list(chain.from_iterable(
        (
            Func(arg_2).members
            if isinstance(arg_2, arg_1) else
            [arg_2]
        )
        for arg_2 in arg_0.members
    )))


def Func(arg_0: arg_1) -> None:
    """Flatten list abundances."""
    arg_2 = {
        node: flatten_list_abundance(node)
        for node in arg_0
        if isinstance(node, ListAbundance)
    }
    relabel_nodes(arg_0, arg_2, copy=False)


def Func(arg_0: arg_1) -> None:
    """Expand all list abundances to simple subject-predicate-object networks."""
    for arg_2, arg_3, arg_4, arg_5 in list(arg_0.edges(keys=True, data=True)):
        if CITATION not in arg_5:
            continue

        if isinstance(arg_2, ListAbundance) and isinstance(arg_3, ListAbundance):
            for arg_6, arg_7 in itt.product(arg_2.members, arg_3.members):
                arg_0.add_qualified_edge(
                    arg_6, arg_7,
                    relation=arg_5[RELATION],
                    citation=arg_5.get(CITATION),
                    evidence=arg_5.get(EVIDENCE),
                    annotations=arg_5.get(ANNOTATIONS),
                )

        elif isinstance(arg_2, ListAbundance):
            for arg_8 in arg_2.members:
                arg_0.add_qualified_edge(
                    arg_8, arg_3,
                    relation=arg_5[RELATION],
                    citation=arg_5.get(CITATION),
                    evidence=arg_5.get(EVIDENCE),
                    annotations=arg_5.get(ANNOTATIONS),
                )

        elif isinstance(arg_3, ListAbundance):
            for arg_8 in arg_3.members:
                arg_0.add_qualified_edge(
                    arg_2, arg_8,
                    relation=arg_5[RELATION],
                    citation=arg_5.get(CITATION),
                    evidence=arg_5.get(EVIDENCE),
                    annotations=arg_5.get(ANNOTATIONS),
                )

    _remove_list_abundance_nodes(arg_0)


def Func(
        arg_0: arg_1,
        arg_2: arg_3,
        arg_4: arg_3,
        arg_5: arg_6,
) -> None:
    """Helper to deal with cartension expansion in unqualified edges."""
    if isinstance(arg_2, Reaction) and isinstance(arg_4, Reaction):
        arg_7 = _get_catalysts_in_reaction(arg_2) | _get_catalysts_in_reaction(arg_4)

        for arg_8, arg_9 in chain(itt.product(arg_2.reactants, arg_2.products),
                                       itt.product(arg_4.reactants, arg_4.products)):
            if arg_8 in arg_7 or arg_9 in arg_7:
                continue

            arg_0.add_unqualified_edge(
                arg_8, arg_9, INCREASES
            )
        for arg_9, arg_8 in itt.product(arg_2.products, arg_2.reactants):

            if arg_8 in arg_7 or arg_9 in arg_7:
                continue

            arg_0.add_unqualified_edge(
                arg_9, arg_8, arg_5[RELATION],
            )

    elif isinstance(arg_2, Reaction):

        arg_7 = _get_catalysts_in_reaction(arg_2)

        for arg_9 in arg_2.products:

            # Skip create increases edges between enzymes
            if arg_9 in arg_7:
                continue

            # Only add edge between v and reaction if the node is not part of the reaction
            # In practice skips hasReactant, hasProduct edges
            if arg_4 not in arg_2.products and arg_4 not in arg_2.reactants:
                arg_0.add_unqualified_edge(
                    arg_9, arg_4, INCREASES
                )
            for arg_8 in arg_2.reactants:
                arg_0.add_unqualified_edge(
                    arg_8, arg_9, INCREASES
                )

    elif isinstance(arg_4, Reaction):

        arg_7 = _get_catalysts_in_reaction(arg_4)

        for arg_8 in arg_4.reactants:

            # Skip create increases edges between enzymes
            if arg_8 in arg_7:
                continue

            # Only add edge between v and reaction if the node is not part of the reaction
            # In practice skips hasReactant, hasProduct edges
            if arg_2 not in arg_4.products and arg_2 not in arg_4.reactants:
                arg_0.add_unqualified_edge(
                    arg_2, arg_8, INCREASES
                )
            for arg_9 in arg_4.products:
                arg_0.add_unqualified_edge(
                    arg_8, arg_9, INCREASES
                )


def Func(arg_0: arg_1) -> Set[BaseAbundance]:
    """Return nodes that are both in reactants and reactions in a reaction."""
    return {
        arg_2
        for arg_2 in arg_0.reactants
        if arg_2 in arg_0.products
    }


def Func(arg_0: arg_1) -> Mapping[str, Mapping[Tuple[BaseEntity, BaseEntity], str]]:
    """Prepare a citation data dictionary from a graph.

    :return: A dictionary of dictionaries {citation type: {(source, target): citation reference}
    """
    arg_2 = defaultdict(lambda: defaultdict(set))

    for arg_3, arg_4, arg_5 in arg_0.edges(arg_5=True):
        if CITATION not in arg_5:
            continue
        arg_2[arg_5[CITATION][CITATION_TYPE]][arg_3, arg_4].add(arg_5[CITATION][CITATION_REFERENCE].strip())

    return dict(arg_2)


def Func(arg_0: arg_1, **arg_2) -> Counter:
    """Counts the citations in a graph based on a given filter

    :param graph: A BEL graph
    :param dict annotations: The annotation filters to use
    :return: A counter from {(citation type, citation reference): frequency}
    """
    arg_3 = defaultdict(set)

    arg_4 = build_edge_data_filter(arg_2)

    for arg_5, arg_6, arg_7, arg_8 in filter_edges(arg_0, arg_4):
        if CITATION not in arg_8:
            continue

        arg_3[arg_5, arg_6].add((arg_8[CITATION][CITATION_TYPE], arg_8[CITATION][CITATION_REFERENCE].strip()))

    return Counter(itt.chain.from_iterable(arg_3.values()))


def Func(arg_0: arg_1) -> typing.Counter[str]:
    """Count the number of publications of each author to the given graph."""
    arg_2 = group_as_dict(_iter_author_publiations(arg_0))
    return Counter(count_dict_values(count_defaultdict(arg_2)))


def Func(arg_0: arg_1, arg_2: arg_3 = 'Subgraph') -> Mapping[arg_3, typing.Counter[arg_3]]:
    """Group the author counters by sub-graphs induced by the annotation.

    :param graph: A BEL graph
    :param annotation: The annotation to use to group the graph
    :return: A dictionary of Counters {subgraph name: Counter from {author: frequency}}
    """
    arg_4 = group_as_dict(_iter_authors_by_annotation(arg_0, arg_2=arg_2))
    return count_defaultdict(arg_4)


def Func(arg_0: arg_1, arg_2: arg_3[arg_4, arg_5[arg_4]]):
    """Get a dictionary from the given PubMed identifiers to the sets of all evidence strings associated with each
    in the graph.

    :param graph: A BEL graph
    :param pmids: An iterable of PubMed identifiers, as strings. Is consumed and converted to a set.
    :return: A dictionary of {pmid: set of all evidence strings}
    :rtype: dict
    """
    arg_6 = defaultdict(set)

    for arg_7, arg_7, arg_7, arg_8 in filter_edges(arg_0, build_pmid_inclusion_filter(arg_2)):
        arg_6[arg_8[CITATION][CITATION_REFERENCE]].add(arg_8[EVIDENCE])

    return dict(arg_6)


def Func(arg_0: arg_1.Counter[arg_3]) -> List[Tuple[arg_3, arg_3]]:
    """Complete the Counter timeline.

    :param Counter year_counter: counter dict for each year
    :return: complete timeline
    """
    if not arg_0:
        return []

    arg_4 = min(arg_0) - 1
    arg_5 = datetime.now().year + 1

    return [
        (arg_6, arg_0.get(arg_6, 0))
        for arg_6 in range(arg_4, arg_5)
    ]


def Func(arg_0: arg_1) -> typing.Counter[str]:
    """Count the confidences in the graph."""
    return Counter(
        (
            'None'
            if ANNOTATIONS not in arg_3 or 'Confidence' not in arg_3[ANNOTATIONS] else
            list(arg_3[ANNOTATIONS]['Confidence'])[0]
        )
        for arg_2, arg_2, arg_3 in arg_0.edges(arg_3=True)
        if CITATION in arg_3  # don't bother with unqualified statements
    )


def Func(arg_0: arg_1, arg_2: arg_3) -> Set[str]:
    """Overwrite all PubMed citations with values from NCBI's eUtils lookup service.

    :return: A set of PMIDs for which the eUtils service crashed
    """
    arg_4 = get_pubmed_identifiers(arg_0)
    arg_5, arg_6 = get_citations_by_pmids(arg_2=arg_2, arg_4=arg_4)

    for arg_7, arg_8, arg_9 in filter_edges(arg_0, has_pubmed):
        arg_10 = arg_0[arg_7][arg_8][arg_9][CITATION][CITATION_REFERENCE].strip()

        if arg_10 not in arg_5:
            log.warning('Missing data for PubMed identifier: %s', arg_10)
            arg_6.add(arg_10)
            continue

        arg_0[arg_7][arg_8][arg_9][CITATION].update(arg_5[arg_10])

    return arg_6


def Func(arg_0: arg_1, arg_2: arg_1):
    """Update the context of a subgraph from the universe of all knowledge."""
    for arg_3 in get_namespaces(arg_2):
        if arg_3 in arg_0.namespace_url:
            arg_2.namespace_url[arg_3] = arg_0.namespace_url[arg_3]
        elif arg_3 in arg_0.namespace_pattern:
            arg_2.namespace_pattern[arg_3] = arg_0.namespace_pattern[arg_3]
        else:
            log.warning('namespace: %s missing from universe', arg_3)

    for arg_6 in get_annotations(arg_2):
        if arg_6 in arg_0.annotation_url:
            arg_2.annotation_url[arg_6] = arg_0.annotation_url[arg_6]
        elif arg_6 in arg_0.annotation_pattern:
            arg_2.annotation_pattern[arg_6] = arg_0.annotation_pattern[arg_6]
        elif arg_6 in arg_0.annotation_list:
            arg_2.annotation_list[arg_6] = arg_0.annotation_list[arg_6]
        else:
            log.warning('annotation: %s missing from universe', arg_6)


def Func(arg_0: arg_1, arg_2: arg_3[arg_4[arg_5]] = None, arg_6: arg_3[arg_7]=None):
    """Adds a highlight tag to the given nodes.

    :param graph: A BEL graph
    :param nodes: The nodes to add a highlight tag on
    :param color: The color to highlight (use something that works with CSS)
    """
    arg_6 = arg_6 or NODE_HIGHLIGHT_DEFAULT_COLOR
    for arg_8 in arg_2 if arg_2 is not None else arg_0:
        arg_0.node[arg_8][arg_9] = arg_6


def Func(arg_0: arg_1, arg_2: arg_3) -> bool:
    """Returns if the given node is highlighted.

    :param graph: A BEL graph
    :param node: A BEL node
    :type node: tuple
    :return: Does the node contain highlight information?
    :rtype: bool
    """
    return NODE_HIGHLIGHT in arg_0.node[arg_2]


def Func(arg_0: arg_1, arg_2: arg_3[arg_4[arg_5]]=None) -> None:
    """Removes the highlight from the given nodes, or all nodes if none given.

    :param graph: A BEL graph
    :param nodes: The list of nodes to un-highlight
    """
    for arg_6 in arg_0 if arg_2 is None else arg_2:
        if is_node_highlighted(arg_0, arg_6):
            del arg_0.node[arg_6][NODE_HIGHLIGHT]


def Func(arg_0: arg_1, arg_2=None):
    """Remove the highlight from the given edges, or all edges if none given.

    :param graph: A BEL graph
    :param edges: The edges (4-tuple of u,v,k,d) to remove the highlight from)
    :type edges: iter[tuple]
    """
    for arg_3, arg_4, arg_5, arg_6 in arg_0.edges(keys=True, data=True) if arg_2 is None else arg_2:
        if is_edge_highlighted(arg_0, arg_3, arg_4, arg_5):
            del arg_0[arg_3][arg_4][arg_5][EDGE_HIGHLIGHT]


def Func(
        arg_0: arg_1,
        arg_2: arg_3[arg_4, arg_5[arg_4]],
) -> Set[Tuple[arg_4, arg_4]]:
    """Get the out-edges to the given node that are causal.

    :return: A set of (source, target) pairs where the source is the given node
    """
    return {
        (arg_6, arg_7)
        for arg_6, arg_7, arg_8, arg_9 in arg_0.out_edges(arg_2, keys=True, data=True)
        if is_causal_relation(arg_0, arg_6, arg_7, arg_8, arg_9)
    }


def Func(arg_0: arg_1, arg_2: arg_3) -> Set[BaseEntity]:
    """Return a set of all nodes that have an in-degree of 0.

    This likely means that it is an external perturbagen and is not known to have any causal origin from within the
    biological system. These nodes are useful to identify because they generally don't provide any mechanistic insight.
    """
    return {
        arg_4
        for arg_4 in arg_0
        if arg_4.function == arg_2 and is_causal_source(arg_0, arg_4)
    }


def Func(arg_0: arg_1) -> Mapping[str, int]:
    """Get a modifications count dictionary."""
    return remove_falsy_values({
        'Translocations': len(get_translocated(arg_0)),
        'Degradations': len(get_degradations(arg_0)),
        'Molecular Activities': len(get_activities(arg_0)),
    })


def Func(arg_0: arg_1[arg_2, arg_3]) -> arg_1[arg_2, arg_3]:
    """Remove all values that are zero."""
    return {
        arg_4: arg_5
        for arg_4, arg_5 in arg_0.items()
        if arg_5
    }


def Func(arg_0: arg_1, arg_2: arg_3) -> None:
    """Collapse all of the given functions' variants' edges to their parents, in-place."""
    for arg_4, arg_5, arg_6 in arg_0.edges(arg_6=True):
        if arg_6[RELATION] == HAS_VARIANT and arg_4.function == arg_2:
            collapse_pair(arg_0, from_node=arg_5, to_node=arg_4)


def Func(arg_0: arg_1, arg_2: arg_3 = None) -> None:
    """Collapse all edges passing the given edge predicates."""
    for arg_4, arg_5, arg_6 in filter_edges(arg_0, arg_2=arg_2):
        collapse_pair(arg_0, survivor=arg_4, victim=arg_5)


def Func(arg_0: arg_1, arg_2: arg_3, arg_4: arg_5) -> None:
    """Collapse pairs of nodes with the given namespaces that have orthology relationships.

    :param graph: A BEL Graph
    :param victim_namespace: The namespace(s) of the node to collapse
    :param survivor_namespace: The namespace of the node to keep

    To collapse all MGI nodes to their HGNC orthologs, use:
    >>> Func('MGI', 'HGNC')

    To collapse collapse both MGI and RGD nodes to their HGNC orthologs, use:
    >>> Func(['MGI', 'RGD'], 'HGNC')
    """
    _collapse_edge_by_namespace(arg_0, arg_2, arg_4, ORTHOLOGOUS)


def Func(arg_0: arg_1):
    """Collapse all equivalence edges away from Entrez. Assumes well formed, 2-way equivalencies."""
    arg_2 = build_relation_predicate(EQUIVALENT_TO)
    arg_3 = build_source_namespace_filter(['EGID', 'EG', 'ENTREZ'])

    arg_4 = [
        arg_2,
        arg_3,
    ]

    _collapse_edge_passing_predicates(arg_0, arg_4=arg_4)


def Func(arg_0: arg_1):
    """Collapse consistent edges together.

    .. warning:: This operation doesn't preserve evidences or other annotations
    """
    for arg_2, arg_3 in arg_0.edges():
        arg_4 = pair_is_consistent(arg_0, arg_2, arg_3)

        if not arg_4:
            continue

        arg_5 = [(arg_2, arg_3, k) for k in arg_0[arg_2][arg_3]]
        arg_0.remove_edges_from(arg_5)
        arg_0.add_edge(arg_2, arg_3, attr_dict={RELATION: arg_4})


def Func(arg_0: arg_1) -> None:
    """Collapse all nodes with the same name, merging namespaces by picking first alphabetical one."""
    arg_2 = defaultdict(set) # Collapse mapping dict
    arg_3 = set() # Things already mapped while iterating

    arg_4 = tqdm(itt.combinations(arg_0, r=2), total=arg_0.number_of_nodes() * (arg_0.number_of_nodes() - 1) / 2)
    for arg_5, arg_6 in arg_4:
        if arg_6 in arg_3:
            continue

        arg_7, arg_8 = arg_5.get(NAME), arg_6.get(NAME)
        if not arg_7 or not arg_8 or arg_7.lower() != arg_8.lower():
            continue

        if arg_5.keys() != arg_6.keys():  # not same version (might have variants)
            continue

        # Ensure that the values in the keys are also the same
        for arg_9 in set(arg_5.keys()) - {NAME, NAMESPACE}:
            if arg_5[arg_9] != arg_6[arg_9]:  # something different
                continue

        arg_2[arg_5].add(arg_6)
        # Keep track of things that has been already mapped
        arg_3.add(arg_6)

    collapse_nodes(arg_0, arg_2)


def Func(arg_0):
    """Output the HBP knowledge graph to the desktop"""
    from hbp_knowledge import get_graph
    arg_1 = get_graph()
    arg_2 = to_html(arg_1)
    print(arg_2, file=arg_0)


def Func(arg_0: arg_1, arg_2: arg_3) -> bool:
    """Return if the node is an upstream leaf.

    An upstream leaf is defined as a node that has no in-edges, and exactly 1 out-edge.
    """
    return 0 == len(arg_0.predecessors(arg_2)) and 1 == len(arg_0.successors(arg_2))


def Func(arg_0: arg_1, arg_2: arg_3, arg_4: arg_5) -> bool:
    """Check if the node is both a source and also has an annotation.

    :param graph: A BEL graph
    :param node: A BEL node
    :param key: The key in the node data dictionary representing the experimental data
    """
    return arg_0.in_degree(arg_2) == 0 and arg_4 not in arg_0.nodes[arg_2]


def Func(arg_0: arg_1, arg_2: arg_3[arg_4] = None) -> Iterable[BaseEntity]:
    """Get nodes on the periphery of the sub-graph that do not have a annotation for the given key.

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data
    :return: An iterator over BEL nodes that are unannotated and on the periphery of this subgraph
    """
    if arg_2 is None:
        arg_2 = WEIGHT

    for arg_5 in arg_0:
        if is_unweighted_source(arg_0, arg_5, arg_2):
            yield arg_5


def Func(arg_0: arg_1, arg_2: arg_3[arg_4] = None) -> None:
    """Prune unannotated nodes on the periphery of the sub-graph.

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data. Defaults to
     :data:`pybel_tools.constants.WEIGHT`.
    """
    arg_5 = list(get_unweighted_sources(arg_0, arg_2=arg_2))
    arg_0.remove_nodes_from(arg_5)


def Func(arg_0, arg_1: arg_2[arg_3] = None) -> None:
    """Remove all leaves and source nodes that don't have weights.

    Is a thin wrapper around  :func:`remove_unweighted_leaves` and :func:`remove_unweighted_sources`

    :param graph: A BEL graph
    :param key: The key in the node data dictionary representing the experimental data. Defaults to
     :data:`pybel_tools.constants.WEIGHT`.

    Equivalent to:

    >>> remove_unweighted_leaves(graph)
    >>> remove_unweighted_sources(graph)
    """
    remove_unweighted_leaves(arg_0, arg_1=arg_1)
    remove_unweighted_sources(arg_0, arg_1=arg_1)


def Func(arg_0: arg_1, arg_2: arg_3, arg_4: arg_5[arg_6] = None) -> arg_1:
    """Generate a mechanistic sub-graph upstream of the given node.

    :param graph: A BEL graph
    :param node: A BEL node
    :param key: The key in the node data dictionary representing the experimental data.
    :return: A sub-graph grown around the target BEL node
    """
    arg_7 = get_upstream_causal_subgraph(arg_0, arg_2)
    expand_upstream_causal(arg_0, arg_7)
    remove_inconsistent_edges(arg_7)
    collapse_consistent_edges(arg_7)

    if arg_4 is not None:  # FIXME when is it not pruned?
        prune_mechanism_by_data(arg_7, arg_4)

    return arg_7


def Func(arg_0: arg_1,
                          arg_2: arg_3[arg_4],
                          arg_5: arg_6 = 'Subgraph',
                          arg_7: arg_8[arg_9] = None,
                          arg_10: arg_8[arg_9] = None,
                          arg_11: arg_8[arg_9] = None,
                          arg_12: arg_8[arg_9] = None,
                          arg_13: arg_14 = False
                          ) -> arg_8[Mapping[arg_6, arg_9]]:
    """Preprocess the graph, stratify by the given annotation, then run the NeuroMMSig algorithm on each.

    :param graph: A BEL graph
    :param genes: A list of gene nodes
    :param annotation: The annotation to use to stratify the graph to subgraphs
    :param ora_weight: The relative weight of the over-enrichment analysis score from
     :py:func:`neurommsig_gene_ora`. Defaults to 1.0.
    :param hub_weight: The relative weight of the hub analysis score from :py:func:`neurommsig_hubs`.
     Defaults to 1.0.
    :param top_percent: The percentage of top genes to use as hubs. Defaults to 5% (0.05).
    :param topology_weight: The relative weight of the topolgical analysis core from
     :py:func:`neurommsig_topology`. Defaults to 1.0.
    :param preprocess: If true, preprocess the graph.
    :return: A dictionary from {annotation value: NeuroMMSig composite score}

    Pre-processing steps:

    1. Infer the central dogma with :func:``
    2. Collapse all proteins, RNAs and miRNAs to genes with :func:``
    3. Collapse variants to genes with :func:``
    """
    if arg_13:
        arg_0 = neurommsig_graph_preprocessor.run(arg_0)

    if not any(arg_15 in arg_0 for arg_15 in arg_2):
        logger.debug('no genes mapping to graph')
        return

    arg_16 = get_subgraphs_by_annotation(arg_0, arg_5=arg_5)

    return Func_prestratified(
        arg_16=arg_16,
        arg_2=arg_2,
        arg_7=arg_7,
        arg_10=arg_10,
        arg_11=arg_11,
        arg_12=arg_12,
    )


def Func(arg_0: arg_1[arg_2, arg_3],
                                        arg_4: arg_5[arg_6],
                                        arg_7: arg_8[arg_9] = None,
                                        arg_10: arg_8[arg_9] = None,
                                        arg_11: arg_8[arg_9] = None,
                                        arg_12: arg_8[arg_9] = None,
                                        ) -> arg_8[arg_1[arg_2, arg_9]]:
    """Takes a graph stratification and runs neurommsig on each

    :param subgraphs: A pre-stratified set of graphs
    :param genes: A list of gene nodes
    :param ora_weight: The relative weight of the over-enrichment analysis score from
     :py:func:`neurommsig_gene_ora`. Defaults to 1.0.
    :param hub_weight: The relative weight of the hub analysis score from :py:func:`neurommsig_hubs`.
     Defaults to 1.0.
    :param top_percent: The percentage of top genes to use as hubs. Defaults to 5% (0.05).
    :param topology_weight: The relative weight of the topolgical analysis core from
     :py:func:`neurommsig_topology`. Defaults to 1.0.
    :return: A dictionary from {annotation value: NeuroMMSig composite score}

    Pre-processing steps:

    1. Infer the central dogma with :func:``
    2. Collapse all proteins, RNAs and miRNAs to genes with :func:``
    3. Collapse variants to genes with :func:``
    """
    return {
        arg_13: get_neurommsig_score(
            graph=arg_14,
            arg_4=arg_4,
            arg_7=arg_7,
            arg_10=arg_10,
            arg_11=arg_11,
            arg_12=arg_12,
        )
        for arg_13, arg_14 in arg_0.items()
    }


def Func(arg_0: arg_1,
                         arg_2: arg_3[arg_4],
                         arg_5: arg_6[arg_7] = None,
                         arg_8: arg_6[arg_7] = None,
                         arg_9: arg_6[arg_7] = None,
                         arg_10: arg_6[arg_7] = None) -> arg_7:
    """Calculate the composite NeuroMMSig Score for a given list of genes.

    :param graph: A BEL graph
    :param genes: A list of gene nodes
    :param ora_weight: The relative weight of the over-enrichment analysis score from
     :py:func:`neurommsig_gene_ora`. Defaults to 1.0.
    :param hub_weight: The relative weight of the hub analysis score from :py:func:`neurommsig_hubs`.
     Defaults to 1.0.
    :param top_percent: The percentage of top genes to use as hubs. Defaults to 5% (0.05).
    :param topology_weight: The relative weight of the topolgical analysis core from
     :py:func:`neurommsig_topology`. Defaults to 1.0.
    :return: The NeuroMMSig composite score
    """
    arg_5 = arg_5 or 1.0
    arg_8 = arg_8 or 1.0
    arg_10 = arg_10 or 1.0
    arg_11 = arg_5 + arg_8 + arg_10

    arg_2 = list(arg_2)

    arg_12 = neurommsig_gene_ora(arg_0, arg_2)
    arg_13 = neurommsig_hubs(arg_0, arg_2, arg_9=arg_9)
    arg_14 = neurommsig_topology(arg_0, arg_2)

    arg_15 = (
            arg_5 * arg_12 +
            arg_8 * arg_13 +
            arg_10 * arg_14
    )

    return arg_15 / arg_11


def Func(
    arg_0, arg_1, arg_2, arg_3
):
    """
    Perform a number of runs

    The number of runs is the number of seeds

    convolution_factors_tasks_iterator needs to be an iterator

    We shield the convolution factors tasks from jug value/result mechanism
    by supplying an iterator to the list of tasks for lazy evaluation
    http://github.com/luispedro/jug/blob/43f0d80a78f418fd3aa2b8705eaf7c4a5175fff7/jug/task.py#L100
    http://github.com/luispedro/jug/blob/43f0d80a78f418fd3aa2b8705eaf7c4a5175fff7/jug/task.py#L455
    """

    # restore the list of convolution factors tasks
    arg_4 = list(arg_3)

    return reduce(
        percolate.hpc.bond_reduce,
        map(
            bond_run,
            itertools.repeat(arg_0),
            arg_1,
            itertools.repeat(arg_2),
            itertools.repeat(arg_4),
        )
    )


def Func(arg_0: arg_1, arg_2: arg_1) -> EdgeIterator:
    """Get the set of possible successor edges peripheral to the sub-graph.

    The source nodes in this iterable are all inside the sub-graph, while the targets are outside.
    """
    for arg_3 in arg_2:
        for arg_4, arg_5, arg_6 in arg_0.out_edges(arg_3, keys=True):
            if arg_5 not in arg_2:
                yield arg_3, arg_5, arg_6


def Func(arg_0: arg_1, arg_2: arg_1) -> EdgeIterator:
    """Get the set of possible predecessor edges peripheral to the sub-graph.

    The target nodes in this iterable are all inside the sub-graph, while the sources are outside.
    """
    for arg_3 in arg_2:
        for arg_4, arg_5, arg_6 in arg_0.in_edges(arg_3, keys=True):
            if arg_4 not in arg_2:
                yield arg_4, arg_3, arg_6


def Func(arg_0: arg_1) -> Counter:
    """Count the target nodes in an edge iterator with keys and data.

    :return: A counter of target nodes in the iterable
    """
    return Counter(arg_3 for arg_2, arg_3, arg_2 in arg_0)


def Func(arg_0: arg_1,
                       arg_2: arg_3,
                       arg_4: arg_3,
                       arg_5=None,
                       arg_6=None,
                       ):
    """Gets all edges from a given subgraph whose source and target nodes pass all of the given filters

    :param pybel.BELGraph graph: A BEL graph
    :param str annotation:  The annotation to search
    :param str value: The annotation value to search by
    :param source_filter: Optional filter for source nodes (graph, node) -> bool
    :param target_filter: Optional filter for target nodes (graph, node) -> bool
    :return: An iterable of (source node, target node, key, data) for all edges that match the annotation/value and
             node filters
    :rtype: iter[tuple]
    """
    if arg_5 is None:
        arg_5 = keep_node_permissive

    if arg_6 is None:
        arg_6 = keep_node_permissive

    for arg_7, arg_8, arg_9, arg_10 in arg_0.edges(keys=True, arg_10=True):
        if not edge_has_annotation(arg_10, arg_2):
            continue
        if arg_10[ANNOTATIONS][arg_2] == arg_4 and arg_5(arg_0, arg_7) and arg_6(arg_0, arg_8):
            yield arg_7, arg_8, arg_9, arg_10


def Func(arg_0: arg_1,
                                  arg_2: arg_3[arg_4],
                                  arg_5: arg_6 = None,
                                  arg_7: arg_8 = None,
                                  ):
    """Get a summary dictionary of all peripheral nodes to a given sub-graph.

    :return: A dictionary of {external node: {'successor': {internal node: list of (key, dict)},
                                            'predecessor': {internal node: list of (key, dict)}}}
    :rtype: dict

    For example, it might be useful to quantify the number of predecessors and successors:

    >>> from pybel.struct.filters import exclude_pathology_filter
    >>> value = 'Blood vessel dilation subgraph'
    >>> sg = get_subgraph_by_annotation_value(graph, annotation='Subgraph', value=value)
    >>> p = Func(graph, sg, node_predicates=exclude_pathology_filter)
    >>> for node in sorted(p, key=lambda n: len(set(p[n]['successor']) | set(p[n]['predecessor'])), reverse=True):
    >>>     if 1 == len(p[value][node]['successor']) or 1 == len(p[value][node]['predecessor']):
    >>>         continue
    >>>     print(node,
    >>>           len(p[node]['successor']),
    >>>           len(p[node]['predecessor']),
    >>>           len(set(p[node]['successor']) | set(p[node]['predecessor'])))
    """
    arg_9 = concatenate_node_predicates(arg_5=arg_5)
    arg_10 = and_edge_predicates(arg_7=arg_7)

    arg_11 = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))

    for arg_12, arg_13, arg_14, arg_15 in get_peripheral_successor_edges(arg_0, arg_2):
        if not arg_9(arg_0, arg_13) or not arg_9(arg_0, arg_12) or not arg_10(arg_0, arg_12, arg_13, arg_14):
            continue
        arg_11[arg_13]['predecessor'][arg_12].append((arg_14, arg_15))

    for arg_12, arg_13, arg_14, arg_15 in get_peripheral_predecessor_edges(arg_0, arg_2):
        if not arg_9(arg_0, arg_13) or not arg_9(arg_0, arg_12) or not arg_10(arg_0, arg_12, arg_13, arg_14):
            continue
        arg_11[arg_12]['successor'][arg_13].append((arg_14, arg_15))

    return arg_11


def Func(arg_0: arg_1):
    """Adds all of the reactants and products of reactions to the graph."""
    arg_2 = list(get_nodes_by_function(arg_0, REACTION))
    for arg_3 in arg_2:
        for arg_4 in arg_3.reactants:
            arg_0.add_has_reactant(arg_3, arg_4)

        for arg_4 in arg_3.products:
            arg_0.add_has_product(arg_3, arg_4)


def Func(arg_0: arg_1, arg_2: arg_3[None, arg_4, arg_5[arg_4]] = None):
    """Add the reference nodes for all variants of the given function.

    :param graph: The target BEL graph to enrich
    :param func: The function by which the subject of each triple is filtered. Defaults to the set of protein, rna,
     mirna, and gene.
    """
    if arg_2 is None:
        arg_2 = {PROTEIN, RNA, MIRNA, GENE}

    arg_6 = list(get_nodes_by_function(arg_0, arg_2))
    for arg_7 in arg_6:
        arg_8 = arg_7.get_parent()

        if arg_8 is None:
            continue

        if arg_8 not in arg_0:
            arg_0.add_has_variant(arg_8, arg_7)


def Func(arg_0: arg_1):
    """Enrich the sub-graph with the unqualified edges from the graph.

    The reason you might want to do this is you induce a sub-graph from the original graph based on an annotation
    filter, but the unqualified edges that don't have annotations that most likely connect elements within your graph
    are not included.

    .. seealso::

        This function thinly wraps the successive application of the following functions:

        - :func:`enrich_complexes`
        - :func:`enrich_composites`
        - :func:`enrich_reactions`
        - :func:`enrich_variants`

    Equivalent to:

    >>> enrich_complexes(graph)
    >>> enrich_composites(graph)
    >>> enrich_reactions(graph)
    >>> enrich_variants(graph)
    """
    enrich_complexes(arg_0)
    enrich_composites(arg_0)
    enrich_reactions(arg_0)
    enrich_variants(arg_0)


def Func(arg_0: arg_1, arg_2: arg_1, arg_3: arg_4 = None) -> None:
    """Edges between entities in the sub-graph that pass the given filters.

    :param universe: The full graph
    :param graph: A sub-graph to find the upstream information
    :param edge_predicates: Optional list of edge filter functions (graph, node, node, key, data) -> bool
    """
    arg_5 = and_edge_predicates(arg_3)

    for arg_6, arg_7 in itt.product(arg_2, repeat=2):
        if arg_2.has_edge(arg_6, arg_7) or not arg_0.has_edge(arg_6, arg_7):
            continue

        arg_8 = defaultdict(list)
        for arg_9, arg_10 in arg_0[arg_6][arg_7].items():
            if not arg_5(arg_0, arg_6, arg_7, arg_9):
                continue

            arg_8[arg_10[RELATION]].append((arg_9, arg_10))

        if 1 == len(arg_8):
            arg_11 = list(arg_8)[0]
            for arg_9, arg_10 in arg_8[arg_11]:
                arg_2.add_edge(arg_6, arg_7, arg_9=arg_9, **arg_10)
        else:
            log.debug('Multiple relationship types found between %s and %s', arg_6, arg_7)


def Func(arg_0: arg_1, arg_2: arg_1) -> None:
    """Add causal edges between entities in the sub-graph.

    Is an extremely thin wrapper around :func:`expand_internal`.

    :param universe: A BEL graph representing the universe of all knowledge
    :param graph: The target BEL graph to enrich with causal relations between contained nodes

    Equivalent to:

    >>> from pybel_tools.mutation import expand_internal
    >>> from pybel.struct.filters.edge_predicates import is_causal_relation
    >>> expand_internal(universe, graph, edge_predicates=is_causal_relation)
    """
    expand_internal(arg_0, arg_2, edge_predicates=is_causal_relation)


def Func(arg_0: arg_1) -> Mapping[str, Set[str]]:
    """Return the dict of the sets of all incorrect names from the given namespace in the graph.

    :return: The set of all incorrect names from the given namespace in the graph
    """
    return {
        arg_2: Func_by_namespace(arg_0, arg_2)
        for arg_2 in get_namespaces(arg_0)
    }


def Func(arg_0: arg_1) -> Mapping[str, List[int]]:
    """Group the errors together for analysis of the most frequent error.

    :return: A dictionary of {error string: list of line numbers}
    """
    arg_2 = defaultdict(list)

    for arg_3, arg_4, arg_3 in arg_0.warnings:
        arg_2[str(arg_4)].append(arg_4.line_number)

    return dict(arg_2)


def Func(arg_0: arg_1[arg_2, arg_3[arg_4]]) -> arg_1[arg_2, typing.Counter[arg_4]]:
    """Count the number of elements in each value of the dictionary."""
    return {
        arg_5: Counter(arg_6)
        for arg_5, arg_6 in arg_0.items()
    }


def Func(arg_0: arg_1[arg_2], arg_3: arg_1[arg_2]) -> float:
    """What percentage of x is contained within y?

    :param set x: A set
    :param set y: Another set
    :return: The percentage of x contained within y
    """
    arg_4, arg_5 = set(arg_0), set(arg_3)

    if not arg_4:
        return 0.0

    return len(arg_4 & arg_5) / len(arg_4)


def Func(arg_0: arg_1[arg_2], arg_3: arg_1[arg_2]) -> float:
    """Calculate the tanimoto set similarity using the minimum size.

    :param set x: A set
    :param set y: Another set
    :return: The similarity between
        """
    arg_4, arg_5 = set(arg_0), set(arg_3)

    if not arg_4 or not arg_5:
        return 0.0

    return len(arg_4 & arg_5) / min(len(arg_4), len(arg_5))


def Func(arg_0, arg_1, arg_2=None):
    """A convenience function for plotting a horizontal bar plot from a Counter"""
    arg_3 = sorted(arg_0, key=arg_0.get)
    arg_4 = range(len(arg_3))

    arg_1.yticks(arg_4, arg_3)
    arg_1.Func(arg_4, [arg_0[arg_5] for arg_5 in arg_3])

    if arg_2 is not None:
        arg_1.title(arg_2)


def Func(arg_0, arg_1, arg_2, arg_3, arg_4, **arg_5):
    """Adds an edge while preserving negative keys, and paying no respect to positive ones

    :param pybel.BELGraph graph: A BEL Graph
    :param tuple u: The source BEL node
    :param tuple v: The target BEL node
    :param int key: The edge key. If less than zero, corresponds to an unqualified edge, else is disregarded
    :param dict attr_dict: The edge data dictionary
    :param dict attr: Edge data to assign via keyword arguments
    """
    if arg_3 < 0:
        arg_0.add_edge(arg_1, arg_2, arg_3=arg_3, arg_4=arg_4, **arg_5)
    else:
        arg_0.add_edge(arg_1, arg_2, arg_4=arg_4, **arg_5)


def Func(arg_0: arg_1[arg_2[arg_3[arg_4, arg_5]], arg_6[arg_4, arg_5]],
               arg_7: arg_4 = 'y',
               arg_8: arg_4 = 'x',
               ) -> arg_4:
    """Prepares C3 JSON for making a bar chart from a Counter

    :param data: A dictionary of {str: int} to display as bar chart
    :param y_axis_label: The Y axis label
    :param x_axis_label: X axis internal label. Should be left as default 'x')
    :return: A JSON dictionary for making a C3 bar chart
    """
    if not isinstance(arg_0, list):
        arg_0 = sorted(arg_0.items(), key=itemgetter(1), reverse=True)

    try:
        arg_9, arg_10 = zip(*arg_0)
    except ValueError:
        log.info(f'no values found for {x_axis_label}, {y_axis_label}')
        arg_9, arg_10 = [], []

    return json.dumps([
        [arg_8] + list(arg_9),
        [arg_7] + list(arg_10),
    ])


def Func(arg_0: arg_1, arg_2: arg_3 = arg_4) -> Counter:
    """Calculate the betweenness centrality over nodes in the graph.

    Tries to do it with a certain number of samples, but then tries a complete approach if it fails.
    """
    try:
        arg_5 = nx.betweenness_centrality(arg_0, k=arg_2)
    except Exception:
        arg_5 = nx.betweenness_centrality(arg_0)
    return Counter(arg_5)


def Func(arg_0: arg_1, arg_2: arg_3[arg_4[[arg_1], arg_5]] = None) -> arg_1:
    """Get get a canonical representation of the ordered collection by finding its minimum circulation with the
    given sort key
    """
    return min(get_circulations(arg_0), arg_2=arg_2)


def Func(arg_0: arg_1, arg_2: arg_3, arg_4: arg_3) -> bool:
    """Check if a pair of nodes has any contradictions in their causal relationships.

    Assumes both nodes are in the graph.
    """
    arg_5 = {data[RELATION] for data in arg_0[arg_2][arg_4].values()}
    return relation_set_has_contradictions(arg_5)


def Func(arg_0: arg_1[arg_2]) -> bool:
    """Return if the set of BEL relations contains a contradiction."""
    arg_3 = any(relation in CAUSAL_INCREASE_RELATIONS for relation in arg_0)
    arg_4 = any(relation in CAUSAL_DECREASE_RELATIONS for relation in arg_0)
    arg_5 = any(relation == CAUSES_NO_CHANGE for relation in arg_0)
    return 1 < sum([arg_5, arg_4, arg_3])


def Func(arg_0, arg_1):
    r'''
    Compute the average number of runs that have a spanning cluster

    Helper function for :func:`microcanonical_averages`

    Parameters
    ----------

    has_spanning_cluster : 1-D :py:class:`numpy.ndarray` of bool
        Each entry is the ``has_spanning_cluster`` field of the output of
        :func:`sample_states`:
        An entry is ``True`` if there is a spanning cluster in that respective
        run, and ``False`` otherwise.

    alpha : float
        Significance level.

    Returns
    -------

    ret : dict
        Spanning cluster statistics

    ret['spanning_cluster'] : float
        The average relative number (Binomial proportion) of runs that have a
        spanning cluster.
        This is the Bayesian point estimate of the posterior mean, with a
        uniform prior.

    ret['spanning_cluster_ci'] : 1-D :py:class:`numpy.ndarray` of float, size 2
        The lower and upper bounds of the Binomial proportion confidence
        interval with uniform prior.

    See Also
    --------

    sample_states : spanning cluster detection

    microcanonical_averages : spanning cluster statistics

    Notes
    -----

    Averages and confidence intervals for Binomial proportions

    As Cameron [8]_ puts it, the normal approximation to the confidence
    interval for a Binomial proportion :math:`p` "suffers a *systematic*
    decline in performance (...) towards extreme values of :math:`p` near
    :math:`0` and :math:`1`, generating binomial [confidence intervals]
    with effective coverage far below the desired level." (see also
    References [6]_ and [7]_).

    A different approach to quantifying uncertainty is Bayesian inference.
    [5]_
    For :math:`n` independent Bernoulli trails with common success
    probability :math:`p`, the *likelihood* to have :math:`k` successes
    given :math:`p` is the binomial distribution

    .. math::

        P(k|p) = \binom{n}{k} p^k (1-p)^{n-k} \equiv B(a,b),

    where :math:`B(a, b)` is the *Beta distribution* with parameters
    :math:`a = k + 1` and :math:`b = n - k + 1`.
    Assuming a uniform prior :math:`P(p) = 1`, the *posterior* is [5]_

    .. math::

        P(p|k) = P(k|p)=B(a,b).

    A point estimate is the posterior mean

    .. math::

        \bar{p} = \frac{k+1}{n+2}

    with the :math:`1 - \alpha` credible interval :math:`(p_l, p_u)` given
    by

    .. math::

        \int_0^{p_l} dp B(a,b) = \int_{p_u}^1 dp B(a,b) = \frac{\alpha}{2}.

    References
    ----------

    .. [5] Wasserman, L. All of Statistics (Springer New York, 2004),
       `doi:10.1007/978-0-387-21736-9 <http://dx.doi.org/10.1007/978-0-387-21736-9>`_.

    .. [6] DasGupta, A., Cai, T. T. & Brown, L. D. Interval Estimation for a
       Binomial Proportion. Statistical Science 16, 101-133 (2001).
       `doi:10.1214/ss/1009213286 <http://dx.doi.org/10.1214/ss/1009213286>`_.

    .. [7] Agresti, A. & Coull, B. A. Approximate is Better than "Exact" for
       Interval Estimation of Binomial Proportions. The American Statistician
       52, 119-126 (1998),
       `doi:10.2307/2685469 <http://dx.doi.org/10.2307/2685469>`_.

    .. [8] Cameron, E. On the Estimation of Confidence Intervals for Binomial
       Population Proportions in Astronomy: The Simplicity and Superiority of
       the Bayesian Approach. Publications of the Astronomical Society of
       Australia 28, 128-139 (2011),
       `doi:10.1071/as10046 <http://dx.doi.org/10.1071/as10046>`_.

    '''

    arg_2 = dict()
    arg_3 = arg_0.size

    # Bayesian posterior mean for Binomial proportion (uniform prior)
    arg_4 = arg_0.sum(dtype=np.float)
    arg_2['spanning_cluster'] = (
        (arg_4 + 1) / (arg_3 + 2)
    )

    # Bayesian credible interval for Binomial proportion (uniform
    # prior)
    arg_2['spanning_cluster_ci'] = scipy.stats.beta.ppf(
        [arg_1 / 2, 1 - arg_1 / 2], arg_4 + 1, arg_3 - arg_4 + 1
    )

    return arg_2


def Func(
    arg_0, arg_1=40, arg_2=True, arg_3='bond', arg_4=arg_5,
    arg_6=True
):
    r'''
    Generate successive microcanonical percolation ensemble averages

    This is a :ref:`generator function <python:tut-generators>` to successively
    add one edge at a time from the graph to the percolation model for a number
    of independent runs in parallel.
    At each iteration, it calculates and returns the averaged cluster
    statistics.

    Parameters
    ----------
    graph : networkx.Graph
        The substrate graph on which percolation is to take place

    runs : int, optional
        Number of independent runs.
        Defaults to ``40``.

    spanning_cluster : bool, optional
        Defaults to ``True``.

    model : str, optional
        The percolation model (either ``'bond'`` or ``'site'``).
        Defaults to ``'bond'``.

        .. note:: Other models than ``'bond'`` are not supported yet.

    alpha: float, optional
        Significance level.
        Defaults to 1 sigma of the normal distribution.
        ``1 - alpha`` is the confidence level.

    copy_result : bool, optional
        Whether to return a copy or a reference to the result dictionary.
        Defaults to ``True``.

    Yields
    ------
    ret : dict
        Cluster statistics

    ret['n'] : int
        Number of occupied bonds

    ret['N'] : int
        Total number of sites

    ret['M'] : int
        Total number of bonds

    ret['spanning_cluster'] : float
        The average number (Binomial proportion) of runs that have a spanning
        cluster.
        This is the Bayesian point estimate of the posterior mean, with a
        uniform prior.
        Only exists if `spanning_cluster` is set to ``True``.

    ret['spanning_cluster_ci'] : 1-D :py:class:`numpy.ndarray` of float, size 2
        The lower and upper bounds of the Binomial proportion confidence
        interval with uniform prior.
        Only exists if `spanning_cluster` is set to ``True``.

    ret['max_cluster_size'] : float
        Average size of the largest cluster (absolute number of sites)

    ret['max_cluster_size_ci'] : 1-D :py:class:`numpy.ndarray` of float, size 2
        Lower and upper bounds of the normal confidence interval of the average
        size of the largest cluster (absolute number of sites)

    ret['moments'] : 1-D :py:class:`numpy.ndarray` of float, size 5
        The ``k``-th entry is the average ``k``-th raw moment of the (absolute)
        cluster size distribution, with ``k`` ranging from ``0`` to ``4``.

    ret['moments_ci'] : 2-D :py:class:`numpy.ndarray` of float, shape (5,2)
        ``ret['moments_ci'][k]`` are the lower and upper bounds of the normal
        confidence interval of the average ``k``-th raw moment of the
        (absolute) cluster size distribution, with ``k`` ranging from ``0`` to
        ``4``.

    Raises
    ------
    ValueError
        If `runs` is not a positive integer

    ValueError
        If `alpha` is not a float in the interval (0, 1)

    See also
    --------

    sample_states

    percolate.percolate._microcanonical_average_spanning_cluster

    percolate.percolate._microcanonical_average_max_cluster_size

    Notes
    -----
    Iterating through this generator corresponds to several parallel runs of
    the Newman-Ziff algorithm.
    Each iteration yields a microcanonical percolation ensemble for the number
    :math:`n` of occupied bonds. [9]_
    The first iteration yields the trivial microcanonical percolation ensemble
    with :math:`n = 0` occupied bonds.

    Spanning cluster

        .. seealso:: :py:func:`sample_states`

    Raw moments of the cluster size distribution

        .. seealso:: :py:func:`sample_states`

    References
    ----------
    .. [9] Newman, M. E. J. & Ziff, R. M. Fast monte carlo algorithm for site
        or bond percolation. Physical Review E 64, 016706+ (2001),
        `doi:10.1103/physreve.64.016706 <http://dx.doi.org/10.1103/physreve.64.016706>`_.

    '''

    try:
        arg_1 = int(arg_1)
    except:
        raise ValueError("runs needs to be a positive integer")

    if arg_1 <= 0:
        raise ValueError("runs needs to be a positive integer")

    try:
        arg_4 = float(arg_4)
    except:
        raise ValueError("alpha needs to be a float in the interval (0, 1)")

    if arg_4 <= 0.0 or arg_4 >= 1.0:
        raise ValueError("alpha needs to be a float in the interval (0, 1)")

    # initial iteration
    # we do not need a copy of the result dictionary since we copy the values
    # anyway
    arg_7 = [
        sample_states(
            arg_0, arg_2=arg_2, arg_3=arg_3,
            arg_6=False
        )
        for _ in range(arg_1)
    ]

    arg_8 = dict()
    for arg_9 in zip(*arg_7):
        # merge cluster statistics
        arg_8['n'] = arg_9[0]['n']
        arg_8['N'] = arg_9[0]['N']
        arg_8['M'] = arg_9[0]['M']

        arg_10 = np.empty(arg_1)
        arg_11 = np.empty((arg_1, 5))
        if arg_2:
            arg_12 = np.empty(arg_1)

        for arg_13, arg_14 in enumerate(arg_9):
            assert arg_14['n'] == arg_8['n']
            assert arg_14['N'] == arg_8['N']
            assert arg_14['M'] == arg_8['M']
            arg_10[arg_13] = arg_14['max_cluster_size']
            arg_11[arg_13] = arg_14['moments']
            if arg_2:
                arg_12[arg_13] = arg_14['has_spanning_cluster']

        arg_8.update(_microcanonical_average_max_cluster_size(
            arg_10, arg_4
        ))

        arg_8.update(_microcanonical_average_moments(arg_11, arg_4))

        if arg_2:
            arg_8.update(_microcanonical_average_spanning_cluster(
                arg_12, arg_4
            ))

        if arg_6:
            yield copy.deepcopy(arg_8)
        else:
            yield arg_8


def Func(arg_0):
    """
    Generate a linear chain with auxiliary nodes for spanning cluster detection

    Parameters
    ----------

    length : int
       Number of nodes in the chain, excluding the auxiliary nodes.

    Returns
    -------

    networkx.Graph
       A linear chain graph with auxiliary nodes for spanning cluster detection

    See Also
    --------

    sample_states : spanning cluster detection

    """
    arg_1 = nx.grid_graph(dim=[int(arg_0 + 2)])

    arg_1.node[0]['span'] = 0
    arg_1[0][1]['span'] = 0
    arg_1.node[arg_0 + 1]['span'] = 1
    arg_1[arg_0][arg_0 + 1]['span'] = 1

    return arg_1


def Func(arg_0):
    """
    Compile microcanonical averages over all iteration steps into single arrays

    Helper function to aggregate the microcanonical averages over all iteration
    steps into single arrays for further processing

    Parameters
    ----------

    microcanonical_averages : iterable
       Typically, this is the :func:`microcanonical_averages` generator

    Returns
    -------

    ret : dict
       Aggregated cluster statistics

    ret['N'] : int
        Total number of sites

    ret['M'] : int
        Total number of bonds

    ret['spanning_cluster'] : 1-D :py:class:`numpy.ndarray` of float
        The percolation probability:
        The normalized average number of runs that have a spanning cluster.

    ret['spanning_cluster_ci'] : 2-D :py:class:`numpy.ndarray` of float, size 2
        The lower and upper bounds of the percolation probability.

    ret['max_cluster_size'] : 1-D :py:class:`numpy.ndarray` of float
        The percolation strength:
        Average relative size of the largest cluster

    ret['max_cluster_size_ci'] : 2-D :py:class:`numpy.ndarray` of float
        Lower and upper bounds of the normal confidence interval of the
        percolation strength.

    ret['moments'] : 2-D :py:class:`numpy.ndarray` of float, shape (5, M + 1)
        Average raw moments of the (relative) cluster size distribution.

    ret['moments_ci'] : 3-D :py:class:`numpy.ndarray` of float, shape (5, M + 1, 2)
        Lower and upper bounds of the normal confidence interval of the raw
        moments of the (relative) cluster size distribution.

    See Also
    --------

    microcanonical_averages

    """

    arg_1 = dict()

    for arg_2, arg_3 in enumerate(arg_0):
        assert arg_2 == arg_3['n']
        if arg_2 == 0:
            arg_4 = arg_3['M']
            arg_5 = arg_3['N']
            arg_6 = ('spanning_cluster' in arg_3)
            arg_1['max_cluster_size'] = np.empty(arg_4 + 1)
            arg_1['max_cluster_size_ci'] = np.empty((arg_4 + 1, 2))

            if arg_6:
                arg_1['spanning_cluster'] = np.empty(arg_4 + 1)
                arg_1['spanning_cluster_ci'] = np.empty((arg_4 + 1, 2))

            arg_1['moments'] = np.empty((5, arg_4 + 1))
            arg_1['moments_ci'] = np.empty((5, arg_4 + 1, 2))

        arg_1['max_cluster_size'][arg_2] = arg_3['max_cluster_size']
        arg_1['max_cluster_size_ci'][arg_2] = (
            arg_3['max_cluster_size_ci']
        )

        if arg_6:
            arg_1['spanning_cluster'][arg_2] = (
                arg_3['spanning_cluster']
            )
            arg_1['spanning_cluster_ci'][arg_2] = (
                arg_3['spanning_cluster_ci']
            )

        arg_1['moments'][:, arg_2] = arg_3['moments']
        arg_1['moments_ci'][:, arg_2] = arg_3['moments_ci']

    # normalize by number of sites
    for arg_7 in arg_1:
        if 'spanning_cluster' in arg_7:
            continue
        arg_1[arg_7] /= arg_5

    arg_1['M'] = arg_4
    arg_1['N'] = arg_5
    return arg_1


def Func(arg_0, arg_1):
    """
    Compute the binomial PMF according to Newman and Ziff

    Helper function for :func:`canonical_averages`

    See Also
    --------

    canonical_averages

    Notes
    -----

    See Newman & Ziff, Equation (10) [10]_

    References
    ----------

    .. [10] Newman, M. E. J. & Ziff, R. M. Fast monte carlo algorithm for site
        or bond percolation. Physical Review E 64, 016706+ (2001),
        `doi:10.1103/physreve.64.016706 <http://dx.doi.org/10.1103/physreve.64.016706>`_.

    """

    arg_0 = int(arg_0)
    arg_2 = np.empty(arg_0 + 1)

    arg_3 = int(np.round(arg_1 * arg_0))

    arg_2[arg_3] = 1.0

    arg_4 = np.seterr(under='ignore')  # seterr to known value

    for arg_5 in range(arg_3 + 1, arg_0 + 1):
        arg_2[arg_5] = arg_2[arg_5 - 1] * (arg_0 - arg_5 + 1.0) / arg_5 * arg_1 / (1.0 - arg_1)

    for arg_5 in range(arg_3 - 1, -1, -1):
        arg_2[arg_5] = arg_2[arg_5 + 1] * (arg_5 + 1.0) / (arg_0 - arg_5) * (1.0 - arg_1) / arg_1

    np.seterr(**arg_4)  # reset to default

    return arg_2 / arg_2.sum()


def Func(arg_0, arg_1):
    """
    Compute the canonical cluster statistics from microcanonical statistics

    This is according to Newman and Ziff, Equation (2).
    Note that we also simply average the bounds of the confidence intervals
    according to this formula.

    Parameters
    ----------

    ps : iterable of float
       Each entry is a probability for which to form the canonical ensemble
       and compute the weighted statistics from the microcanonical statistics

    microFunc_arrays
       Typically the output of :func:`microFunc_arrays`

    Returns
    -------

    ret : dict
       Canonical ensemble cluster statistics

    ret['ps'] : iterable of float
        The parameter `ps`

    ret['N'] : int
        Total number of sites

    ret['M'] : int
        Total number of bonds

    ret['spanning_cluster'] : 1-D :py:class:`numpy.ndarray` of float
        The percolation probability:
        The normalized average number of runs that have a spanning cluster.

    ret['spanning_cluster_ci'] : 2-D :py:class:`numpy.ndarray` of float, size 2
        The lower and upper bounds of the percolation probability.

    ret['max_cluster_size'] : 1-D :py:class:`numpy.ndarray` of float
        The percolation strength:
        Average relative size of the largest cluster

    ret['max_cluster_size_ci'] : 2-D :py:class:`numpy.ndarray` of float
        Lower and upper bounds of the normal confidence interval of the
        percolation strength.

    ret['moments'] : 2-D :py:class:`numpy.ndarray` of float, shape (5, M + 1)
        Average raw moments of the (relative) cluster size distribution.

    ret['moments_ci'] : 3-D :py:class:`numpy.ndarray` of float, shape (5, M + 1, 2)
        Lower and upper bounds of the normal confidence interval of the raw
        moments of the (relative) cluster size distribution.

    See Also
    --------

    microFunc

    microFunc_arrays

    """

    arg_2 = arg_1['N']
    arg_3 = arg_1['M']
    arg_4 = ('spanning_cluster' in arg_1)

    arg_5 = dict()
    arg_5['ps'] = arg_0
    arg_5['N'] = arg_2
    arg_5['M'] = arg_3

    arg_5['max_cluster_size'] = np.empty(arg_0.size)
    arg_5['max_cluster_size_ci'] = np.empty((arg_0.size, 2))

    if arg_4:
        arg_5['spanning_cluster'] = np.empty(arg_0.size)
        arg_5['spanning_cluster_ci'] = np.empty((arg_0.size, 2))

    arg_5['moments'] = np.empty((5, arg_0.size))
    arg_5['moments_ci'] = np.empty((5, arg_0.size, 2))

    for arg_6, arg_7 in enumerate(arg_0):
        arg_8 = _binomial_pmf(n=arg_3, arg_7=arg_7)

        for arg_9, arg_10 in arg_1.items():
            if len(arg_9) <= 1:
                continue

            if arg_9 in ['max_cluster_size', 'spanning_cluster']:
                arg_5[arg_9][arg_6] = np.sum(arg_8 * arg_10)
            elif arg_9 in ['max_cluster_size_ci', 'spanning_cluster_ci']:
                arg_5[arg_9][arg_6] = np.sum(
                    np.tile(arg_8, (2, 1)).T * arg_10, axis=0
                )
            elif arg_9 == 'moments':
                arg_5[arg_9][:, arg_6] = np.sum(
                    np.tile(arg_8, (5, 1)) * arg_10, axis=1
                )
            elif arg_9 == 'moments_ci':
                arg_5[arg_9][:, arg_6] = np.sum(
                    np.rollaxis(np.tile(arg_8, (5, 2, 1)), 2, 1) * arg_10,
                    axis=1
                )
            else:
                raise NotImplementedError(
                    '{}-dimensional array'.format(arg_10.ndim)
                )

    return arg_5


def Func(
    arg_0, arg_1, arg_2=True, arg_3='bond', arg_4=arg_5, arg_6=40
):
    """
    Helper function to compute percolation Func

    See Also
    --------

    canonical_averages

    microcanonical_averages

    sample_states

    """

    arg_7 = microcanonical_averages(
        arg_0=arg_0, arg_6=arg_6, arg_2=arg_2, arg_3=arg_3,
        arg_4=arg_4
    )

    arg_8 = microcanonical_averages_arrays(
        arg_7
    )

    return canonical_averages(arg_1, arg_8)


def Func(arg_0, arg_1, arg_2):
    """Calculate the final effect of the root node to the sink node in the path.

    :param pybel.BELGraph graph: A BEL graph
    :param list path: Path from root to sink node
    :param dict relationship_dict: dictionary with relationship effects
    :rtype: Effect
    """
    arg_3 = []

    for arg_4, arg_5 in pairwise(arg_1):

        if pair_has_contradiction(arg_0, arg_4, arg_5):
            return Effect.ambiguous

        arg_6 = arg_0.get_edge_data(arg_4, arg_5)

        arg_7, arg_8, arg_9 = rank_edges(arg_6)

        arg_10 = arg_0[arg_4][arg_5][arg_7][RELATION]

        # Returns Effect.no_effect if there is a non causal edge in path
        if arg_10 not in arg_2 or arg_2[arg_10] == 0:
            return Effect.no_effect

        arg_3.append(arg_2[arg_10])

    arg_11 = reduce(lambda x, y: x * y, arg_3)

    return Effect.activation if arg_11 == 1 else Effect.inhibition


def Func(arg_0, arg_1=None):
    """Return the highest ranked edge from a multiedge.

    :param dict edges: dictionary with all edges between two nodes
    :param dict edge_ranking: A dictionary of {relationship: score}
    :return: Highest ranked edge
    :rtype: tuple: (edge id, relation, score given ranking)
    """
    arg_1 = default_edge_ranking if arg_1 is None else arg_1

    arg_2 = [
        (edge_id, edge_data[RELATION], arg_1[edge_data[RELATION]])
        for edge_id, edge_data in arg_0.items()
    ]

    return max(arg_2, key=itemgetter(2))


def Func(arg_0: arg_1,
                                       arg_2: arg_3 = None,
                                       arg_4: arg_5 = 'Subgraph',
                                       ) -> Mapping[arg_5, Set[BaseEntity]]:
    """Group the nodes occurring in edges by the given annotation, with a node filter applied.

    :param graph: A BEL graph
    :param node_predicates: A predicate or list of predicates (graph, node) -> bool
    :param annotation: The annotation to use for grouping
    :return: A dictionary of {annotation value: set of nodes}
    """
    arg_6 = concatenate_node_predicates(arg_2)

    return {
        arg_8: {
            arg_7
            for arg_7 in arg_9
            if arg_6(arg_0, arg_7)
        }
        for arg_8, arg_9 in group_nodes_by_annotation(arg_0, arg_4).items()
    }


def Func(arg_0: arg_1) -> Callable[[arg_3, arg_3, arg_6], None]:
    """Make an expand function that's bound to the manager."""

    @uni_in_place_transformation
    def expand_node_neighborhood_by_hash(arg_2: arg_3, arg_4: arg_3, arg_5: arg_6) -> None:
        """Expand around the neighborhoods of a node by identifier."""
        arg_7 = arg_0.get_dsl_by_hash(arg_5)
        return expand_node_neighborhood(arg_2, arg_4, arg_7)

    return expand_node_neighborhood_by_hash


def Func(arg_0: arg_1) -> Callable[[arg_3, arg_5], None]:
    """Make a delete function that's bound to the manager."""

    @in_place_transformation
    def delete_node_by_hash(arg_2: arg_3, arg_4: arg_5) -> None:
        """Remove a node by identifier."""
        arg_6 = arg_0.get_dsl_by_hash(arg_4)
        arg_2.remove_node(arg_6)

    return delete_node_by_hash


def Func(arg_0: arg_1[arg_2]) -> Dict[arg_2, pd.DataFrame]:
    """Build an adjacency matrix for each KEGG relationship and return in a dictionary.

    :param nodes: A set of HGNC gene symbols
    :return: Dictionary of adjacency matrix for each relationship
    """
    arg_0 = list(sorted(arg_0))

    # Create sheets of the excel in the given order
    arg_3 = OrderedDict()
    for arg_4 in KEGG_RELATIONS:
        arg_3[arg_4] = pd.DataFrame(0, index=arg_0, columns=arg_0)

    return arg_3


def Func(arg_0: arg_1[arg_2, arg_3.DataFrame],
                         arg_5: arg_6,
                         arg_7: arg_6,
                         arg_8: arg_9,
                         ) -> None:
    """Populate the adjacency matrix."""
    if arg_5.namespace.upper() != 'HGNC' or arg_7.namespace.upper() != 'HGNC':
        return

    arg_10 = arg_5.name
    arg_11 = arg_7.name
    arg_12 = arg_8[RELATION]

    if arg_12 in CAUSAL_INCREASE_RELATIONS:
        # If it has pmod check which one and add it to the corresponding matrix
        if arg_7.variants and any(isinstance(arg_13, ProteinModification) for arg_13 in arg_7.variants):
            for arg_13 in arg_7.variants:
                if not isinstance(arg_13, ProteinModification):
                    continue
                if arg_13[IDENTIFIER][NAME] == "Ub":
                    arg_0["activation_ubiquination"][arg_10][arg_11] = 1
                elif arg_13[IDENTIFIER][NAME] == "Ph":
                    arg_0["activation_phosphorylation"][arg_10][arg_11] = 1
        elif isinstance(arg_7, (Gene, Rna)):  # Normal increase, add activation
            arg_0['expression'][arg_10][arg_11] = 1
        else:
            arg_0['activation'][arg_10][arg_11] = 1

    elif arg_12 in CAUSAL_DECREASE_RELATIONS:
        # If it has pmod check which one and add it to the corresponding matrix
        if arg_7.variants and any(isinstance(arg_13, ProteinModification) for arg_13 in arg_7.variants):
            for arg_13 in arg_7.variants:
                if not isinstance(arg_13, ProteinModification):
                    continue
                if arg_13[IDENTIFIER][NAME] == "Ub":
                    arg_0['inhibition_ubiquination'][arg_10][arg_11] = 1
                elif arg_13[IDENTIFIER][NAME] == "Ph":
                    arg_0["inhibition_phosphorylation"][arg_10][arg_11] = 1
        elif isinstance(arg_7, (Gene, Rna)):  # Normal decrease, check which matrix
            arg_0["repression"][arg_10][arg_11] = 1
        else:
            arg_0["inhibition"][arg_10][arg_11] = 1

    elif arg_12 == ASSOCIATION:
        arg_0["binding_association"][arg_10][arg_11] = 1


def Func(arg_0: arg_1[arg_2, arg_3.DataFrame], arg_5: arg_2) -> None:
    """Export a SPIA data dictionary into an Excel sheet at the given path.

    .. note::

        # The R import should add the values:
        # ["nodes"] from the columns
        # ["title"] from the name of the file
        # ["NumberOfReactions"] set to "0"
    """
    arg_6 = arg_3.ExcelWriter(arg_5, engine='xlsxwriter')

    for arg_7, arg_8 in arg_0.items():
        arg_8.to_excel(arg_6, sheet_name=arg_7, index=False)

    # Save excel
    arg_6.save()


def Func(arg_0: arg_1[arg_2, arg_3.DataFrame], arg_5: arg_2) -> None:
    """Export a SPIA data dictionary into a directory as several TSV documents."""
    os.makedirs(arg_5, exist_ok=True)
    for arg_6, arg_7 in arg_0.items():
        arg_7.to_csv(os.path.join(arg_5, f'{relation}.tsv'), index=True)


def Func(arg_0: arg_1,
                                      arg_2: arg_1 = 'Gene.symbol',
                                      arg_3: arg_1 = 'logFC',
                                      arg_4: arg_5[arg_6[[arg_7[arg_8]], arg_8]] = None,
                                      ) -> Mapping[arg_1, arg_8]:
    """Load and pre-process a differential gene expression data.

    :param path: The path to the CSV
    :param gene_symbol_column: The header of the gene symbol column in the data frame
    :param logfc_column: The header of the log-fold-change column in the data frame
    :param aggregator: A function that aggregates a list of differential gene expression values. Defaults to
                       :func:`numpy.median`. Could also use: :func:`numpy.mean`, :func:`numpy.average`,
                       :func:`numpy.min`, or :func:`numpy.max`
    :return: A dictionary of {gene symbol: log fold change}
    """
    if arg_4 is None:
        arg_4 = np.median

    # Load the data frame
    arg_9 = pd.read_csv(arg_0)

    # Check the columns exist in the data frame
    assert arg_2 in arg_9.columns
    assert arg_3 in arg_9.columns

    # throw away columns that don't have gene symbols - these represent control sequences
    arg_9 = arg_9.loc[arg_9[arg_2].notnull(), [arg_2, arg_3]]

    arg_10 = defaultdict(list)

    for arg_11, arg_12, arg_13 in arg_9.itertuples():
        arg_10[arg_12].append(arg_13)

    return {
        arg_12: arg_4(arg_14)
        for arg_12, arg_14 in arg_10.items()
    }


def Func(arg_0, arg_1, arg_2, arg_3, arg_4, arg_5,
                     arg_6, arg_7=None, arg_8=None, arg_9=None,
                     arg_10=None, arg_11=None, arg_12=None, arg_13=None,
                     arg_14=None, arg_15=None, arg_16=None, arg_17=None,
                     arg_18=True, arg_19='|', arg_20=True, arg_21=None, arg_22='',
                     arg_23=None, arg_24=True):
    """Merges namespaces from multiple locations to one.

    :param iter input_locations: An iterable of URLs or file paths pointing to BEL namespaces.
    :param str output_path: The path to the file to write the merged namespace
    :param str namespace_name: The namespace name
    :param str namespace_keyword: Preferred BEL Keyword, maximum length of 8
    :param str namespace_domain: One of: :data:`pybel.constants.NAMESPACE_DOMAIN_BIOPROCESS`,
                            :data:`pybel.constants.NAMESPACE_DOMAIN_CHEMICAL`,
                            :data:`pybel.constants.NAMESPACE_DOMAIN_GENE`, or
                            :data:`pybel.constants.NAMESPACE_DOMAIN_OTHER`
    :param str author_name: The namespace's authors
    :param str citation_name: The name of the citation
    :param str namespace_query_url: HTTP URL to query for details on namespace values (must be valid URL)
    :param str namespace_description: Namespace description
    :param str namespace_species: Comma-separated list of species taxonomy id's
    :param str namespace_version: Namespace version
    :param str namespace_created: Namespace public timestamp, ISO 8601 datetime
    :param str author_contact: Namespace author's contact info/email address
    :param str author_copyright: Namespace's copyright/license information
    :param str citation_description: Citation description
    :param str citation_url: URL to more citation information
    :param str citation_version: Citation version
    :param str citation_date: Citation publish timestamp, ISO 8601 Date
    :param bool case_sensitive: Should this config file be interpreted as case-sensitive?
    :param str delimiter: The delimiter between names and labels in this config file
    :param bool cacheable: Should this config file be cached?
    :param functions: The encoding for the elements in this namespace
    :type functions: iterable of characters
    :param str value_prefix: a prefix for each name
    :param sort_key: A function to sort the values with :func:`sorted`
    :param bool check_keywords: Should all the keywords be the same? Defaults to ``True``
    """
    arg_25 = get_merged_namespace_names(arg_0, arg_24=arg_24)

    with open(arg_1, 'w') as file:
        write_namespace(
            arg_2=arg_2,
            arg_3=arg_3,
            arg_4=arg_4,
            arg_5=arg_5,
            arg_6=arg_6,
            values=arg_25,
            arg_8=arg_8,
            arg_7=arg_7,
            arg_10=arg_10,
            arg_9=arg_9,
            arg_11=arg_11,
            arg_12=arg_12,
            arg_13=arg_13,
            arg_14=arg_14,
            arg_15=arg_15,
            arg_16=arg_16,
            arg_17=arg_17,
            arg_18=arg_18,
            arg_19=arg_19,
            arg_20=arg_20,
            arg_21=arg_21,
            arg_22=arg_22,
            arg_23=arg_23,
            file=file
        )


def Func(arg_0, arg_1='dgxp'):
    """Run the reverse causal reasoning algorithm on a graph.

    Steps:

    1. Get all downstream controlled things into map (that have at least 4 downstream things)
    2. calculate population of all things that are downstream controlled

    .. note:: Assumes all nodes have been pre-tagged with data

    :param pybel.BELGraph graph:
    :param str tag: The key for the nodes' data dictionaries that corresponds to the integer value for its differential
                    expression.
    """

    # Step 1: Calculate the hypothesis subnetworks (just simple star graphs)
    arg_2 = defaultdict(set)
    arg_3 = defaultdict(set)
    arg_4 = defaultdict(set)

    for arg_5, arg_6, arg_7 in arg_0.edges(data=True):
        arg_2[arg_5].add(arg_6)

        if arg_7[RELATION] in CAUSAL_INCREASE_RELATIONS:
            arg_3[arg_5].add(arg_6)

        elif arg_7[RELATION] in CAUSAL_DECREASE_RELATIONS:
            arg_4[arg_5].add(arg_6)

    # Step 2: Calculate the matching of the data points to the causal relationships

    #: A dictionary from {tuple controller node: int count of correctly matching observations}
    arg_8 = defaultdict(int)
    #: A dictionary from {tuple controller node: int count of incorrectly matching observations}
    arg_9 = defaultdict(int)
    #: A dictionary from {tuple controller node: int count of ambiguous observations}
    arg_10 = defaultdict(int)
    #: A dictionary from {tuple controller node: int count of missing obvservations}
    arg_11 = defaultdict(int)

    for arg_12, arg_13 in arg_2.items():
        if len(arg_13) < 4:
            continue  # need enough data to make reasonable calculations!

        for arg_14 in arg_13:

            if arg_14 in arg_3[arg_12] and arg_14 in arg_4[arg_12]:
                arg_10[arg_12] += 1

            elif arg_14 in arg_3[arg_12]:
                if arg_0.node[arg_14][arg_1] == 1:
                    arg_8[arg_12] += 1
                elif arg_0.node[arg_14][arg_1] == -1:
                    arg_9[arg_12] += 1

            elif arg_14 in arg_4[arg_12]:
                if arg_0.node[arg_14][arg_1] == 1:
                    arg_9[arg_12] += 1
                elif arg_0.node[arg_14][arg_1] == -1:
                    arg_8[arg_12] += 1

            else:
                arg_11[arg_12] += 1

    # Step 3: Keep only controller nodes who have 4 or more downstream nodes
    arg_15 = {
        arg_12
        for arg_12, arg_13 in arg_2.items()
        if 4 <= len(arg_13)
    }

    # Step 4: Calculate concordance scores
    arg_16 = {
        arg_12: scipy.stats.beta(0.5, arg_8[arg_12], arg_9[arg_12])
        for arg_12 in arg_15
    }

    # Step 5: Calculate richness scores
    # TODO

    # Calculate the population as the union of all downstream nodes for all controllers
    arg_17 = {
        arg_14
        for arg_12 in arg_15
        for arg_14 in arg_2[arg_12]
    }
    arg_18 = len(arg_17)

    # Step 6: Export

    return pandas.DataFrame({
        'contra': arg_9,
        'correct': arg_8,
        'concordance': arg_16
    })


def Func(arg_0, arg_1, arg_2=None, arg_3=False):
    """Exports all names and missing names from the given namespace to its own BEL Namespace files in the given
    directory.

    Could be useful during quick and dirty curation, where planned namespace building is not a priority.

    :param pybel.BELGraph graph: A BEL graph
    :param str namespace: The namespace to process
    :param str directory: The path to the directory where to output the namespace. Defaults to the current working
                      directory returned by :func:`os.getcwd`
    :param bool cacheable: Should the namespace be cacheable? Defaults to ``False`` because, in general, this operation
                        will probably be used for evil, and users won't want to reload their entire cache after each
                        iteration of curation.
    """
    arg_2 = os.getcwd() if arg_2 is None else arg_2
    arg_4 = os.path.join(arg_2, '{}.belns'.format(arg_1))

    with open(arg_4, 'w') as file:
        log.info('Outputting to %s', arg_4)
        arg_5 = get_names_by_namespace(arg_0, arg_1)
        log.info('Graph has %d correct names in %s', len(arg_5), arg_1)
        arg_6 = get_incorrect_names_by_namespace(arg_0, arg_1)
        log.info('Graph has %d incorrect names in %s', len(arg_5), arg_1)
        arg_7 = get_undefined_namespace_names(arg_0, arg_1)
        log.info('Graph has %d names in missing namespace %s', len(arg_5), arg_1)

        arg_8 = (arg_5 | arg_6 | arg_7)

        if 0 == len(arg_8):
            log.warning('%s is empty', arg_1)

        write_namespace(
            namespace_name=arg_1,
            namespace_keyword=arg_1,
            namespace_domain='Other',
            author_name=arg_0.authors,
            author_contact=arg_0.contact,
            citation_name=arg_0.name,
            values=arg_8,
            arg_3=arg_3,
            file=file
        )


def Func(arg_0, arg_1=None):
    """Helps remove extraneous whitespace from the lines of a file

    :param file in_file: A readable file or file-like
    :param file out_file: A writable file or file-like
    """
    for arg_2 in arg_0:
        print(arg_2.strip(), file=arg_1)


def Func(arg_0, arg_1):
    """Adds a linted version of each document in the source directory to the target directory

    :param str source: Path to directory to lint
    :param str target: Path to directory to output
    """
    for arg_2 in os.listdir(arg_0):
        if not arg_2.endswith('.bel'):
            continue

        log.info('linting: %s', arg_2)
        with open(os.path.join(arg_0, arg_2)) as i, open(os.path.join(arg_1, arg_2), 'w') as o:
            lint_file(i, o)


def Func(arg_0: arg_1[arg_2[arg_3, arg_4]]) -> arg_1[arg_3]:
    """Builds a skeleton for gene summaries

    :param entrez_ids: A list of Entrez Gene identifiers to query the PubMed service
    :return: An iterator over statement lines for NCBI Entrez Gene summaries
    """
    arg_5 = PUBMED_GENE_QUERY_URL.format(','.join(arg_3(arg_8).strip() for arg_8 in arg_0))
    arg_6 = requests.get(arg_5)
    arg_7 = ElementTree.fromstring(arg_6.content)

    for arg_8 in arg_7.findall('./DocumentSummarySet/DocumentSummary'):
        yield '\n# {}'.format(arg_8.find('Description').text)
        yield 'SET Citation = {{"Other", "PubMed Gene", "{}"}}'.format(arg_8.attrib['uid'])
        yield 'SET Evidence = "{}"'.format(arg_8.find('Summary').text.strip().replace('\n', ''))
        yield '\nUNSET Evidence\nUNSET Citation'