Showing posts with label bug. Show all posts
Showing posts with label bug. Show all posts

Thursday, 4 June 2009

hash symbol

No Haskell or NLP experience needed to understand this post, I think

I have a piece of code that converts coreference resolved OpenNLP output from one format to another. This is a piece of OpenNLP output, just a parse tree for the NP "the way"
(NP (DT the) (NN way))
The assumption I'm going on is that categories that have a hash after them are coreference annotations. For example, this bracketed expression is the phrase "Nova Scotia" marked as an NP with coref identifier = 1
(NP#1 (NP (location (NNP Nova) (NNP Scotia)))

So far so good?

Here's a piece of Haskell code that extracts the id for a given category. It's just a three liner, nothing exotic here:
getCoref :: String -> Maybe String
getCoref s =
case break (== '#') s of
(_,"") -> Nothing
(_,x ) -> Just (drop 1 x)


To get an idea what this does, getCoref "NP#42" == Just "42" and getCoref "NP" == Nothing

spot the bug

What am I missing? SPOILERS BELOW.














partial solution

Consider the phrase "#1" as in "#1 on the charts".
It gets parsed as (NP (# #) (CD 1))
Get the idea?

full solution

The code above uses a standard library function "break" which splits a string on a delimiter. For example, break "/" "foo/bar" returns the pair ("foo","/bar"). (This is why I use drop 1; it lets me drop the delimiter). The assumption I was making is that for all categories in which a hash symbol occurs, the hash symbol is preceded by some non-empty string (which I ignore with the underscore). Tis assumption was incorrect. Sometimes you have a category "#" which is not a case of coreference annotation, but a vanilla category that we can ignore.

So the current version of myself (Eric 2009-06-04T09:45+0100) thinks that the right way to write this function is
getCoref :: String -> Maybe String
getCoref s =
case break (== '#') s of
(_,[]) -> Nothing
("","#") -> Nothing -- specifically handle the "#" category
(_,"#") -> error ("unexpected category ending with just a hash: " ++ s)
(_,x ) -> Just (drop 1 x)


I've inserted two extra cases into my pattern-matching (Prolog folks should like ML and Haskell for the pattern matching if nothing else). The first extra case accounts for just the "#" category.

The second extra case accounts for any other unexpected uses of "#". As an aside, I'll say that this is an attempt to apply the "dead programs never lie" principle from The Pragmatic Programmer (a must-read). When I was a freshman, our programming prof tried to explain the idea of robust code and defensive programming. But being a dumb freshman, I walked away his lecture unknowingly confused. I had this idea that my code should somehow never fail. But there are cases (and maybe this is something he said, but dumb freshman Eric did not hear) where you should deliberately fail to avoid silently perpetuating errors and lies. Now that I am a dumb post-doc, I am slightly less confused about this point, although I probably am still confused and misguided about lots of other points.

Sorry for the distracting aside. The main point of this post was to show an example of the kind of bug that arises from an incomplete understanding the problem ("oh, just grab the string that follows the hash symbol") you're trying to solve.

Tuesday, 26 May 2009

done with OT stuff?

Working on the OT stuff was fun, except of course for the fact that I've reached a point where I really should be shifting back to my regularly scheduled UoB programming (an accidental pun with accidental truth).

sorting bug

Here's a mildly interesting bug we got. Our goal is to sort the results so that the ones with the lowest ranking violations come first. We do this with a simple dictionary sort (i.e. as in a paper dictionary, where each result aka set of violations is a word, and the rank of each violation is a letter).

In my first implementation, the order of the output we got was a little bizzare. I'll put down the expected global ranking in numbers next to each output to show how they're out of order:
2. marie etre aimer par jean
(aimer Tn0Vn1-1546)
(jean Tpropername-1297)
(marie Tpropername-1297)
(aimer Tn0Vn1-1546) *passiveVerbMorphology (r6)

3. ce etre par jean que etre aimer marie
(aimer Tn0Vn1-1570) *(InvertedNominalSubject & UnboundedCleft) (r3)
(jean Tpropername-1297)
(marie Tpropername-1297)
(aimer Tn0Vn1-1570) *InvertedNominalSubject (r5) *passiveVerbMorphology
(r6) *UnboundedCleft (r7)

1. ce etre jean qui aimer marie
(aimer Tn0Vn1-1515)
(jean Tpropername-1297)
(marie Tpropername-1297)
OT-GenI was doing the right thing for #2 and #3; it correctly recognised that an r6 violation is better than an r3 violation, but it was totally wrong for #1. Why?

I'll just talk in terms of the paper dictionary. Imagine we have the words "zoo", "zo", "zebra" and "z". The desired order of these results is:
1. z
2. zo
3. zoo
4. zebra
But what I was giving my user was something more like
3. zoo
2. zo
4. zebra
1. z
Did you know there was once an online contest for the best WTF ligature? Some of the results are actually quite pretty.

So what happened? You can probably guess how I came up with my wrong output: I was just performing reverse comparisons on the words. And what I should I have been doing instead? Easy: a forward comparison on the words using a reverse comparisons on the letters (durr). So instead of saying that "zoo" comes before "zebra" (because then "z" comes after "zoo"), I should be saying that "o" comes before "e".

Shoddy workmanship. What bothers me isn't that I made this kind of mistake but that I never noticed it, and that when somebody first pointed it out to me, I thought it was one of those weird works-for-me deals which I couldn't reproduce on my end.

Anyway, it's a great example of the kind of errors to which I'm prone, i.e. those involving a notion of polarity or sides. One way or the other, I'm always liable to get something backwards.

bleeding time

I need to be getting back to genchal work. In between all these trips in April, I've been slacking off a lot and bleeding hours left and right from my holiday-adjusted time bank:
See the bit where the graph nose-dives in the end? You should make little airplane noises as you look at it: eeeeeeynoowwww, sploosh! That's the result of three trips and the corresponding routine-lag. The last trip, by the way, would be a 2 week holiday back in Florida, from which I returned on Friday (2009-05-15) a couple weeks ago.

Again, the point isn't to go into bean-counter mode. Slacking is good for you and your productivity, but if you can't get yours done within generous European holiday allowances, you've got a problem. What this graph is showing is that I need to be getting off my butt and back into the office. I'm at -43h which basically means two months of spending Saturday in the office, assuming I can convert myself back into the Good Eric I once knew...