source: src/tre/tre @ acbabee

Last change on this file since acbabee was acbabee, checked in by Tomasz Obrebski <obrebski@…>, 9 years ago

many changes, mainly dgp1 algorithm

  • Property mode set to 100755
File size: 13.5 KB
Line 
1#!/usr/bin/env ruby
2# -*- coding: iso-8859-2 -*-
3
4# $: << "#{ENV['HOME']}/.local/lib/utt"
5$: << "/usr/local/lib/utt"
6
7require 'getoptlong'
8require 'seg.rb'
9
10opts = GetoptLong.new(
11[ '--help',     '-h',   GetoptLong::NO_ARGUMENT ],
12[ '--debug',    '-d',   GetoptLong::NO_ARGUMENT ],
13[ '--format',   '-F',   GetoptLong::REQUIRED_ARGUMENT ],
14[ '--info',     '-I',   GetoptLong::REQUIRED_ARGUMENT ],
15[ '--dgpids',           GetoptLong::NO_ARGUMENT ],
16[ '--graph',            GetoptLong::NO_ARGUMENT ],
17[ '--uniq',     '-u',   GetoptLong::NO_ARGUMENT ],
18[ '--utt',              GetoptLong::NO_ARGUMENT ],
19[ '--span',     '-s',   GetoptLong::REQUIRED_ARGUMENT ],
20[ '--maxsize',          GetoptLong::REQUIRED_ARGUMENT ],
21[ '--forest',           GetoptLong::NO_ARGUMENT ],
22[ '--only-trees','-t',  GetoptLong::NO_ARGUMENT ])
23
24$helptext = <<END
25The program generates trees from the graph output by dgp. dgp must be run
26with '--info=ds' option.
27
28Command:       tre [options]
29
30Options:
31--help         -h      Print help (this text) and exit.
32--debug        -d      Verbose output. For developers only.
33--format=s     -F s    Output format. Recognized values:
34                               a       root + list of arcs
35                               p       parenthesized notation
36                               h       human readable indented format
37                               c       CONLL format
38                       Multiple values are allowed. (default p)
39--info=s       -I s    Information printed. Recognized values:
40                               n       node identifier
41                               f       surface form
42                               m       morphological information
43                               l       arc labels\
44--gphids               Used gph node identifiers (default: linear)
45--dgpids               Used dgp node identifiers (default: linear)
46--graph                Do not generate trees, just print the graph.
47--uniq         -u      Remove duplicate trees.
48--utt                  UTT formatted output.
49
50END
51
52$DEBUG=false
53$FORMAT='p'
54$INFO='DEFAULT'
55$UTTOUTPUT=false
56$START=nil
57$END=nil
58$FOREST=false
59$MAXSIZE=nil
60$GPHIDS=false
61$DGPIDS=false
62$GRAPH==false
63
64opts.each do |opt, arg|
65  case opt
66  when '--help'
67    print $helptext
68    exit 0
69  when '--debug'
70    $DEBUG=true
71  when '--format'
72    $FORMAT=arg
73  when '--info'
74    $INFO=arg
75  when '--gphids'
76    $GPHIDS=true
77  when '--dgpids'
78    $DGPIDS=true
79  when '--graph'
80    $GRAPH=true
81  when '--uniq'
82    $UNIQ=true
83  when '--utt'
84    $UTTOUTPUT=true
85  when '--forest'
86    $FOREST=true
87  when '--maxsize'
88    $MAXSIZE=arg.to_i
89  when '--span'
90    $START,$END = arg.split ','
91  else
92    print "Unknown option #{opt}. Ignored.\n"
93  end
94end
95
96if $INFO=='DEFAULT'
97  case $FORMAT
98    when 'p','a'
99    $INFO='fl'
100    when 'h'
101    $INFO='fmnl'
102  end
103end
104
105$dgpsep=';'
106
107def tre(input)
108  $gphid=[]
109  $form=[]
110  $lem=[]
111  $ord1=[]
112  $count=0
113  nodes=[]
114  prevpos=-1
115  tokennumber=0
116  for line in input
117    seg=Seg.new(line)
118    print line if $UTTOUTPUT && seg.field(3) == 'EOS'
119   
120    if dgp=seg['dgp']
121      if nodes==[] && seg[3]!='BOS'
122        print "A sentence must start with BOS segment. Aborting.\n"
123        return
124      end
125
126      id=dgp[/^\d+/].to_i
127
128      if gph=seg['gph']
129        $gphid[id]=gph[/^\d+/].to_i
130      else
131        print "No gph field. Aborting.\n"
132        return
133      end
134
135      $form[$gphid[id]] = seg[4]
136      $lem[$gphid[id]] = seg['lem']
137      $ord1[$gphid[id]] = if prevpos==seg[1].to_i then tokennumber
138                         else prevpos=seg[1].to_i; tokennumber+=1 end
139             
140      nodes[id] = [seg[1].to_i,seg[2].to_i,dgp]
141
142      if seg[3]=='EOS'
143
144        $pref = "#{seg[1]} #{seg[2]} SYN *"
145        parsegraph(nodes)
146        set_ord #(0...(nodes.length)).each{|i| set_distance_from_i i }
147        printgraph if $DEBUG
148        if $GRAPH
149          if $FORMAT =~ /c/
150            printconll
151          else
152            printground
153          end
154        else
155          thetrees = $FOREST ? genforest : gentrees
156          outputs = output_trees thetrees
157          outputs = outputs.sort.uniq if $UNIQ
158          print outputs.join
159          print line if $UTTOUTPUT
160          $gphid=[]
161          $form=[]
162          $lem=[] 
163          $ord1=[]
164          $count=0     
165          nodes=[]
166          prevpos=-1
167          tokennumber=0
168        end
169      end
170    end
171  end
172end
173
174
175def output_trees trees
176 
177  outputs = []
178 
179  for t in trees
180    $count += 1
181    # t1=ground(t)
182
183    t1=t
184
185    # span = $FOREST ? " span:" + (ground_tree_min(t1).to_s + ","+ground_tree_max(t1).to_s)+";" : ""
186    # case $FORMAT
187    # when /a/
188    #   outputs << "#{$pref} tre:#{$count}#{span} #{arc_output(t1)}\n"
189    # when /p/
190    #   outputs << "#{$pref}#{span} tre:#{$count} par:#{par_output(t1)}\n"
191    # when /h/
192    #   outputs << "#\n# tree #{$count}\n# ------\n#{dgp_output(t1,0)}"
193    # when /c/
194    #   outputs << conll_output(t1,0)
195    # end
196
197    case $FORMAT
198    when /a/
199      outputs << "#{arc_output(t1)}\n"
200    when /p/
201      outputs << "#{par_output(t1)}\n"
202    when /h/
203      outputs << human_output(t1,0)
204    when /c/
205      outputs << conll_output(t1,0)
206    end
207
208  end
209
210  outputs
211
212end
213
214def id_output id
215  if $DGPIDS then id elsif $GPHIDS then $gphid[id] else $ord1[$gphid[id]] end
216end
217
218def nodeinfo(id)
219  info=""
220  gphid = $gphid[id]
221  if $INFO =~ /o/
222    info += $ord1[gphid].to_s + '/' + gphid.to_s + '/' + id.to_s
223    info += '.' if $INFO =~ /[nfm]/
224  end
225  if $INFO =~ /n/
226    info += id_output(id).to_s                           
227    info += '.' if $INFO =~ /[fm]/
228  end
229  if $INFO =~ /f/
230    info += $form[gphid] 
231    info += ';' if $INFO =~ /m/
232  end
233  if $INFO =~ /m/
234    info += $lem[gphid] 
235  end
236  info
237end
238
239
240def arc_output(tree)
241  root, arcs = tree
242  "head:#{nodeinfo(root)} links:" + arcs.map{|a| "(#{($INFO =~ /l/) ? a[2]+":" : ""}#{nodeinfo(a[0])}-#{nodeinfo(a[1])})"}.join("")
243end
244
245def printtree(root,arcs,o)
246  if o==0
247        print "# %-16s" % "root: "
248  end
249  print nodeinfo(root),"\n"
250  for arc in arcs.select{ |a| a[0]==root }.sort{|a,b| a[1]<=>b[1] }
251    print '# ',"   "*(o+1)
252    print "%-16s" % (arc[2]+": ")
253    printtree(arc[1],arcs,o+1)
254  end
255end
256
257def human_output(tree,o)
258  root, arcs = tree
259  output = ''
260  if o==0
261        output += "%-16s" % "root: "
262  end
263  output += nodeinfo(root) + "\n"
264  for arc in arcs.select{ |a| a[0]==root }.sort{|a,b| a[1]<=>b[1] }
265    output += "   "*(o+1)
266    output += "%-16s" % (arc[2]+": ")
267    output += human_output([arc[1],arcs],o+1)
268  end
269  output
270end
271
272def conll_output(tree,o)
273  root,arcs = tree
274  nodes = ([root] + arcs.map{|a| a[1]}).sort{|a,b| $gphid[a] <=> $gphid[b]}
275  conll_lines = []
276  for i in nodes
277    gphid = $gphid[i]
278    id = $ord1[gphid]
279    form = $form[gphid]
280    /^(?<lemma>.*),(?<cpostag>[^\/]*)(\/(?<feats>.+))?/ =~ $lem[gphid]
281    thearcs = arcs.select{|a| a[1]==i }.map{|a| [$ord1[$gphid[a[0]]],a[2]] } 
282    thearcs = [[0,'root']] if thearcs.empty?
283    for a in thearcs
284      head,deprel = a
285      conll_lines << [id,form,lemma,cpostag,cpostag,feats,head,deprel,nil,nil].map{|s| s ? s.to_s : "_"}.join("\t")
286    end
287  end
288  conll_lines.join("\n") + "\n\n"
289end
290
291def par_output(tree)
292  root, arcs = tree
293  ldeps = arcs.select{|a| a[0]==root and $gphid[a[1]] < $gphid[root]}.sort{|a,b| $gphid[a[1]]<=>$gphid[b[1]] }
294  rdeps = arcs.select{|a| a[0]==root and $gphid[a[1]] > $gphid[root]}.sort{|a,b| $gphid[a[1]]<=>$gphid[b[1]] }
295
296  output = ''
297
298  output_left  = ldeps.map{|arc| ' (' + (($INFO =~ /l/) ? arc[2].upcase : '') + par_output([arc[1],arcs]) + ')'}.join
299  output_right = rdeps.map{|arc| ' (' + (($INFO =~ /l/) ? arc[2].upcase : '') + par_output([arc[1],arcs]) + ')'}.join
300
301  # for arc in ldeps
302  #   output += ' ('
303  #   output += arc[2].upcase if $INFO =~ /l/
304  #   output += par_output(arc[1],arcs)
305  #   output += ')'
306  # end
307
308  # print ' ',nodeinfo(root)
309
310  # for arc in rdeps
311  #   print ' ('
312  #   print arc[2].upcase if $INFO =~ /l/
313  #   printpar(arc[1],arcs)
314  #   print ')'
315  # end
316
317  output_left + ' ' + nodeinfo(root) + output_right
318
319end
320
321
322def ground_tree_min t
323  ([t[0]]+t[1].map{|e| [e[0],e[1]]}).flatten.min
324end
325
326def ground_tree_max t
327  ([t[0]]+t[1].map{|e| [e[0],e[1]]}).flatten.max
328end
329
330
331
332def parsegraph(nodes)
333
334  $n   =nodes.length
335  $sat =[]; $vis =[]; $succ=[]; $lhs =[]; $arcs=[]; $pos=[]; $len=[]; $ord=[]; $distance={}
336
337  for dgp in nodes
338
339    parts  = dgp[2].split($dgpsep,7)
340
341    if parts[3]==nil || parts[4]==nil || parts[5]==nil
342      $stderr.print "ERR: tre requires dgp be called with '--info=ds' option. Aborting.\n"
343      exit
344    end
345
346    i      = parts[0].to_i
347    $pos[i] = dgp[0].to_i
348    $len[i] = dgp[1].to_i
349    $sat << i if parts[1]=="s"
350
351    $arcs |= parts[2].scan(/\([^()]+\)/).map{ |a| case a
352                                          when /\-\-(\w+):(\d+)/
353                                            # [i, $2.to_i, $1, $3.to_i, $4.to_i]
354                                            [i, $2.to_i, $1, 0, 0]
355                                          when /\+\+(\w+):(\d+)/
356                                            # [$2.to_i, i, $1, $3.to_i, $4.to_i]
357                                            [$2.to_i, i, $1, 0, 0]
358                                          end }
359    $succ |= parts[3][1..-2].split(',').map{|x| [x.to_i,i]}
360    $vis  |= parts[4][1..-2].split(',').map{|x| [x.to_i,i]} 
361    $lhs  |= parts[5][1..-2].split(',').map{|x| [x.to_i,i]} + [[i,i]]
362
363  end
364
365end
366
367
368def ground(t)
369  [ $gphid[t[0]] , t[1].map{|a| [$gphid[a[0]],$gphid[a[1]],a[2]]} ]
370end 
371
372
373#NOWE-START
374
375def successors i
376  $succ.select{|e| e[0]==i}.map{|e| e[1]}
377end
378
379def predecessors i
380  $succ.select{|e| e[1]==i}.map{|e| e[0]}
381end
382
383def start_nodes
384  $succ.map{|e| e[1]}.map{|e| predecessors(e)}.uniq.map{|e| e[0]}
385end
386
387def end_nodes
388  $succ.map{|e| e[0]}.map{|e| successors(e)}.uniq.map{|e| e[0]}
389end
390
391def set_ord
392  positions = $pos.uniq.sort
393  (0...$n).each{|i| $ord[i] = positions.index($pos[i]) }
394end
395
396
397def set_distance_from_i i
398  set_distance_from_i_to_jth_successors_to_v i, i, 1
399end
400
401def set_distance_from_i_to_jth_successors_to_v i, j , v
402  succ = successors(j)
403  for j1 in succ
404    $distance[[i,j1]] = v
405    set_distance_from_i_to_jth_successors_to_v i, j1, v+1
406  end
407end
408
409#NOWE-END
410
411
412def gentrees
413  bos=0; eos=$n-1;
414  gentrees2 bos, eos
415end
416
417
418def genforest
419  forest=[]
420  for bos in start_nodes
421    for eos in end_nodes # tu s± te¿ wierzcho³ki poprzedzaj±ce!!!
422      next if $ord[bos] > $ord[eos] or ($MAXSIZE != nil and $ord[eos] - $ord[bos] > $MAXSIZE+1)
423      forest += gentrees2(bos,eos)
424    end
425  end
426  forest
427end
428
429def gentrees2 bos, eos
430  $thetrees=[];
431  roots = (bos..eos).select{|i| $vis.include? [i,eos]}.select{|i| $vis.include? [bos,i]}
432
433  if $DEBUG then print "ROOTS: #{roots.inspect}\n" end
434  for root in roots
435    gentrees3 bos, eos, root
436  end
437  $thetrees
438end
439
440def gentrees3 bos, eos, root
441  $theroot=root
442  $thebos=bos
443  $theeos=eos
444  for r in buildR(root , eos, [])
445    (rmin,rmax,rtree) = r
446    buildR(bos, rmin, rtree)
447  end
448end
449
450def buildR(min, max, tree)
451  if $DEBUG then print "buildR--#{min}--#{max}--#{tree.inspect}\n" end
452  trees=[]
453  for arc in $arcs.select{|a| a[0]==max && $vis.include?([min,a[1]]) }
454    if $DEBUG then print "ARC: #{arc.inspect}\n" end
455    for r in buildR(arc[1],arc[0],tree+[arc])                 #!!! buildR(a[1],a[3],tree+[a])
456      # for r in buildR(arc[4],arc[3],tree+[arc])                 #!!! buildR(a[1],a[3],tree+[a])
457      (rmin,rmax,rarcs) = r
458      for l in buildR(min,rmin,rarcs)
459        (lmin,lmax,larcs) = l
460        trees << [lmin,rmax,larcs]
461      end
462    end
463  end
464  for i in (0...$n).select{|i| $succ.include?([i,max])}.select{|i| $lhs.include?([min,i])}
465    for l in buildL(min,i,tree)
466      (lmin,lmax,larcs) = l
467      trees << [lmin,lmax,larcs]
468    end
469  end
470  trees 
471end
472   
473
474def buildL(min,max,tree)
475  if $DEBUG then print "buildL--#{min}--#{max}--#{tree.inspect}\n" end
476  if $pos[min]==$pos[max]
477    if min==$thebos && max==$thebos
478      $thetrees.push [$theroot,tree]
479      if $DEBUG then print "adding tree: #{tree.inspect}\n" end
480    end
481    return [[max,max,tree]]
482  end
483  trees=[]
484  for arc in $arcs.select{|a| a[1]==max && $lhs.include?([min,a[0]]) }
485    if $DEBUG then print "ARC: #{arc.inspect}\n" end
486     for r in buildR(arc[0],arc[1],tree+[arc]) ### buildR(arc[3],max,tree+[arc])
487     #for r in buildR(arc[3],arc[4],tree+[arc]) ### buildR(arc[3],max,tree+[arc])
488      (rmin,rmax,rarcs) = r
489      for l in buildL(min,rmin,rarcs)
490        (lmin,lmax,larcs) = l
491        trees << [lmin,lmax,larcs]
492      end
493    end
494  end
495  trees
496end
497
498
499def printgraph()
500  print "N:    #{$n}\n"
501  print "SAT:  #{set_to_s($sat)}\n"
502  print "SUCC: #{rel_to_s($succ)}\n"
503  print "VIS:  #{rel_to_s($vis)}\n"
504  print "LHS:  #{rel_to_s($lhs)}\n"
505  print "ARCS: #{arcs_to_s($arcs)}\n"
506end
507
508
509def printground
510  for i in 1...($form.length-1)
511    print "#{$ord1[i]} #{$form[i]} #{$lem[i]} "
512    print $arcs.select{|a| $ord1[$gphid[a[1]]] == $ord1[i]}.map{|a| "#{a[2]}:#{$ord1[$gphid[a[0]]]}"}.sort.uniq.join(' ')
513    print "\n"
514  end
515end
516
517def printconll
518  for i in 1...($form.length-1)
519    id = $ord1[i]
520    form = $form[i]
521    /^(?<lemma>.*),(?<cpostag>[^\/]*)(\/(?<feats>.+))?/ =~ $lem[i]
522    arcs = $arcs.select{|a| $ord1[$gphid[a[1]]] == $ord1[i]}.map{|a| [$ord1[$gphid[a[0]]],a[2]]}.sort.uniq
523    arcs = [[0,'root']] if arcs.empty?
524    for a in arcs
525      head,deprel = a
526      puts [id,form,lemma,cpostag,cpostag,feats,head,deprel,nil,nil].map{|s| s ? s.to_s : "_"}.join("\t")
527    end
528  end
529  puts
530end
531
532
533def set_to_s(s) "{#{s.join(',')}}" end
534def rel_to_s(r) "{#{r.map{|p| "(#{p[0]},#{p[1]})"}.join(',')}}" end
535def arc_to_s(q) "-#{q[0]}-#{q[2]}-#{q[1]}/#{q[3]}" end
536def arcs_to_s(a) "{#{a.map{|q| arc_to_s(q)}.join(',')}}" end
537
538######################################################################
539
540tre($stdin)
Note: See TracBrowser for help on using the repository browser.