import nltk
nltk.download()

"""
Explanation: Übungsblatt 10
Präsenzaufgaben
Aufgabe 1 &nbsp;&nbsp;&nbsp; Herunterladen von Ressourcen
Laden Sie sich zunächst die Ressource corpora/treebank über den NLTK Download-Manager herunter.
End of explanation
"""


cfg = """
S -> NP VP
VP -> V NP PP
VP -> V NP
NP -> DET N
NP -> DET N PP
PP -> P NP

DET -> "the" | "a"
N -> "boy" | "woman" | "telescope"
V -> "saw"
P -> "with"
"""

"""
Explanation: Aufgabe 2 &nbsp;&nbsp;&nbsp; Von Daten zu Regelwahrscheinlichkeiten
Gegeben sei folgende kontextfreie Grammatik:
End of explanation
"""


from collections import defaultdict
from nltk.corpus import treebank

def find_relevant_constructions(lhs, only_with=None):
    lhs_nt = nltk.grammar.Nonterminal(lhs)
    should_filter = only_with is not None
    if should_filter:
        filter_by = list(map(nltk.grammar.Nonterminal, only_with))
        def passes_filter(tup):
            for f in filter_by:
                if f not in tup:
                    return False
            return True

    # iterate over all trees and all their productions
    # and count those which pass the filter
    counter = defaultdict(int)
    for tree in treebank.parsed_sents():
        for prod in tree.productions():
            if prod.lhs() == lhs_nt:
                # either we do not have to filter or the rhs has to pass the filter
                if not should_filter or passes_filter(prod.rhs()):
                    counter[prod.rhs()] += 1

    # sort right sides of productions according to frequency
    return [ (k, counter[k]) for k in sorted(counter.keys(), key=counter.__getitem__) ]

constructions = find_relevant_constructions('VP', only_with=["NP"])
constructions[-30:]

"""
Explanation: Sie modelliert sehr einfache Sätze der Form SBJ saw OBJ mit optionaler Präpositionalphrase am Ende. Diese Präpositionalphrase kann entweder der näheren Bestimmung des Objekts oder der näheren Bestimmung der in der Verbalphrase ausgedrückten Handlung dienen.
Im folgenden sollen aus der Penn Treebank Wahrscheinlichkeiten für die einzelnen Regeln extrahiert werden, um dieser Ambiguität Herr zu werden.
(a) Relevante Konstruktionen erkennen
Nutzen das im NLTK enthaltene Sample der Penn Treebank (nach Installation unter nltk.corpus.treebank zu finden) zunächst zur Identifikation der für eine Disambiguierung nützlichen (Teil-)bäume der Penn Treebank.
Gegebenfalls müssen Sie die obige Grammatik den Strukturen, die Sie finden, anpassen.
Hinweis: Sie können sich bei der Analyse auf die jeweils 30 häufigsten Konstruktionen der Baumbank beschränken.
End of explanation
"""


# the following are numbers counted from iteratively executing the previous code cell
# with different parameters for find_relevant_constructions
vp_with_pp_frq = 170 + 34 + 24 + 11 + 4 + 4
vp_without_frq = 805 + 378 + 375 + 261 + 250 + 185
vp_with_pp = vp_with_pp_frq / (vp_with_pp_frq + vp_without_frq)
vp_without = vp_without_frq / (vp_with_pp_frq + vp_without_frq)

np_with_pp_frq = 2188
np_without_frq = 2020
np_with_pp = np_with_pp_frq / (np_with_pp_frq + np_without_frq)
np_without = np_without_frq / (np_with_pp_frq + np_without_frq)

det_the_frq = 4038
det_a_frq = 1874
det_the = det_the_frq / (det_the_frq + det_a_frq)
det_a = det_a_frq / (det_the_frq + det_a_frq)

(vp_with_pp, vp_without, np_with_pp, np_without, det_the, det_a)

"""
Explanation: Zählen Sie nun, wie oft die jeweiligen Konstruktionen in der Penn Treebank vorkommen und berechnen Sie die relativen Häufigkeiten als Approximation der Regelwahrscheinlichkeiten. Das Vorgehen wird in folgender Formel veranschaulicht:
$$P(V, N!P, P!P \mid V!P) = \dfrac{count(V!P \rightarrow V\:N!P\:P!P)}{count(V!P \rightarrow \setminus*)}$$
End of explanation
"""


# {} are placeholders that are filled by the .format method
pcfg = """
S -> NP VP     [1.0]
VP -> V NP PP  [{}]
VP -> V NP     [{}]
NP -> DET N    [{}]
NP -> NP PP [{}]
PP -> P NP     [1.0]

DET -> "the"     [{}]
DET -> "a"       [{}]
N -> "boy"       [0.4]
N -> "woman"     [0.4]
N -> "telescope" [0.2]
V -> "saw"       [1.0]
P -> "with"      [1.0]
""".format(
    vp_with_pp, vp_without, np_without,
    np_with_pp, det_the, det_a
)
grammar = nltk.PCFG.fromstring(pcfg)

from IPython.display import display

parser = nltk.ViterbiParser(grammar)
for tree in parser.parse("the boy saw a woman with a telescope".split()):
    display(tree)

"""
Explanation: (b) Erstellen einer PCFG
Die aus den Daten extrahierten relativen Häufigkeiten sollen nun zur Erstellung einer probabilistischen kontextfreien Grammatik (PCFG)  genutzt werden.
End of explanation
"""


pos_examples = [
    "Er beabsichtigt , den Kuchen ganz alleine zu essen .",
    "Er behauptet , ihn gesehen zu haben ."
]
neg_examples = [
    "Er glaubt , nach Hause zu fliegen .",
    "Zu fliegen ist schön .",
    "Er will gehen ."
]

"""
Explanation: Wenn Sie sich die extrahierten Wahrscheinlichkeiten und das disambiguierte Ergebnis ansehen, überrascht Sie dann das Ergebnis der Syntaxanalyse?
Aufgabe 3 &nbsp;&nbsp;&nbsp; Weiterverarbeitung syntaktischer Analysen
In dieser Aufgabe sollen Sie die Ausgaben eines state-of-the-art-Parsers, nämlich des Stanford Parsers, weiterverarbeiten.
Mit dem Ziel, Sie erst einmal mit den typischen Strukturen einer solchen Aufgabe vertraut zu machen, sollen Sie in dieser Aufgabe lediglich entscheiden, ob die Eingabe einen Infinitivsatz mit Objekt enthält.
Zur Klarheit betrachten Sie die folgenden positiven und negativen Beispiele:
End of explanation
"""


from nltk.parse.stanford import StanfordParser

PATH_TO_CORE = r"/pfad/zu/stanford-corenlp-full-2017-06-09"
PATH_TO_GERMAN_MODEL = r"/pfad/zu/deutschem/modell"
jar = PATH_TO_CORE + '/' + "stanford-corenlp-3.8.0.jar"
model = PATH_TO_GERMAN_MODEL + '/' + "stanford-german-corenlp-2017-06-09-models.jar"

"""
Explanation: Zur Erinnerung die wichtigsten Schritte zur Nutzung des Stanford Parsers im NLTK:
Initialisierung:
End of explanation
"""


parser = StanfordParser(
    jar, model,
    model_path="edu/stanford/nlp/models/lexparser/germanPCFG.ser.gz"
)

"""
Explanation: Parser-Erstellung:
End of explanation
"""


for sentence in pos_examples + neg_examples:
    tree_list = list(parser.raw_parse(sentence))
    tree_list[0].pretty_print(unicodelines=True)

def classify(sentence):
    tree = list(parser.raw_parse(sentence))[0]
    # find all the subtrees that are labeled as 'VP'
    for vp in tree.subtrees(filter=lambda t: t.label() == 'VP'):
        # can we find a subtree in the VP that is an infinitiv ?
        has_infs = list(vp.subtrees(filter=lambda t: t.label().endswith("INF")))
        # can we find a subtree in the VP that is an NP ?
        has_nps = list(vp.subtrees(filter=lambda t: t.label() == 'NP'))
        # if we have found both: we have a hit !
        if has_infs and has_nps:
            return True
    # obviously we did not find anything !
    return False

"""
Explanation: Parsen:
End of explanation
"""


for p in pos_examples:
    print(classify(p))
for n in neg_examples:
    print(classify(n))

"""
Explanation: Die Ausgabe sollte sein:
True
True
False
False
False
End of explanation
"""


