1 """Evaluation tools for evaluating the parsing process.
2
3 Once upon a time, this was a handy interface to
4 supertagging and parsing that made it easy to write evaluation routines.
5 Nowadays, the main parse script is much more capable and the interface
6 to the parsers is much cleaner anyway.
7
8 The only thing left here now is C{parse_sequence_with_annotations}
9
10 """
11 """
12 ============================== License ========================================
13 Copyright (C) 2008, 2010-12 University of Edinburgh, Mark Granroth-Wilding
14
15 This file is part of The Jazz Parser.
16
17 The Jazz Parser is free software: you can redistribute it and/or modify
18 it under the terms of the GNU General Public License as published by
19 the Free Software Foundation, either version 3 of the License, or
20 (at your option) any later version.
21
22 The Jazz Parser is distributed in the hope that it will be useful,
23 but WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 GNU General Public License for more details.
26
27 You should have received a copy of the GNU General Public License
28 along with The Jazz Parser. If not, see <http://www.gnu.org/licenses/>.
29
30 ============================ End license ======================================
31
32 """
33 __author__ = "Mark Granroth-Wilding <mark.granroth-wilding@ed.ac.uk>"
34
35 import sys, traceback, os
36
37 from jazzparser.data.trees import build_tree_for_sequence, TreeBuildError
38 from jazzparser.parsers.cky.parser import DirectedCkyParser, DirectedParseError
39 from jazzparser.data.input import DbInput, AnnotatedDbInput
40 from jazzparser.taggers.pretagged import PretaggedTagger
41 from jazzparser.parsers import ParseError
42 from jazzparser.grammar import get_grammar
43 from jazzparser.data.db_mirrors import SequenceIndex
44 from jazzparser.data.parsing import ParseResults
45 from jazzparser.utils.loggers import create_logger, create_dummy_logger
46
49 """
50 Parses an annotated sequence. This is essentially just constructing
51 the implicit derivation encoded in the annotations (categories
52 and coordination markers) and returning the top-level result that
53 this derivation yields.
54
55 If there are missing annotations, this will not yield a single
56 result, but the results of each parse of a subsequence.
57
58 Returns a list of parse results. If the annotation is complete, the
59 return value should be a single-item list containing the parse
60 result.
61
62 @type allow_subparses: boolean
63 @param allow_subparses: if True (default) will return multiple
64 subparses if the annotation is incomplete. If False, raises
65 a ParseError in this case.
66 @type popts: dict
67 @param popts: options to pass to the parser
68 @type parse_logger: bool
69 @param parse_logger: logger to which to report a trace of the shift-reduce
70 parse of the annotations. If None, nothing is output
71
72 """
73
74 if isinstance(sequence, DbInput):
75 input = sequence
76 sequence = sequence.sequence
77 durations = input.durations
78 times = input.times
79 else:
80 raise ValueError, "parse_sequence_with_annotations can only be "\
81 "applied to a DbInput. Got: %s" % type(sequence).__name__
82 categories = [chord.category for chord in sequence.iterator()]
83
84 try:
85 tree = build_tree_for_sequence(sequence, grammar=grammar)
86 except TreeBuildError, err:
87 raise ParseError, "could not build a tree for '%s': %s" % \
88 (sequence.string_name, err)
89
90 if not allow_subparses and len(tree.children) > 1:
91 raise ParseError, "gold standard for sequence '%s' does not "\
92 "yield a single tree: annotation is incomplete" % \
93 sequence.string_name
94
95 sub_parses = []
96 end = 0
97 i = 0
98
99 for sub_tree in tree.children:
100
101 length = sub_tree.span_length
102 start = end
103 end += length
104
105
106 if hasattr(sub_tree, 'chord'):
107 continue
108 i += 1
109
110
111
112 tags = []
113 for word,tag,duration,time in zip(
114 list(input)[start:end],
115 categories[start:end],
116 durations[start:end],
117 times[start:end]):
118 if tag == "":
119 word_signs = []
120 elif tag not in grammar.families:
121 raise ParseError, "could not get a sign from the "\
122 "grammar for the tag %s (chord %s)" % \
123 (tag, word)
124 else:
125
126 word_signs = grammar.get_signs_for_word(word, tags=[tag],
127 extra_features={
128 'duration' : duration,
129 'time' : time,
130 })
131 tags.append(word_signs)
132 tagger = PretaggedTagger(grammar, input.slice(start,end), tags=tags)
133
134
135 parser = DirectedCkyParser(grammar, tagger, derivation_tree=sub_tree, options=popts)
136 try:
137 parser.parse(derivations=True)
138 except DirectedParseError, err:
139
140 raise ParseError, "[Partial parse %d (%d-%d)] parsing using "\
141 "the derivation tree failed: %s" % (i,start,end,err)
142
143 parses = parser.chart.parses
144 if len(parses) == 0:
145 raise ParseError, "[Partial parse %d (%d-%d)] parsing using the "\
146 "derivation tree did not produce any results" % (i,start,end)
147 elif len(parses) > 1:
148 raise ParseError, "the annotated tree gave multiple "\
149 "parse results: %s" % ", ".join(["%s" % p for p in parses])
150 else:
151 sub_parses.append(parses[0])
152
153 return sub_parses
154