def _decorate_existing_edges(self):
        # This function decorates all existing edges in the knowledge graph with ICEES data, stored in EdgeAttributes
        knowledge_graph = self.message.knowledge_graph
        log = self.response

        # Query ICEES for each edge in the knowledge graph that ICEES can provide data on (use known curies)
        num_edges_obtained_icees_data_for = 0
        edges_by_node_pair = self._get_edges_by_node_pair(
            knowledge_graph)  # Don't duplicate effort for parallel edges
        for node_pair_key, node_pair_edges in edges_by_node_pair.items():
            subject_key = node_pair_edges[0].subject
            object_key = node_pair_edges[0].object
            accepted_subject_synonyms = self._get_accepted_synonyms(
                subject_key)
            accepted_object_synonyms = self._get_accepted_synonyms(object_key)
            if accepted_subject_synonyms and accepted_object_synonyms:
                # Query ICEES for each possible combination of accepted subject/object synonyms
                for subject_curie_to_try, object_curie_to_try in itertools.product(
                        accepted_subject_synonyms, accepted_object_synonyms):
                    qedge = QEdge(subject=subject_curie_to_try,
                                  object=object_curie_to_try)
                    qedge.id = f"icees_e00"
                    log.debug(
                        f"Sending query to ICEES+ for {subject_curie_to_try}--{object_curie_to_try}"
                    )
                    p_value = self._get_icees_p_value_for_edge(qedge, log)
                    if p_value is not None:
                        num_edges_obtained_icees_data_for += len(
                            node_pair_edges)
                        new_edge_attribute = self._create_icees_edge_attribute(
                            p_value)
                        # Add the data as new EdgeAttributes on the existing edges with this subject/object ID
                        for edge in node_pair_edges:
                            if not edge.attributes:
                                edge.attributes = []
                            edge.attributes.append(new_edge_attribute)
                        # Don't worry about checking remaining synonym combos if we got results
                        break

        if num_edges_obtained_icees_data_for:
            log.info(
                f"Overlayed {num_edges_obtained_icees_data_for} edges with exposures data from ICEES+"
            )
        else:
            log.warning(
                f"Could not find ICEES+ exposures data for any edges in the KG"
            )

        return self.response
    def _add_virtual_edges(self, subject_qnode_key, object_qnode_key):
        # This function adds ICEES exposures data as virtual edges between nodes with the specified qnode IDs
        knowledge_graph = self.message.knowledge_graph
        query_graph = self.message.query_graph
        log = self.response
        nodes_by_qg_id = self._get_nodes_by_qg_id(knowledge_graph)
        subject_curies = set(nodes_by_qg_id.get(subject_qnode_key))
        object_curies = set(nodes_by_qg_id.get(object_qnode_key))
        # Determine which curies ICEES 'knows' about
        known_subject_curies = {
            curie
            for curie in subject_curies if self._get_accepted_synonyms(curie)
        }
        known_object_curies = {
            curie
            for curie in object_curies if self._get_accepted_synonyms(curie)
        }

        num_node_pairs_recognized = 0
        for subject_curie, object_curie in ou.get_node_pairs_to_overlay(
                subject_qnode_key, object_qnode_key, query_graph,
                knowledge_graph, log):
            # Query ICEES only for synonyms it 'knows' about
            if subject_curie in known_subject_curies and object_curie in known_object_curies:
                accepted_subject_synonyms = self._get_accepted_synonyms(
                    subject_curie)
                accepted_object_synonyms = self._get_accepted_synonyms(
                    object_curie)
                for subject_synonym, object_synonym in itertools.product(
                        accepted_subject_synonyms, accepted_object_synonyms):
                    # qedge = QEdge(id=f"icees_{subject_synonym}--{object_synonym}",
                    #               subject_key=subject_synonym,
                    #               object_key=object_synonym)
                    qedge = QEdge(subject=subject_synonym,
                                  object=object_synonym)
                    qedge.id = f"icees_{subject_synonym}--{object_synonym}"
                    log.debug(
                        f"Sending query to ICEES+ for {subject_synonym}--{object_synonym}"
                    )
                    p_value = self._get_icees_p_value_for_edge(qedge, log)
                    if p_value is not None:
                        num_node_pairs_recognized += 1
                        # Add a new virtual edge with this data
                        id, virtual_edge = self._create_icees_virtual_edge(
                            subject_curie, object_curie, p_value)
                        old_id = id
                        while id in knowledge_graph.edges:
                            id = old_id + f".{random.randint(10**(9-1), (10**9)-1)}"
                        knowledge_graph.edges[id] = virtual_edge
                        break  # Don't worry about checking remaining synonym combos if we got results
            # Add an 'empty' virtual edge (p-value of None) if we couldn't find any results for this node pair #1009
            id, empty_virtual_edge = self._create_icees_virtual_edge(
                subject_curie, object_curie, None)
            while id in knowledge_graph.edges:
                id = old_id + f".{random.randint(10**(9-1), (10**9)-1)}"
            knowledge_graph.edges[id] = empty_virtual_edge

        # Add a qedge to the query graph that corresponds to our new virtual edges
        # new_qedge = QEdge(id=self.virtual_relation_label,
        #                   subject_key=subject_qnode_key,
        #                   object_key=object_qnode_key,
        #                   type=self.icees_edge_type,
        #                   option_group_id=ou.determine_virtual_qedge_option_group(subject_qnode_key, object_qnode_key,
        #                                                                           query_graph, log))
        # Likely need to change this for TRAPI 1.0
        new_qedge = QEdge(
            subject=subject_qnode_key,
            object=object_qnode_key,
            predicate=self.icees_edge_type,
            option_group_id=ou.determine_virtual_qedge_option_group(
                subject_qnode_key, object_qnode_key, query_graph, log))
        query_graph.edges[self.virtual_relation_label] = new_qedge

        if num_node_pairs_recognized:
            log.info(
                f"ICEES+ returned data for {num_node_pairs_recognized} node pairs"
            )
        else:
            log.warning(
                f"Could not find ICEES+ exposures data for any {subject_qnode_key}--{object_qnode_key} node pairs"
            )