<gh_stars>10-100
import bisect
import subprocess
import numpy as np
import time
import os
try:
    import ujson as json
except:
    import json
from functools import partial
from itertools import chain
from pprint import pprint
from copy import deepcopy
from aser.extract.discourse_parser import ConnectiveExtractor, ArgumentPositionClassifier, \
    SSArgumentExtractor, PSArgumentExtractor, ExplicitSenseClassifier
from aser.eventuality import Eventuality
from aser.relation import Relation, relation_senses
from aser.extract.rule import SEED_CONNECTIVE_DICT
from aser.extract.utils import EMPTY_SENT_PARSED_RESULT


class BaseRelationExtractor(object):
    def __init__(self, **kw):
        pass

    def close(self):
        pass

    def __del__(self):
        self.close()

    def extract_from_parsed_result(self, parsed_result, para_eventualities, output_format="Relation", in_order=True, **kw):
        """ This method extracts relations among extracted eventualities.

        :type parsed_result: list
        :type para_eventualities: list
        :type output_format: str
        :type in_order bool
        :param parsed_result: a list of dicts generated by `aser.extract.utils.parse_sentense_with_stanford`
        :param para_eventualities: a list of lists of `Eventuality` objects
        :param output_format: the specific output format
        :param in_order: in order or out of order
        :return: a list of lists of `Relation` objects, or a list of lists of triples

        .. highlight:: python
        .. code-block:: python

            Input:
                [
                    {'text': 'The dog barks loudly because it is hungry.',
                    'dependencies': [(1, 'det', 0),
                                     (2, 'nsubj', 1),
                                     (2, 'advmod', 3),
                                     (2, 'punct', 8),
                                     (3, 'dep', 7),
                                     (7, 'mark', 4),
                                     (7, 'nsubj', 5),
                                     (7, 'cop', 6)],
                    'tokens': ['The', 'dog', 'barks', 'loudly', 'because', 'it', 'is', 'hungry', '.'],
                    'pos_tags': ['DT', 'NN', 'VBZ', 'RB', 'IN', 'PRP', 'VBZ', 'JJ', '.'],
                    'lemmas': ['the', 'dog', 'bark', 'loudly', 'because', 'it', 'be', 'hungry', '.'],
                    'ners': ['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O', 'O'],
                    'mentions': [],
                    'parse': '(ROOT (S (NP (DT The) (NN dog)) (VP (VBZ barks) (ADVP (RB loudly) (SBAR (IN because) (S (NP (PRP it)) (VP (VBZ is) (ADJP (JJ hungry))))))) (. .)))'},
                    {'text': 'The dog barks loudly because',
                    'dependencies': [(4, 'cc', 0),
                                     (4, 'nsubj', 1),
                                     (4, 'aux', 2),
                                     (4, 'neg', 3),
                                     (4, 'dobj', 6),
                                     (4, 'punct', 7),
                                     (6, 'compound', 5)],
                    'tokens': ['But', 'we', 'do', "n't", 'have', 'food', 'left', '.'],
                    'pos_tags': ['CC', 'PRP', 'VBP', 'RB', 'VB', 'NN', 'NN', '.'],
                    'lemmas': ['but', 'we', 'do', 'not', 'have', 'food', 'left', '.'],
                    'ners': ['O', 'O', 'O', 'O', 'O', 'O', 'O', 'O'],
                    'mentions': [],
                    'parse': "(ROOT (S (CC But) (NP (PRP we)) (VP (VBP do) (RB n't) (VP (VB have) (NP (NN food) (NN left)))) (. .)))"}],
                [
                    [
                        Eventuality(
                            {'dependencies': [((1, 'dog', 'NN'), 'det', (0, 'the', 'DT')),
                                              ((2, 'bark', 'VBZ'), 'nsubj', (1, 'dog', 'NN')),
                                              ((2, 'bark', 'VBZ'), 'advmod', (3, 'loudly', 'RB'))],
                            'eid': 'b51425727182a0d25734a92ae16a456cb5e6351f',
                            'frequency': 1.0,
                            'mentions': {},
                            'ners': ['O', 'O', 'O', 'O'],
                            'pattern': 's-v',
                            'pos_tags': ['DT', 'NN', 'VBZ', 'RB'],
                            'skeleton_dependencies': [((2, 'bark', 'VBZ'), 'nsubj', (1, 'dog', 'NN'))],
                            'skeleton_words': ['dog', 'bark'],
                            'verbs': ['bark'],
                            'words': ['the', 'dog', 'bark', 'loudly']}),
                        Eventuality(
                            {'dependencies': [((2, 'hungry', 'JJ'), 'nsubj', (0, 'it', 'PRP')),
                                              ((2, 'hungry', 'JJ'), 'cop', (1, 'be', 'VBZ'))],
                            'eid': '8fbd35fcb293f526b54c5989969251d6a31e4893',
                            'frequency': 1.0,
                            'mentions': {},
                            'ners': ['O', 'O', 'O'],
                            'pattern': 's-be-a',
                            'pos_tags': ['PRP', 'VBZ', 'JJ'],
                            'skeleton_dependencies': [((2, 'hungry', 'JJ'), 'nsubj', (0, 'it', 'PRP')),
                                                    ((2, 'hungry', 'JJ'), 'cop', (1, 'be', 'VBZ'))],
                            'skeleton_words': ['it', 'be', 'hungry'],
                            'verbs': ['be'],
                            'words': ['it', 'be', 'hungry']})],
                    [
                        Eventuality(
                            {'dependencies': [((3, 'have', 'VB'), 'nsubj', (0, 'we', 'PRP')),
                                              ((3, 'have', 'VB'), 'aux', (1, 'do', 'VBP')),
                                              ((3, 'have', 'VB'), 'neg', (2, 'not', 'RB')),
                                              ((3, 'have', 'VB'), 'dobj', (5, 'left', 'NN')),
                                              ((5, 'left', 'NN'), 'compound', (4, 'food', 'NN'))],
                            'eid': '32bd10b7e116f7656b7424d3f3a47dab230d52de',
                            'frequency': 1.0,
                            'mentions': {},
                            'ners': ['O', 'O', 'O', 'O', 'O', 'O'],
                            'pattern': 's-v-o',
                            'pos_tags': ['PRP', 'VBP', 'RB', 'VB', 'NN', 'NN'],
                            'skeleton_dependencies': [((3, 'have', 'VB'), 'nsubj', (0, 'we', 'PRP')),
                                                    ((3, 'have', 'VB'), 'dobj', (5, 'left', 'NN'))],
                            'skeleton_words': ['we', 'have', 'left'],
                            'verbs': ['do', 'have'],
                            'words': ['we', 'do', 'not', 'have', 'food', 'left']})]],
                "Relation",
                True
            Output:
                [
                    [
                        Relation(
                            {'rid': 'fcdfcabe07446e8e0ba7950016a300ce00d1e4b9',
                            'hid': '8fbd35fcb293f526b54c5989969251d6a31e4893',
                            'tid': 'b51425727182a0d25734a92ae16a456cb5e6351f'}
                            'relations': "{'Co_Occurrence': 1.0}")],
                    [],
                    [
                        Relation(
                            {'rid': 'd75f64002dc742fdbadbc0465dbde50c22facf67',
                            'hid': '8fbd35fcb293f526b54c5989969251d6a31e4893',
                            'tid': '32bd10b7e116f7656b7424d3f3a47dab230d52de',
                            'relations': "{'Contrast': 1.0}",}),
                        Relation(
                            {'rid': '53edca79788c6df43212c034353305061c2619d3',
                            'hid': 'b51425727182a0d25734a92ae16a456cb5e6351f',
                            'tid': '32bd10b7e116f7656b7424d3f3a47dab230d52de',
                            'relations': "{'Contrast': 1.0}",})]]
        """
        raise NotImplementedError

class SeedRuleRelationExtractor(BaseRelationExtractor):
    def __init__(self, **kw):
        super().__init__(**kw)

    def extract_from_parsed_result(self, parsed_result, para_eventualities, output_format="Relation", in_order=True, **kw):
        if output_format not in ["Relation", "triple"]:
            raise NotImplementedError("Error: extract_from_parsed_result only supports Relation or triple.")

        connective_dict = kw.get("connective_dict", SEED_CONNECTIVE_DICT)

        para_relations = list()
        for sent_parsed_result, eventualities in zip(parsed_result, para_eventualities):
            relations_in_sent = list()
            for head_eventuality in eventualities:
                for tail_eventuality in eventualities:
                    if not head_eventuality.position < tail_eventuality.position:
                        continue
                    heid = head_eventuality.eid
                    teid = tail_eventuality.eid
                    extracted_senses = self._extract_from_eventuality_pair_in_one_sentence(
                        connective_dict, sent_parsed_result, head_eventuality, tail_eventuality)
                    if len(extracted_senses) > 0:
                        relations_in_sent.append(Relation(heid, teid, extracted_senses))
            para_relations.append(relations_in_sent)

        for i in range(len(parsed_result) - 1):
            eventualities1, eventualities2 = para_eventualities[i], para_eventualities[i+1]
            relations_between_sents = list()
            if len(eventualities1) == 1 and len(eventualities2) == 1:
                s1_tokens, s2_tokens = parsed_result[i]["tokens"], parsed_result[i+1]["tokens"]
                s1_eventuality, s2_eventuality = eventualities1[0], eventualities2[0]
                heid, teid = s1_eventuality.eid, s2_eventuality.eid
                extracted_senses = self._extract_from_eventuality_pair_in_two_sentence(
                    connective_dict, s1_eventuality, s2_eventuality, s1_tokens, s2_tokens)
                if len(extracted_senses) > 0:
                    relations_between_sents.append(Relation(heid, teid, extracted_senses))
            para_relations.append(relations_between_sents)

        if in_order:
            if output_format == "Relation":
                return para_relations
            elif output_format == "triple":
                return [sorted(chain.from_iterable([r.to_triples() for r in relations])) \
                    for relations in para_relations]
        else:
            if output_format == "Relation":
                rid2relation = dict()
                for relation in chain(*para_relations):
                    if relation.rid not in rid2relation:
                        rid2relation[relation.rid] = deeocopy(relation)
                    else:
                        rid2relation[relation.rid].update(relation)
                return sorted(rid2relation.values(), key=lambda r: r.rid)
            if output_format == "triple":
                return sorted([r.to_triples() for relations in para_relations for r in relations])
            

    def _extract_from_eventuality_pair_in_one_sentence(self, connective_dict, sent_parsed_result, head_eventuality, tail_eventuality):
        extracted_senses = ['Co_Occurrence']
        for sense in relation_senses:
            for connective_words in connective_dict[sense]:
                if self._verify_connective_in_one_sentence(
                    connective_words, head_eventuality, tail_eventuality,
                    sent_parsed_result["dependencies"],
                    sent_parsed_result["tokens"]):
                    extracted_senses.append(sense)
                    break
        return extracted_senses

    def _extract_from_eventuality_pair_in_two_sentence(self, connective_dict, s1_eventuality, s2_eventuality, s1_tokens, s2_tokens):
        extracted_senses = list()
        for sense in relation_senses:
            for connective_words in connective_dict[sense]:
                if self._verify_connective_in_two_sentence(connective_words, s1_eventuality, s2_eventuality, s1_tokens, s2_tokens):
                    extracted_senses.append(sense)
                    break

        return extracted_senses

    def _verify_connective_in_one_sentence(self, connective_words, head_eventuality, tail_eventuality, sentence_dependencies, sentence_tokens):
        def get_connective_position(connective_words):
            tmp_positions = list()
            for w in connective_words:
                tmp_positions.append(sentence_tokens.index(w))
            return sum(tmp_positions) / len(tmp_positions) if tmp_positions else 0.0
        # Connective Words need to be presented in sentence
        if set(connective_words) - set(sentence_tokens):
            return False
        # Connective phrase need to be presented in sentence
        connective_string = " ".join(connective_words)
        sentence_string = " ".join(sentence_tokens)
        if connective_string not in sentence_string:
            return False
        shrinked_dependencies = self._shrink_sentence_dependencies(
            head_eventuality._raw_dependencies,
            tail_eventuality._raw_dependencies,
            sentence_dependencies)
        if not shrinked_dependencies:
            return False
        found_advcl = False
        for (governor, dep, dependent) in shrinked_dependencies:
            if governor == '_H_' and dependent == '_T_' and 'advcl' in dep:
                found_advcl = True
                break
        if not found_advcl:
            return False
        connective_position = get_connective_position(connective_words)
        e1_position, e2_position = head_eventuality.position, tail_eventuality.position
        if 'instead' not in connective_words:
            if e1_position < connective_position < e2_position:
                return True
            else:
                return False
        else:
            if e1_position < e2_position < connective_position:
                return True
            else:
                return False


    def _verify_connective_in_two_sentence(self, connective_words, s1_eventuality, s2_eventuality, s1_tokens, s2_tokens):
        def get_connective_position():
            tmp_positions = list()
            for w in connective_words:
                if w in s1_tokens:
                    tmp_positions.append(s1_tokens.index(w))
                elif w in s2_tokens:
                        tmp_positions.append(s2_tokens.index(w) + len(s1_tokens))
            return sum(tmp_positions) / len(tmp_positions) if tmp_positions else 0.0
        sentence_tokens = s1_tokens + s2_tokens
        # Connective Words need to be presented in sentence
        if set(connective_words) - set(sentence_tokens):
            return False
        # Connective phrase need to be presented in sentence
        connective_string = " ".join(connective_words)
        sentence_string = " ".join(sentence_tokens)
        if connective_string not in sentence_string:
            return False
        connective_position = get_connective_position()
        e1_position, e2_position = s1_eventuality.position, \
                                   s2_eventuality.position + len(s1_tokens)
        if 'instead' not in connective_words:
            if e1_position < connective_position < e2_position and e2_position - e1_position < 10:
                return True
            else:
                return False
        else:
            if e1_position < e2_position < connective_position and e2_position - e1_position < 10:
                return True
            else:
                return False


    def _shrink_sentence_dependencies(self, head_dependencies, tail_dependencies,
                                      sentence_dependencies):
        head_nodes = set()
        for governor, _, dependent in head_dependencies:
            head_nodes.add(governor)
            head_nodes.add(dependent)
        tail_nodes = set()
        for governor, _, dependent in tail_dependencies:
            tail_nodes.add(governor)
            tail_nodes.add(dependent)
        if head_nodes & tail_nodes:
            return None

        new_dependencies = list()
        for governor, dep, dependent in sentence_dependencies:
            if governor in head_nodes:
                new_governor = '_H_'
            elif governor in tail_nodes:
                new_governor = '_T_'
            else:
                new_governor = governor
            if dependent in head_nodes:
                new_dependent = '_H_'
            elif dependent in tail_nodes:
                new_dependent = '_T_'
            else:
                new_dependent = dependent
            if new_governor != new_dependent:
                new_dependencies.append((new_governor, dep, new_dependent))
        return new_dependencies


class DiscourseRelationExtractor(BaseRelationExtractor):
    def __init__(self, **kw):
        super().__init__(**kw)
        self.conn_extractor = ConnectiveExtractor(**kw)
        self.argpos_classifier = ArgumentPositionClassifier(**kw)
        self.ss_extractor = SSArgumentExtractor(**kw)
        self.ps_extractor = PSArgumentExtractor(**kw)
        self.explicit_classifier = ExplicitSenseClassifier(**kw)

    def extract_from_parsed_result(self, parsed_result, para_eventualities, output_format="triple", in_order=False, **kw):
        if output_format not in ["Relation", "triple"]:
            raise NotImplementedError("Error: extract_from_parsed_result only supports Relation or triple.")
        
        similarity = kw.get("similarity", "simpson").lower()
        threshold = kw.get("threshold", 0.8)
        if threshold < 0.0 or threshold > 1.0:
            raise ValueError("Error: threshold should be between 0.0 and 1.0.")
        if similarity == "simpson":
            similarity_func = self._match_argument_eventuality_by_Simpson
        elif similarity == "jaccard":
            similarity_func = self._match_argument_eventuality_by_Jaccard
        elif similarity == "discourse":
            similarity_func = self._match_argument_eventuality_by_dependencies
        else:
            raise NotImplementedError("Error: extract_from_parsed_result only supports Simpson or Jaccard.")

        syntax_tree_cache = kw.get("syntax_tree_cache", dict())

        len_sentences = len(parsed_result)
        if len_sentences == 0:
            if in_order:
                return [list()]
            else:
                return list()

        para_relations = [list() for _ in range(2*len_sentences-1)]

        # replace sentences that contains no eventuality with empty sentences
        filtered_parsed_result = list()
        for sent_idx, (sent_parsed_result, sent_eventualities) in enumerate(zip(parsed_result, para_eventualities)):
            if len(sent_eventualities) > 0:
                filtered_parsed_result.append(sent_parsed_result)
                relations_in_sent = para_relations[sent_idx]
                for head_e in sent_eventualities:
                    heid = head_e.eid
                    for tail_e in sent_eventualities:
                        if not head_e.position < tail_e.position:
                            continue
                        teid = tail_e.eid
                        relations_in_sent.append(Relation(heid, teid, ["Co_Occurrence"]))
            else:
                filtered_parsed_result.append(EMPTY_SENT_PARSED_RESULT) # empty sentence
                # filtered_parsed_result.append(sent_parsed_result)

        connectives = self.conn_extractor.extract(filtered_parsed_result, syntax_tree_cache)
        SS_connectives, PS_connectives = self.argpos_classifier.classify(filtered_parsed_result, connectives, syntax_tree_cache)
        SS_connectives = self.ss_extractor.extract(filtered_parsed_result, SS_connectives, syntax_tree_cache)
        PS_connectives = self.ps_extractor.extract(filtered_parsed_result, PS_connectives, syntax_tree_cache)
        connectives = self.explicit_classifier.classify(filtered_parsed_result, SS_connectives+PS_connectives, syntax_tree_cache)
        connectives.sort(key=lambda x: (x["sent_idx"], x["indices"][0] if len(x["indices"]) > 0 else -1))
        
        # For CoNLL share task 2015
        # sent_offset = 0
        # for sent_parsed_result in parsed_result:
        #     sent_parsed_result["sentence_offset"] = sent_offset
        #     sent_offset += len(sent_parsed_result["tokens"])
        # with open("aser.json", "a") as f:
        #     for conn_idx, connective in enumerate(connectives):
        #         sense = connective.get("sense", None)
        #         arg1 = connective.get("arg1", None)
        #         arg2 = connective.get("arg2", None)
        #         if arg1 and arg2 and sense and sense != "None":
        #             x = {
        #                 "DocID": parsed_result[0]["doc"], 
        #                 "ID": conn_idx, 
        #                 "Connective": {
        #                     "RawText": connective["connective"],
        #                     "TokenList": [i+parsed_result[connective["sent_idx"]]["sentence_offset"] for i in connective["indices"]],
        #                     "Tokens": [parsed_result[connective["sent_idx"]]["tokens"][i] for i in connective["indices"]]},
        #                 "Arg1": {
        #                     "RawText": " ".join([parsed_result[arg1["sent_idx"]]["tokens"][i] for i in arg1["indices"]]),
        #                     "TokenList": [i+parsed_result[arg1["sent_idx"]]["sentence_offset"] for i in arg1["indices"]],
        #                     "Tokens": [parsed_result[arg1["sent_idx"]]["tokens"][i] for i in arg1["indices"]]},
        #                 "Arg2": {
        #                     "RawText": " ".join([parsed_result[arg2["sent_idx"]]["tokens"][i] for i in arg2["indices"]]),
        #                     "TokenList": [i+parsed_result[arg2["sent_idx"]]["sentence_offset"] for i in arg2["indices"]],
        #                     "Tokens": [parsed_result[arg2["sent_idx"]]["tokens"][i] for i in arg2["indices"]]},
        #                 "Type": "Explicit",
        #                 "Sense": [connective["sense"]]}
        #             f.write(json.dumps(x))
        #             f.write("\n")

        for connective in connectives:
            conn_indices = connective.get("indices", None)
            arg1 = connective.get("arg1", None)
            arg2 = connective.get("arg2", None)
            sense = connective.get("sense", None)
            if conn_indices and arg1 and arg2 and (sense and sense != "None"):
                arg1_sent_idx = arg1["sent_idx"]
                arg2_sent_idx = arg2["sent_idx"]
                relation_list_idx = arg1_sent_idx if arg1_sent_idx == arg2_sent_idx else arg1_sent_idx + len_sentences
                relations = para_relations[relation_list_idx]
                sent_parsed_result1, sent_eventualities1 = parsed_result[arg1_sent_idx], para_eventualities[arg1_sent_idx]
                sent_parsed_result2, sent_eventualities2 = parsed_result[arg2_sent_idx], para_eventualities[arg2_sent_idx]
                arg1_eventualities = [e for e in sent_eventualities1 if \
                    similarity_func(sent_parsed_result1, arg1, e, threshold=threshold, conn_indices=conn_indices)]
                arg2_eventualities = [e for e in sent_eventualities2 if \
                    similarity_func(sent_parsed_result2, arg2, e, threshold=threshold, conn_indices=conn_indices)]
                cnt = 0.0
                if len(arg1_eventualities) > 0 and len(arg2_eventualities) > 0:
                    cnt = 1.0 / (len(arg1_eventualities) * len(arg2_eventualities))
                for e1 in arg1_eventualities:
                    heid = e1.eid
                    for e2 in arg2_eventualities:
                        teid = e2.eid
                        is_existed = False
                        for relation in relations:
                            if relation.hid == heid and relation.tid == teid:
                                relation.update({sense: cnt})
                                is_existed = True
                                break
                        if not is_existed:
                            relations.append(Relation(heid, teid, {sense: cnt}))

        if in_order:
            if output_format == "Relation":
                return para_relations
            elif output_format == "triple":
                return [sorted(chain.from_iterable([r.to_triples() for r in relations])) \
                    for relations in para_relations]
        else:
            if output_format == "Relation":
                rid2relation = dict()
                for relation in chain(*para_relations):
                    if relation.rid not in rid2relation:
                        rid2relation[relation.rid] = deeocopy(relation)
                    else:
                        rid2relation[relation.rid].update(relation)
                return sorted(rid2relation.values(), key=lambda r: r.rid)
            if output_format == "triple":
                return sorted([r.to_triples() for relations in para_relations for r in relations])

    @staticmethod
    def _match_argument_eventuality_by_Simpson(sent_parsed_result, argument, eventuality, **kw):
        threshold = kw.get("threshold", 0.8)
        match = False
        if eventuality.raw_sent_mapping:
            argument_indices = set(argument["indices"])
            event_indices = set(eventuality.raw_sent_mapping.values())
            try:
                Simpson = len(argument_indices & event_indices) / min(len(argument_indices), len(event_indices))
                match = Simpson >= threshold
            except ZeroDivisionError:
                match = False
        else:
            argument_tokens = set([sent_parsed_result["lemmas"][idx].lower() for idx in argument["indices"]])
            event_tokens = set(eventuality.words)
            try:
                Simpson = len(argument_tokens & event_tokens) / min(len(argument_tokens), len(event_tokens))
                match = Simpson >= threshold
            except ZeroDivisionError:
                match = False
        return match
    
    @staticmethod
    def _match_argument_eventuality_by_Jaccard(sent_parsed_result, argument, eventuality, **kw):
        threshold = kw.get("threshold", 0.8)
        match = False
        if eventuality.raw_sent_mapping:
            argument_indices = set(argument["indices"])
            event_indices = set(eventuality.raw_sent_mapping.values())
            try:
                Jaccard = len(argument_indices & event_indices) / len(argument_indices | event_indices)
                match = Jaccard >= threshold
            except ZeroDivisionError:
                match = False
        else:
            argument_tokens = set([sent_parsed_result["lemmas"][idx].lower() for idx in argument["indices"]])
            event_tokens = set(eventuality.words)
            try:
                Jaccard = len(argument_tokens & event_tokens) / len(argument_tokens | event_tokens)
                match = Jaccard >= threshold
            except ZeroDivisionError:
                match = False
        return match

    @staticmethod
    def _match_argument_eventuality_by_dependencies(sent_parsed_result, argument, eventuality, **kw):
        conn_indices = kw.get("conn_indices", list())
        match = False
        conn_indices = set(conn_indices)
        if eventuality.raw_sent_mapping:
            argument_indices = set(argument["indices"])
            event_indices = set(eventuality.raw_sent_mapping.values())

            for (governor, dep, dependent) in sent_parsed_result["dependencies"]:
                # find the word linked to the connective
                if dependent in conn_indices and governor in argument_indices and governor in event_indices:
                    match = True
                    break
                elif governor in conn_indices and dependent in argument_indices and dependent in event_indices:
                    match = True
                    break
        else:
            argument_tokens = set([sent_parsed_result["lemmas"][idx].lower() for idx in argument["indices"]])
            event_token_pos_tags = set(zip(eventuality.words, eventuality.pos_tags))

            argument_indices = set(argument["indices"])
            for (governor, dep, dependent) in sent_parsed_result["dependencies"]:
                # find the word linked to the connective
                if dependent in conn_indices and governor in argument_indices:
                    token_pos_tag = (sent_parsed_result["lemmas"][governor].lower(), sent_parsed_result["pos_tags"][governor])
                    if token_pos_tag in event_token_pos_tags:
                        match = True
                        break
                elif governor in conn_indices and dependent in argument_indices:
                    token_pos_tag = (sent_parsed_result["lemmas"][dependent].lower(), sent_parsed_result["pos_tags"][dependent])
                    if token_pos_tag in event_token_pos_tags:
                        match = True
                        break
        return match
            

class NeuralRelationExtractor(BaseRelationExtractor):
    def __init__(self, **kw):
        super().__init__(kw)
        raise NotImplementedError

    def extract(self, eventualtity_pair):
        raise NotImplementedError