3 new revisions:
Revision: 895a72ee6f48
Branch: default
Author: Michael Gasser <
gas...@cs.indiana.edu>
Date: Thu May 1 06:39:44 2014 UTC
Log: Additional restrictions on variables, fixed changed
variable->constrai...
http://code.google.com/p/hltdi-l3/source/detail?r=895a72ee6f48
Revision: 0d759cb5483e
Branch: default
Author: Michael Gasser <
gas...@cs.indiana.edu>
Date: Fri May 2 20:10:49 2014 UTC
Log: Handling input words that don't match and group in lexicon
http://code.google.com/p/hltdi-l3/source/detail?r=0d759cb5483e
Revision: 829e0849435b
Branch: default
Author: Michael Gasser <
gas...@cs.indiana.edu>
Date: Fri May 2 22:53:47 2014 UTC
Log: Solved output order problems; 3 example translations in
hiiktuu.py.
http://code.google.com/p/hltdi-l3/source/detail?r=829e0849435b
==============================================================================
Revision: 895a72ee6f48
Branch: default
Author: Michael Gasser <
gas...@cs.indiana.edu>
Date: Thu May 1 06:39:44 2014 UTC
Log: Additional restrictions on variables, fixed changed
variable->constraint issue in constraint satisfaction
http://code.google.com/p/hltdi-l3/source/detail?r=895a72ee6f48
Added:
/l3lite/languages/spa.lg
Modified:
/l3lite/constraint.py
/l3lite/cs.py
/l3lite/entry.py
/l3lite/language.py
/l3lite/languages/eng.lg
/l3lite/sentence.py
/l3lite/variable.py
/lite.py
=======================================
--- /dev/null
+++ /l3lite/languages/spa.lg Thu May 1 06:39:44 2014 UTC
@@ -0,0 +1,67 @@
+name: español
+abbrev: spa
+
+groups:
+ cantar:
+ # cantar las cuarenta (with pronoun indirect object)
+ - words: [$algnp, cantar, las, cuarenta]
+ trans:
+ eng:
+ - [read_$sbd_the_riot_act,
+ {alg: [1, 0, 2, 3],
+ agr: [false, {tmp: tns, num: num, prs: prs}, false, false]}]
+ # cantar (una canción)
+ - words: [cantar, $cantable]
+ trans:
+ eng:
+ - [sing_$singable,
+ {alg: [0, 1],
+ agr: [{tmp: tns, num: num, prs: prs}, false]}]
+ canción:
+ - words: [una, canción]
+ trans:
+ eng:
+ - [a_song, {alg: [0, 1]}]
+ canciones:
+ - words: [canciones]
+ les:
+ - words: [les]
+ trans:
+ eng:
+ - [them]
+ muchacho:
+ - words: [el, muchacho]
+ trans:
+ eng:
+ - [the boy, {alg: [0, 1]}]
+
+forms:
+ canción:
+ root: canción
+ features: {num: 0, prs: 3}
+ cats: [$cantable, $algo]
+ canciones:
+ root: canción
+ features: {num: 1, prs: 3}
+ cats: [$cantable, $algo]
+ les:
+ root: ellos
+ features: {num: 1, prs: 3, case: i}
+ cats: [$algnp]
+ nos:
+ root: nosotros
+ features: {num: 1, prs: 1, case: i}
+ cantó:
+ root: cantar
+ features: {num: 0, prs: 3, tmp: prt}
+ canta:
+ root: cantar
+ features: {num: 0, prs: 3, tmp: prs}
+ muchacho:
+ root: muchacho
+ features: {num: 0, prs: 3, gen: 0}
+ cats: [$algn]
+
+
+
+
=======================================
--- /l3lite/constraint.py Wed Apr 30 06:56:30 2014 UTC
+++ /l3lite/constraint.py Thu May 1 06:39:44 2014 UTC
@@ -38,6 +38,10 @@
# ComplexSetConvexity, Union, Disjoint, Inclusion
# 2014.04.26
# -- Fixed several bugs in SetPrecedence (needed for TL sequencing).
+# 2014.04.30
+# -- Eliminated lots of unused constraints.
+# Fixed complex constraints so that sub-constraints are not recorded
+# in their variables.
from .variable import *
import itertools
@@ -149,12 +153,12 @@
class Member(Constraint):
- def __init__(self, var, domain, problem=None):
+ def __init__(self, var, domain, problem=None, record=True):
"""
var: an IVar
domain: a set of ints
"""
- Constraint.__init__(self, (var,), problem=problem)
+ Constraint.__init__(self, (var,), problem=problem, record=record)
self.domain = domain
self.name = '{0}<{1}'.format(self.get_var(),
Constraint.string_set(self.domain))
@@ -183,12 +187,12 @@
class Superset(Constraint):
"""Set variable is constrained to be a superset of subset."""
- def __init__(self, var, subset, problem=None):
+ def __init__(self, var, subset, problem=None, record=True):
"""
var: a SVar
subset: a set of ints
"""
- Constraint.__init__(self, (var,), problem=problem)
+ Constraint.__init__(self, (var,), problem=problem, record=record)
self.subset = subset
self.name = '{0} >= {1}'.format(self.get_var(),
Constraint.string_set(self.subset))
@@ -216,12 +220,12 @@
class Subset(Constraint):
"""Set variable is constrained to be a subset of superset."""
- def __init__(self, var, superset, problem=None):
+ def __init__(self, var, superset, problem=None, record=True):
"""
var: a SVar
superset: a set of ints
"""
- Constraint.__init__(self, (var,), problem=problem)
+ Constraint.__init__(self, (var,), problem=problem, record=record)
self.superset = superset
self.name = '{0} c= {1}'.format(self.get_var(),
Constraint.string_set(self.superset))
@@ -245,186 +249,186 @@
return Constraint.entailed, {var}
return Constraint.entailed, set()
-# Set cardinality
-
-class CardinalityGEQ(Constraint):
- """Set variable's cardinality is constrained to be >= lower bound."""
-
- def __init__(self, var, lower, problem=None):
- Constraint.__init__(self, (var,), problem=problem)
- self.lower = lower
-
self.name = '|{0}|>={1}'.format(self.get_var(), self.lower)
-
- def fails(self, dstore=None):
- """Is the var's upper cardinality bound < lower?"""
- if self.get_var().get_upper_card(dstore=dstore) < self.lower:
- return True
- return False
-
- def is_entailed(self, dstore=None):
- """Is the variable's lower cardinality bound already >= lower?"""
- if self.get_var().get_lower_card(dstore=dstore) >= self.lower:
- return True
- return False
-
- def infer(self, dstore=None, verbosity=0, tracevar=None):
- """The variable's cardinality is restricted be >= lower: lower
bound
- is raised if necessary."""
- var = self.get_var()
- if var.strengthen_lower_card(self.lower, dstore=dstore,
- constraint=(verbosity>1 or var in
tracevar) and self):
- return Constraint.entailed, {var}
- return Constraint.entailed, set()
-
-class CardinalityLEQ(Constraint):
- """Set variable's cardinality is constrained to be <= upper bound."""
-
- def __init__(self, var, upper, problem=None):
- Constraint.__init__(self, (var,), problem=problem)
- self.upper = upper
-
self.name = '|{0}| c= {1}'.format(self.get_var(), self.upper)
-
- def fails(self, dstore=None):
- """Is the var's lower cardinality bound > upper?"""
- if self.get_var().get_lower_card(dstore=dstore) > self.upper:
- return True
- return False
-
- def is_entailed(self, dstore=None):
- """Is the variable's upper cardinality bound already <= upper?"""
- if self.get_var().get_upper_card(dstore=dstore) <= self.upper:
- return True
- return False
-
- def infer(self, dstore=None, verbosity=0, tracevar=None):
- """The variable's cardinality is restricted to be <= upper:
- upper bound is lowered if necessary."""
- var = self.get_var()
- if var.strengthen_upper_card(self.upper, dstore=dstore,
- constraint=(verbosity>1 or var in
tracevar) and self):
- return Constraint.entailed, {var}
- return Constraint.entailed, set()
-
-### Constraints that propagate
-
-## Primitive propagators
-
-# Integer domain variables only
-
-class LessThan(Constraint):
- """IVar1 is less than or equal to IVar2."""
-
- def __init__(self, variables, problem=None, weight=1):
- Constraint.__init__(self, variables, problem=problem,
- weight=weight)
-
self.name = '{0} <= {1}'.format(self.get_iv1(), self.get_iv2())
-
- def get_iv1(self):
- return self.variables[0]
-
- def get_iv2(self):
- return self.variables[1]
-
- def fails(self, dstore=None):
- """
- Fail if min of domain1 > max of domain2.
- """
- iv1 = self.get_iv1()
- iv2 = self.get_iv2()
- min1 = min(iv1.get_domain(dstore=dstore))
- max2 = max(iv2.get_domain(dstore=dstore))
- if min1 > max2:
- return True
- return False
-
- def is_entailed(self, dstore=None):
- """Entailed if max of domain1 <= min of domain2."""
- iv1 = self.get_iv1()
- iv2 = self.get_iv2()
- max1 = max(iv1.get_domain(dstore=dstore))
- min2 = min(iv2.get_domain(dstore=dstore))
- if max1 <= min2:
- return True
- return False
-
- def infer(self, dstore=None, verbosity=0, tracevar=None):
- changed = set()
- iv1 = self.get_iv1()
- iv2 = self.get_iv2()
- d1 = iv1.get_domain(dstore=dstore)
- d2 = iv2.get_domain(dstore=dstore)
- # iv2 must be between the min of iv1's domain and the maximum value
- iv2_values = set(range(min(d1), max(d2) + 1))
- if iv2.strengthen(iv2_values, dstore=dstore,
- constraint=(verbosity>1 or iv2 in tracevar) and
self):
- changed.add(iv2)
- # iv1 must be between the min of its domain and the max of iv2's
domain
- # (iv2's domain may have changed)
- iv1_values = set(range(min(d1), max(iv2.get_domain(dstore=dstore))
+ 1))
- # Maximum value of sv2's upper bound constrains sv1's upper card
- if iv1.strengthen(iv1_values, dstore=dstore,
- constraint=(verbosity>1 or iv1 in tracevar) and
self):
- changed.add(iv1)
-
- if verbosity > 1 and changed:
- print(' Variables {} changed'.format(changed))
- return Constraint.sleeping, changed
-
-class CardinalityEq(Constraint):
- """Set variable's cardinality is constrained to be equal to value of
IVar."""
-
- def __init__(self, variables, problem=None, weight=1):
- Constraint.__init__(self, variables, problem=problem,
- weight=weight)
-
self.sv = variables[0]
- self.iv = variables[1]
-
self.name = '|{0}| = {1}'.format(
self.sv, self.iv)
-
- def fails(self, dstore=None):
- """Is the sv's lower cardinality bound > max of iv's domain?"""
- if self.iv.determined(dstore=dstore) is not False and
self.sv.determined(dstore=dstore) is not False:
-# print('Both vars determined: {}, {}'.format(self.iv,
self.sv))
- if self.iv.get_value(dstore=dstore) !=
self.sv.get_upper_card(dstore=dstore):
- return True
- if self.sv.get_lower_card(dstore=dstore) >
max(self.iv.get_domain(dstore=dstore)):
- return True
- if min(self.iv.get_domain(dstore=dstore)) >
self.sv.get_upper_card(dstore=dstore):
- return True
- return False
-
- def is_entailed(self, dstore=None):
- """Is the variable's upper cardinality bound already = iv?"""
- if self.iv.determined(dstore=dstore) is not False and
self.sv.determined(dstore=dstore) is not False:
- if self.sv.get_upper_card(dstore=dstore) ==
self.iv.get_value(dstore=dstore):
- return True
- return False
-
- def infer(self, dstore=None, verbosity=0, tracevar=None):
- """sv's upper cardinality is restricted to be <= min of iv's
domain.
- iv's domain is restricted to values >= lower cardinality of sv."""
- state = Constraint.sleeping
- changed = set()
- sv =
self.sv
- iv = self.iv
- sv_low_card = sv.get_lower_card(dstore=dstore)
- sv_up_card = sv.get_upper_card(dstore=dstore)
- if iv.strengthen(set(range(sv_low_card, sv.max)), dstore=dstore,
- constraint=(verbosity>1 or iv in tracevar) and
self):
- changed.add(iv)
- return state, changed
- if iv.strengthen(set(range(0, sv_up_card + 1)), dstore=dstore,
- constraint=(verbosity>1 or iv in tracevar) and
self):
- changed.add(iv)
- return state, changed
- iv_dom = iv.get_domain(dstore=dstore)
- if sv.strengthen_lower_card(min(iv_dom), dstore=dstore,
- constraint=(verbosity>1 or sv in
tracevar) and self):
- changed.add(sv)
- return state, changed
- if sv.strengthen_upper_card(max(iv_dom), dstore=dstore,
- constraint=(verbosity>1 or sv in
tracevar) and self):
- changed.add(sv)
- return state, changed
+### Set cardinality
+##
+##class CardinalityGEQ(Constraint):
+## """Set variable's cardinality is constrained to be >= lower bound."""
+##
+## def __init__(self, var, lower, problem=None):
+## Constraint.__init__(self, (var,), problem=problem)
+## self.lower = lower
+##
self.name = '|{0}|>={1}'.format(self.get_var(), self.lower)
+##
+## def fails(self, dstore=None):
+## """Is the var's upper cardinality bound < lower?"""
+## if self.get_var().get_upper_card(dstore=dstore) < self.lower:
+## return True
+## return False
+##
+## def is_entailed(self, dstore=None):
+## """Is the variable's lower cardinality bound already >= lower?"""
+## if self.get_var().get_lower_card(dstore=dstore) >= self.lower:
+## return True
+## return False
+##
+## def infer(self, dstore=None, verbosity=0, tracevar=None):
+## """The variable's cardinality is restricted be >= lower: lower
bound
+## is raised if necessary."""
+## var = self.get_var()
+## if var.strengthen_lower_card(self.lower, dstore=dstore,
+## constraint=(verbosity>1 or var in
tracevar) and self):
+## return Constraint.entailed, {var}
+## return Constraint.entailed, set()
+##
+##class CardinalityLEQ(Constraint):
+## """Set variable's cardinality is constrained to be <= upper bound."""
+##
+## def __init__(self, var, upper, problem=None):
+## Constraint.__init__(self, (var,), problem=problem)
+## self.upper = upper
+##
self.name = '|{0}| c= {1}'.format(self.get_var(), self.upper)
+##
+## def fails(self, dstore=None):
+## """Is the var's lower cardinality bound > upper?"""
+## if self.get_var().get_lower_card(dstore=dstore) > self.upper:
+## return True
+## return False
+##
+## def is_entailed(self, dstore=None):
+## """Is the variable's upper cardinality bound already <= upper?"""
+## if self.get_var().get_upper_card(dstore=dstore) <= self.upper:
+## return True
+## return False
+##
+## def infer(self, dstore=None, verbosity=0, tracevar=None):
+## """The variable's cardinality is restricted to be <= upper:
+## upper bound is lowered if necessary."""
+## var = self.get_var()
+## if var.strengthen_upper_card(self.upper, dstore=dstore,
+## constraint=(verbosity>1 or var in
tracevar) and self):
+## return Constraint.entailed, {var}
+## return Constraint.entailed, set()
+##
+##### Constraints that propagate
+##
+#### Primitive propagators
+##
+### Integer domain variables only
+##
+##class LessThan(Constraint):
+## """IVar1 is less than or equal to IVar2."""
+##
+## def __init__(self, variables, problem=None, weight=1):
+## Constraint.__init__(self, variables, problem=problem,
+## weight=weight)
+##
self.name = '{0} <= {1}'.format(self.get_iv1(), self.get_iv2())
+##
+## def get_iv1(self):
+## return self.variables[0]
+##
+## def get_iv2(self):
+## return self.variables[1]
+##
+## def fails(self, dstore=None):
+## """
+## Fail if min of domain1 > max of domain2.
+## """
+## iv1 = self.get_iv1()
+## iv2 = self.get_iv2()
+## min1 = min(iv1.get_domain(dstore=dstore))
+## max2 = max(iv2.get_domain(dstore=dstore))
+## if min1 > max2:
+## return True
+## return False
+##
+## def is_entailed(self, dstore=None):
+## """Entailed if max of domain1 <= min of domain2."""
+## iv1 = self.get_iv1()
+## iv2 = self.get_iv2()
+## max1 = max(iv1.get_domain(dstore=dstore))
+## min2 = min(iv2.get_domain(dstore=dstore))
+## if max1 <= min2:
+## return True
+## return False
+##
+## def infer(self, dstore=None, verbosity=0, tracevar=None):
+## changed = set()
+## iv1 = self.get_iv1()
+## iv2 = self.get_iv2()
+## d1 = iv1.get_domain(dstore=dstore)
+## d2 = iv2.get_domain(dstore=dstore)
+## # iv2 must be between the min of iv1's domain and the maximum
value
+## iv2_values = set(range(min(d1), max(d2) + 1))
+## if iv2.strengthen(iv2_values, dstore=dstore,
+## constraint=(verbosity>1 or iv2 in tracevar)
and self):
+## changed.add(iv2)
+## # iv1 must be between the min of its domain and the max of iv2's
domain
+## # (iv2's domain may have changed)
+## iv1_values = set(range(min(d1),
max(iv2.get_domain(dstore=dstore)) + 1))
+## # Maximum value of sv2's upper bound constrains sv1's upper card
+## if iv1.strengthen(iv1_values, dstore=dstore,
+## constraint=(verbosity>1 or iv1 in tracevar)
and self):
+## changed.add(iv1)
+##
+## if verbosity > 1 and changed:
+## print(' Variables {} changed'.format(changed))
+## return Constraint.sleeping, changed
+##
+##class CardinalityEq(Constraint):
+## """Set variable's cardinality is constrained to be equal to value of
IVar."""
+##
+## def __init__(self, variables, problem=None, weight=1):
+## Constraint.__init__(self, variables, problem=problem,
+## weight=weight)
+##
self.sv = variables[0]
+## self.iv = variables[1]
+##
self.name = '|{0}| = {1}'.format(
self.sv, self.iv)
+##
+## def fails(self, dstore=None):
+## """Is the sv's lower cardinality bound > max of iv's domain?"""
+## if self.iv.determined(dstore=dstore) is not False and
self.sv.determined(dstore=dstore) is not False:
+### print('Both vars determined: {}, {}'.format(self.iv,
self.sv))
+## if self.iv.get_value(dstore=dstore) !=
self.sv.get_upper_card(dstore=dstore):
+## return True
+## if self.sv.get_lower_card(dstore=dstore) >
max(self.iv.get_domain(dstore=dstore)):
+## return True
+## if min(self.iv.get_domain(dstore=dstore)) >
self.sv.get_upper_card(dstore=dstore):
+## return True
+## return False
+##
+## def is_entailed(self, dstore=None):
+## """Is the variable's upper cardinality bound already = iv?"""
+## if self.iv.determined(dstore=dstore) is not False and
self.sv.determined(dstore=dstore) is not False:
+## if self.sv.get_upper_card(dstore=dstore) ==
self.iv.get_value(dstore=dstore):
+## return True
+## return False
+##
+## def infer(self, dstore=None, verbosity=0, tracevar=None):
+## """sv's upper cardinality is restricted to be <= min of iv's
domain.
+## iv's domain is restricted to values >= lower cardinality of
sv."""
+## state = Constraint.sleeping
+## changed = set()
+## sv =
self.sv
+## iv = self.iv
+## sv_low_card = sv.get_lower_card(dstore=dstore)
+## sv_up_card = sv.get_upper_card(dstore=dstore)
+## if iv.strengthen(set(range(sv_low_card, sv.max)), dstore=dstore,
+## constraint=(verbosity>1 or iv in tracevar) and
self):
+## changed.add(iv)
+## return state, changed
+## if iv.strengthen(set(range(0, sv_up_card + 1)), dstore=dstore,
+## constraint=(verbosity>1 or iv in tracevar) and
self):
+## changed.add(iv)
+## return state, changed
+## iv_dom = iv.get_domain(dstore=dstore)
+## if sv.strengthen_lower_card(min(iv_dom), dstore=dstore,
+## constraint=(verbosity>1 or sv in
tracevar) and self):
+## changed.add(sv)
+## return state, changed
+## if sv.strengthen_upper_card(max(iv_dom), dstore=dstore,
+## constraint=(verbosity>1 or sv in
tracevar) and self):
+## changed.add(sv)
+## return state, changed
# Set domain variables only
@@ -432,9 +436,9 @@
"""There must not be any 'holes' in the (single) set variable, which
represents
the positions of the descendants of a node as well as that of the node
itself."""
- def __init__(self, var, problem=None, weight=1):
+ def __init__(self, var, problem=None, weight=1, record=True):
"""Only one variable, so a special constructor."""
- Constraint.__init__(self, [var], problem=problem, weight=weight)
+ Constraint.__init__(self, [var], problem=problem, weight=weight,
record=record)
self.var = self.variables[0]
self.name = '{0} <>'.format(self.var)
@@ -548,9 +552,9 @@
class SupersetIntersection(Constraint):
"""Set var S1 is superset of intersection of set vars S2 and S3."""
- def __init__(self, variables, problem=None, weight=1):
+ def __init__(self, variables, problem=None, weight=1, record=True):
Constraint.__init__(self, variables, problem=problem,
- weight=weight)
+ weight=weight, record=record)
self.name = '{0} >= {1} ^ {2}'.format(self.variables[0],
self.variables[1], self.variables[2])
def fails(self, dstore=None):
@@ -621,8 +625,8 @@
"""Set var S1 is subset of union of set vars S2 and S3."""
def __init__(self, variables, problem=None, propagate=True,
- weight=1):
- Constraint.__init__(self, variables, problem=problem,
weight=weight)
+ weight=1, record=True):
+ Constraint.__init__(self, variables, problem=problem,
weight=weight, record=record)
self.name = '{0} c= {1} U {2}'.format(self.variables[0],
self.variables[1], self.variables[2])
def fails(self, dstore=None):
@@ -682,104 +686,104 @@
print(' Variables {} changed'.format(changed))
return Constraint.sleeping, changed
-class CardinalitySubset(Constraint):
- """Cardinality of set variable 1 is within set variable 2. This
constraint is not included
- in Müller, but it is needed for XDG valency.
- It could be handled with IVMemberSV."""
-
- def __init__(self, variables, problem=None, weight=1):
- Constraint.__init__(self, variables, problem=problem,
- weight=weight)
-
self.name = '|{0}| c= {1}'.format(self.get_sv1(), self.get_sv2())
-
- def get_sv1(self):
- return self.variables[0]
-
- def get_sv2(self):
- return self.variables[1]
-
- def fails(self, dstore=None):
- """Fail if minimum cardinality of SV1 is greater than maximum
possible value of SV2
- or if maximum cardinality of SV1 is less than the minimum possible
value of SV2.
- Fixed 2011.12.09: minimum possible value of SV2 is minimum of
*upper* bound, not
- lower bound."""
- sv1 = self.get_sv1()
- sv2 = self.get_sv2()
- upper2 = sv2.get_upper(dstore=dstore)
- max2card = max(upper2) if upper2 else 0
- if sv1.get_lower_card(dstore=dstore) > max2card:
- return True
-# lower2 = sv2.get_lower(dstore=dstore)
- min2card = min(upper2) if upper2 else 0
- # min(lower2) if lower2 else 0
- if sv1.get_upper_card(dstore=dstore) < min2card:
- return True
- return False
-
- def is_entailed(self, dstore=None):
- """Entailed if cardinality of SV1 determined, SV2 determined, and
the former is in the latter."""
- sv1 = self.get_sv1()
- sv2 = self.get_sv2()
- if sv2.determined(dstore=dstore, constraint=self) is not False and
\
- sv1.get_lower_card(dstore=dstore) ==
sv1.get_upper_card(dstore=dstore) in sv2.get_value(dstore=dstore):
- return True
- return False
-
- def infer(self, dstore=None, verbosity=0, tracevar=None):
- changed = set()
- state = Constraint.sleeping
- sv1 = self.get_sv1()
- sv2 = self.get_sv2()
- sv1_low_card = sv1.get_lower_card(dstore=dstore)
- sv1_up_card = sv1.get_upper_card(dstore=dstore)
-# if tracevar in self.variables:
-# print(self, 'INFERRING')
- # If sv1's cardinality is determined, then it must be in sv2
- if sv1_low_card == sv1_up_card:
-# print('SV1 {} has same upper and lower card {}'.format(sv1,
sv1_low_card))
- if sv2.strengthen_lower({sv1_low_card}, dstore=dstore,
- constraint=(verbosity>1 or sv2 in
tracevar) and self):
-# constraint=self):
-# if sv2.determine({sv1_low_card}, dstore=dstore,
-# constraint=(verbosity>1 or sv2 in tracevar)
and self):
- changed.add(sv2)
- return state, changed
-
-# if tracevar in self.variables:
-# print(self, 'GOT TO 0')
-
- sv2_upper = sv2.get_upper(dstore=dstore)
-# sv2_lower = sv2.get_lower(dstore=dstore)
-
- # Minimum value of sv2 constrains sv1's lower card
- # Fixed 2011.12.09: minimum value of sv2 is min of *upper* bound,
not lower
- if sv2_upper:
- # Could be empty set, in which case no strengthening is
possible
- if sv1.strengthen_lower_card(min(sv2_upper), dstore=dstore,
- constraint=(verbosity>1 or sv1 in
tracevar) and self):
- changed.add(sv1)
- return state, changed
-
-# if tracevar in self.variables:
-# print(self, 'GOT TO 1')
- # Maximum value of sv2's upper bound constrains sv1's upper card
- upcard = max(sv2_upper) if sv2_upper else 0
- if sv1.strengthen_upper_card(upcard, dstore=dstore,
constraint=(verbosity>1 or sv1 in tracevar) and self):
- changed.add(sv1)
- return state, changed
-# if tracevar in self.variables:
-# print(self, 'GOT TO 2')
-
- if verbosity > 1 and changed:
- print(' Variables {} changed'.format(changed))
- return state, changed
+##class CardinalitySubset(Constraint):
+## """Cardinality of set variable 1 is within set variable 2. This
constraint is not included
+## in Müller, but it is needed for XDG valency.
+## It could be handled with IVMemberSV."""
+##
+## def __init__(self, variables, problem=None, weight=1):
+## Constraint.__init__(self, variables, problem=problem,
+## weight=weight)
+##
self.name = '|{0}| c= {1}'.format(self.get_sv1(), self.get_sv2())
+##
+## def get_sv1(self):
+## return self.variables[0]
+##
+## def get_sv2(self):
+## return self.variables[1]
+##
+## def fails(self, dstore=None):
+## """Fail if minimum cardinality of SV1 is greater than maximum
possible value of SV2
+## or if maximum cardinality of SV1 is less than the minimum
possible value of SV2.
+## Fixed 2011.12.09: minimum possible value of SV2 is minimum of
*upper* bound, not
+## lower bound."""
+## sv1 = self.get_sv1()
+## sv2 = self.get_sv2()
+## upper2 = sv2.get_upper(dstore=dstore)
+## max2card = max(upper2) if upper2 else 0
+## if sv1.get_lower_card(dstore=dstore) > max2card:
+## return True
+### lower2 = sv2.get_lower(dstore=dstore)
+## min2card = min(upper2) if upper2 else 0
+## # min(lower2) if lower2 else 0
+## if sv1.get_upper_card(dstore=dstore) < min2card:
+## return True
+## return False
+##
+## def is_entailed(self, dstore=None):
+## """Entailed if cardinality of SV1 determined, SV2 determined,
and the former is in the latter."""
+## sv1 = self.get_sv1()
+## sv2 = self.get_sv2()
+## if sv2.determined(dstore=dstore, constraint=self) is not False
and \
+## sv1.get_lower_card(dstore=dstore) ==
sv1.get_upper_card(dstore=dstore) in sv2.get_value(dstore=dstore):
+## return True
+## return False
+##
+## def infer(self, dstore=None, verbosity=0, tracevar=None):
+## changed = set()
+## state = Constraint.sleeping
+## sv1 = self.get_sv1()
+## sv2 = self.get_sv2()
+## sv1_low_card = sv1.get_lower_card(dstore=dstore)
+## sv1_up_card = sv1.get_upper_card(dstore=dstore)
+### if tracevar in self.variables:
+### print(self, 'INFERRING')
+## # If sv1's cardinality is determined, then it must be in sv2
+## if sv1_low_card == sv1_up_card:
+### print('SV1 {} has same upper and lower card {}'.format(sv1,
sv1_low_card))
+## if sv2.strengthen_lower({sv1_low_card}, dstore=dstore,
+## constraint=(verbosity>1 or sv2 in
tracevar) and self):
+### constraint=self):
+### if sv2.determine({sv1_low_card}, dstore=dstore,
+### constraint=(verbosity>1 or sv2 in
tracevar) and self):
+## changed.add(sv2)
+## return state, changed
+##
+### if tracevar in self.variables:
+### print(self, 'GOT TO 0')
+##
+## sv2_upper = sv2.get_upper(dstore=dstore)
+### sv2_lower = sv2.get_lower(dstore=dstore)
+##
+## # Minimum value of sv2 constrains sv1's lower card
+## # Fixed 2011.12.09: minimum value of sv2 is min of *upper*
bound, not lower
+## if sv2_upper:
+## # Could be empty set, in which case no strengthening is
possible
+## if sv1.strengthen_lower_card(min(sv2_upper), dstore=dstore,
+## constraint=(verbosity>1 or sv1
in tracevar) and self):
+## changed.add(sv1)
+## return state, changed
+##
+### if tracevar in self.variables:
+### print(self, 'GOT TO 1')
+## # Maximum value of sv2's upper bound constrains sv1's upper card
+## upcard = max(sv2_upper) if sv2_upper else 0
+## if sv1.strengthen_upper_card(upcard, dstore=dstore,
constraint=(verbosity>1 or sv1 in tracevar) and self):
+## changed.add(sv1)
+## return state, changed
+### if tracevar in self.variables:
+### print(self, 'GOT TO 2')
+##
+## if verbosity > 1 and changed:
+## print(' Variables {} changed'.format(changed))
+## return state, changed
class SetPrecedence(Constraint):
"""All elements of set variable 1 must precede all elements of set
variable 2."""
- def __init__(self, variables, problem=None, weight=1):
+ def __init__(self, variables, problem=None, weight=1, record=True):
Constraint.__init__(self, variables, problem=problem,
- weight=weight)
+ weight=weight, record=record)
self.name = '{0} << {1}'.format(self.variables[0],
self.variables[1])
# Also used in PrecedenceSelection
@@ -854,66 +858,66 @@
return state, changed
return state, changed
-# Integer domain and set domain variables
-
-class IVMemberSV(Constraint):
- """Integer variable value must be member of set variable value."""
-
- def __init__(self, variables, problem=None, propagate=True,
- weight=1):
- Constraint.__init__(self, variables, problem=problem,
propagate=propagate,
- weight=weight)
-
self.name = '{0} c {1}'.format(self.get_iv(), self.get_sv())
-
- def get_iv(self):
- """The domain variable."""
- return self.variables[0]
-
- def get_sv(self):
- """The set variable."""
- return self.variables[1]
-
- def fails(self, dstore=None):
- """Fail if none of the IV values are in SV upper bound."""
- iv = self.get_iv()
- sv = self.get_sv()
- iv_dom = iv.get_domain(dstore=dstore)
- sv_up = sv.get_upper(dstore=dstore)
- if len(iv_dom & sv_up) == 0:
- return True
- return False
-
- def is_entailed(self, dstore=None):
- """Entailed if IV values are subset of SV lower bound."""
- iv = self.get_iv()
- sv = self.get_sv()
- iv_dom = iv.get_domain(dstore=dstore)
- sv_low = sv.get_lower(dstore=dstore)
-# if self.pattern:
-# # For patterns, the propagator is entailed if every element
in the domain of iv
-# # unifies with the lower bound of sv
-# if all([unify_fssets({tup}, sv_low) for tup in iv_dom]):
-# return True
- if iv_dom <= sv_low:
- return True
- return False
-
- def infer(self, dstore=None, verbosity=0, tracevar=None):
- changed = set()
- iv = self.get_iv()
- sv = self.get_sv()
- # Constrain the values of IV to be within upper bound of SV
- if iv.strengthen(sv.get_upper(dstore=dstore), dstore=dstore,
- constraint=(verbosity>1 or iv in tracevar) and
self):
- changed.add(iv)
- # If IV is determined, constrain SV to include it
- if iv.determined(dstore=dstore, verbosity=verbosity) is not False:
- if sv.strengthen_lower(iv.get_domain(dstore=dstore),
dstore=dstore,
- constraint=(verbosity>1 or sv in
tracevar) and self):
- changed.add(sv)
- if verbosity > 1 and changed:
- print(' Variables {} changed'.format(changed))
- return Constraint.sleeping, changed
+### Integer domain and set domain variables
+##
+##class IVMemberSV(Constraint):
+## """Integer variable value must be member of set variable value."""
+##
+## def __init__(self, variables, problem=None, propagate=True,
+## weight=1):
+## Constraint.__init__(self, variables, problem=problem,
propagate=propagate,
+## weight=weight)
+##
self.name = '{0} c {1}'.format(self.get_iv(), self.get_sv())
+##
+## def get_iv(self):
+## """The domain variable."""
+## return self.variables[0]
+##
+## def get_sv(self):
+## """The set variable."""
+## return self.variables[1]
+##
+## def fails(self, dstore=None):
+## """Fail if none of the IV values are in SV upper bound."""
+## iv = self.get_iv()
+## sv = self.get_sv()
+## iv_dom = iv.get_domain(dstore=dstore)
+## sv_up = sv.get_upper(dstore=dstore)
+## if len(iv_dom & sv_up) == 0:
+## return True
+## return False
+##
+## def is_entailed(self, dstore=None):
+## """Entailed if IV values are subset of SV lower bound."""
+## iv = self.get_iv()
+## sv = self.get_sv()
+## iv_dom = iv.get_domain(dstore=dstore)
+## sv_low = sv.get_lower(dstore=dstore)
+### if self.pattern:
+### # For patterns, the propagator is entailed if every element
in the domain of iv
+### # unifies with the lower bound of sv
+### if all([unify_fssets({tup}, sv_low) for tup in iv_dom]):
+### return True
+## if iv_dom <= sv_low:
+## return True
+## return False
+##
+## def infer(self, dstore=None, verbosity=0, tracevar=None):
+## changed = set()
+## iv = self.get_iv()
+## sv = self.get_sv()
+## # Constrain the values of IV to be within upper bound of SV
+## if iv.strengthen(sv.get_upper(dstore=dstore), dstore=dstore,
+## constraint=(verbosity>1 or iv in tracevar) and
self):
+## changed.add(iv)
+## # If IV is determined, constrain SV to include it
+## if iv.determined(dstore=dstore, verbosity=verbosity) is not
False:
+## if sv.strengthen_lower(iv.get_domain(dstore=dstore),
dstore=dstore,
+## constraint=(verbosity>1 or sv in
tracevar) and self):
+## changed.add(sv)
+## if verbosity > 1 and changed:
+## print(' Variables {} changed'.format(changed))
+## return Constraint.sleeping, changed
# Selection constraint propagators
@@ -927,9 +931,9 @@
"""
def __init__(self, mainvar=None, selvar=None, seqvars=None,
- problem=None, weight=1):
+ problem=None, weight=1, record=True):
Constraint.__init__(self, [mainvar, selvar] + seqvars,
problem=problem,
- weight=weight)
+ weight=weight, record=record)
self.selvar = selvar
self.mainvar = mainvar
self.seqvars = seqvars
@@ -1029,254 +1033,254 @@
string += elem.__repr__()
return string + ']'
-class IntSelection(Selection):
- """Selection constraint with integer variable as selection variable.
- Müller treats this as derived from UnionSelection, but it is more
efficient
- to treat it as a primitive, at least in this program.
- """
-
- def __init__(self, mainvar=None, selvar=None, seqvars=None,
problem=None, weight=1,
- maxset=None):
- Selection.__init__(self, mainvar=mainvar, selvar=selvar,
seqvars=seqvars,
- problem=problem, weight=weight)
- self.maxset = maxset or ALL
-
self.name = '{0} = {1} [{2}]'.format(self.mainvar,
self.format_seq(self.seqvars), self.selvar)
-
- def fails(self, dstore=None):
- """Fail if the domain of sel var includes only indices that are
beyond the
- length of seq vars, of if the domain of sel var is empty."""
- selvar = self.selvar
- sel_domain = selvar.get_domain(dstore=dstore)
- if not sel_domain or min(sel_domain) >= len(self.seqvars):
- return True
- elif selvar.determined(dstore=dstore, constraint=self) is not
False:
- # If the selvar is determined, check the selected seq var
- seqvar = self.seqvars[selvar.get_value(dstore=dstore)]
- if seqvar.determined(dstore=dstore, constraint=self) is not
False:
- # The seqvar is determined too; see if it cannot be equal
to the mainvar
- # This is true if the upper bound of mainvar fails to
contain values in seqvar's value
- seqval = seqvar.get_value(dstore=dstore)
- # In case seqvar is an IVar, make its value a set
- if not isinstance(seqval, set):
- seqval = {seqval}
- mainvar = self.mainvar
- mainvar_upper = mainvar.get_upper(dstore=dstore)
- if seqval - mainvar_upper:
- return True
- # It's also true if the lower bound of mainvar contains
values not in seqvar's value
- mainvar_lower = mainvar.get_lower(dstore=dstore)
- if mainvar_lower - seqval:
- return True
- return False
-
- def infer(self, dstore=None, verbosity=0, tracevar=None):
- seqvars = self.seqvars
- selvar = self.selvar
- mainvar = self.mainvar
- changed = set()
- state = Constraint.sleeping
-
- sel_infer = Selection.infer(self, dstore=dstore,
verbosity=verbosity, tracevar=tracevar)
- if sel_infer:
- return sel_infer
-
- if selvar.determined(dstore=dstore, verbosity=verbosity,
constraint=self) is not False:
- # The selection var is determined
-
- # index of selected seq var
- selval = selvar.get_value(dstore=dstore)
- # selected seq var
- sel_seqvar = seqvars[selval]
-
- # If the selection var is determined, check whether the
indexed sequence var
- # is also
-
- if sel_seqvar.determined(dstore=dstore, verbosity=verbosity,
constraint=self) is not False:
- selseq_value = sel_seqvar.get_value(dstore=dstore)
- # If it's determined, determine the main var
- if mainvar.determine(selseq_value, dstore=dstore,
- constraint=(verbosity>1 or mainvar in
tracevar) and self):
- changed.add(mainvar)
- state = Constraint.entailed
- if verbosity > 1 and changed:
- print(' Variables {} changed'.format(changed))
-# if self.det_seqs:
-# state = self.determine_seqs(dstore=dstore,
verbosity=verbosity, tracevar=tracevar, changed=changed)
- return state, changed
-
- # Otherwise check whether the main var is determined, in which
case the selected
- # seq var can be constrained to its value
- if mainvar.determined(dstore=dstore, verbosity=verbosity,
constraint=self) is not False:
- mainvalue = mainvar.get_value(dstore=dstore)
- if sel_seqvar.determine(mainvalue, dstore=dstore,
- constraint=(verbosity>1 or
sel_seqvar in tracevar) and self):
- changed.add(sel_seqvar)
- return state, changed
-
- sel_domain = selvar.get_domain(dstore=dstore)
-
- # The main variable must be a subset of the union of the upper
bounds of all
- # sequence variables indexed by the elements in the domain of the
selection variable,
- # that is, eliminate any elements from the upper bound that are
not in this union
- seq_uppers = [seqvars[j].get_upper(dstore=dstore) for j in
sel_domain]
- if mainvar.strengthen_upper(set().union(*seq_uppers),
dstore=dstore,
- constraint=(verbosity>1 or mainvar in
tracevar) and self):
- changed.add(mainvar)
- return state, changed
-
- # The main variable must be a superset of the intersection of the
lower bounds of all
- # sequence variables indexed by the lower bound of the selection
variable, that is,
- # add any elements to the lower bound that are in this intersection
- seq_lowers = [seqvars[j].get_lower(dstore=dstore) for j in
sel_domain]
- if
mainvar.strengthen_lower(seq_lowers[0].intersection(*seq_lowers[1:]),
dstore=dstore,
- constraint=(verbosity>1 or mainvar in
tracevar) and self):
- changed.add(mainvar)
- return state, changed
-
- # If the lower bound of some seqvar is not a subset of mainvar's
upper bound,
- # or the upper bound of the seqvar is not a superset of mainvar's
lower bound,
- # then exclude its index from selection var's domain
- for j in sel_domain.copy():
- seqvar = seqvars[j]
- if not seqvar.get_lower(dstore=dstore) <=
mainvar.get_upper(dstore=dstore) or \
- not seqvar.get_upper(dstore=dstore) >=
mainvar.get_lower(dstore=dstore):
- # mainvar cannot equal seqvar, so remove j from selvar's
domain
- if selvar.discard_value(j, dstore=dstore,
- constraint=(verbosity>1 or selvar
in tracevar) and self):
- changed.add(selvar)
- return state, changed
-
- # If excluding any index from selection var's domain in figuring
the
- # union of upper bounds of indexed sequence variables causes the
lower bound
- # of the main variable to contain elements not in the union,
- # then that index must be the value of the selection var.
- for j in selvar.get_domain(dstore=dstore): # Might have changed
- # Consider only indices that in the upper bound of selection
variable
- # Exclude j
- indices = sel_domain - {j}
- # Get the union of the upper bounds of the indexed sequence
variables
- seqvar_union =
set().union(*[seqvars[i].get_upper(dstore=dstore) for i in indices])
- # Does the lower bound of the main variables have any elements
not in the union?
- main_union_diff = mainvar.get_lower(dstore=dstore) -
seqvar_union
- if len(main_union_diff) > 0:
- # Yes; add the difference to the excluded seq var's lower
bound
- if seqvars[j].strengthen_lower(main_union_diff,
dstore=dstore,
- constraint=(verbosity>1 or
seqvars[j] in tracevar) and self):
- changed.add(seqvars[j])
- return state, changed
- # and make j the value of the selection var
- if selvar.determine(j, dstore=dstore,
- constraint=(verbosity>1 or selvar in
tracevar) and self):
- changed.add(selvar)
- return state, changed
-
- if verbosity > 1 and changed:
- print(' Variables {} changed'.format(changed))
-
- return state, changed
-
-class OneSelection(IntSelection):
- """Select one of seq vars for main var."""
-
- def __init__(self, mainvar, seqvars, problem=None, weight=1):
- selvar = IVar(
mainvar.name + ':choice', set(range(len(seqvars))),
problem=problem,
- rootDS=problem.dstore if problem else None)
- IntSelection.__init__(self, mainvar, selvar, seqvars,
problem=problem, weight=weight)
-
-class IntIntSelection(Selection):
- """Selection constraint with integer variables as selection variable
and main
- variable and determined int variables as seq variables."""
-
- def __init__(self, mainvar=None, selvar=None, seqvars=None,
problem=None, weight=1,
- maxset=None):
- Selection.__init__(self, mainvar=mainvar, selvar=selvar,
seqvars=seqvars,
- problem=problem, weight=weight)
- self.maxset = maxset or ALL
-
self.name = '{0} = {1} [{2}]'.format(self.mainvar,
self.format_seq(self.seqvars), self.selvar)
-
- def fails(self, dstore=None):
- """Fail if the domain of sel var includes only indices that are
beyond the
- length of seq vars, of if the domain of sel var is empty."""
- selvar = self.selvar
- sel_domain = selvar.get_domain(dstore=dstore)
- if not sel_domain or min(sel_domain) >= len(self.seqvars):
- return True
- elif selvar.determined(dstore=dstore, constraint=self) is not
False:
- # If the selvar is determined, check the selected seq var
- seqvar = self.seqvars[selvar.get_value(dstore=dstore)]
- # The seqvar is determined too; see if it cannot be equal to
the mainvar
- # This is true if the upper bound of mainvar fails to contain
values in seqvar's value
- seqval = {seqvar.get_value(dstore=dstore)}
- mainvar = self.mainvar
- mainvar_domain = mainvar.get_domain(dstore=dstore)
- if seqval - mainvar_domain:
- return True
- return False
-
- def infer(self, dstore=None, verbosity=0, tracevar=None):
- seqvars = self.seqvars
- selvar = self.selvar
- mainvar = self.mainvar
- changed = set()
- state = Constraint.sleeping
-
- sel_infer = Selection.infer(self, dstore=dstore,
verbosity=verbosity, tracevar=tracevar)
- if sel_infer:
- return sel_infer
-
- if selvar.determined(dstore=dstore, verbosity=verbosity,
constraint=self) is not False:
- # The selection var is determined
-
- # index of selected seq var
- selval = selvar.get_value(dstore=dstore)
***The diff for this file has been truncated for email.***
=======================================
--- /l3lite/cs.py Wed Apr 30 06:56:30 2014 UTC
+++ /l3lite/cs.py Thu May 1 06:39:44 2014 UTC
@@ -53,26 +53,39 @@
def __repr__(self):
return "Solver {}".format(
self.id)
- def fixed_point(self, awaken):
- print("Fixed point? len awaken: {}".format(len(awaken)))
- if len(awaken) == 0:
- # No more constraints are awake
- if self.dstore.is_determined():
- # All variables are determined in the dstore or
peripheral: success
- self.status = Solver.succeeded
- else:
+ def exit(self, result):
+ if result == Constraint.failed:
+ return True
+ else:
+ return self.fixed_point(result)
+
+ def fixed_point(self, awaken, verbosity=0):
+ s = "# constraints to awaken: {}, # variables to determine: {}|{}"
+ print(s.format(len(awaken), len(self.dstore.ess_undet),
len(self.dstore.undetermined)))
+ if self.dstore.is_determined():
+ # All essential variables are determined
+ self.status = Solver.succeeded
+ return True
+ elif len(awaken) == 0:
+# # No more constraints are awake
+# if self.dstore.is_determined():
+# # All variables are determined in the dstore or
peripheral: success
+# self.status = Solver.succeeded
+# else:
# No more constraints apply: continue search
- self.status = Solver.distributable
+ # More variables to determine; we have to distribute
+ self.status = Solver.distributable
return True
# Keep propagating
return False
def run(self, verbosity=0, tracevar=[]):
- s = "Running {} with {} undetermined variables, {} constraints"
- print(s.format(self, len(self.dstore.ess_undet),
len(self.constraints)))
+ """Run the constraints until CS fails or a fixed point is
reached."""
+ s = "Running {} with {}|{} undetermined variables, {} constraints"
+ print(s.format(self, len(self.dstore.ess_undet),
len(self.dstore.undetermined), len(self.constraints)))
awaken = set(self.constraints)
it = 0
- while not self.fixed_point(awaken):
+ while not self.exit(awaken):
print("\nRunning iteration {}".format(it))
awaken = self.run_constraints(awaken, verbosity=verbosity,
tracevar=tracevar)
it += 1
@@ -92,29 +105,31 @@
if constraint in awaken:
awaken.remove(constraint)
+ if state == Constraint.failed:
+ print("FAILED {}".format(constraint))
+ return Constraint.failed
+# # constraint fails; remove it from the entailed or awaken
lists if it's there
+# if constraint in self.entailed:
+# self.entailed.remove(constraint)
+# if constraint in awaken:
+# awaken.remove(constraint)
+## # penalize the CSpace
+## self.penalty += constraint.weight
+# # and remember that it failed
+# self.failed.append(constraint)
+
# Check whether any of the changed vars cannot possibly be
determined; if so,
# the constraint fails
- if state != Constraint.failed:
- for var in changed_vars:
- try:
- var.determined(dstore=self.dstore,
verbosity=verbosity)
- except VarError:
- if verbosity:
- print("{} CAN'T BE DETERMINED, SO {} MUST
FAIL".format(var, constraint))
- state = Constraint.failed
- break
-
- if state == Constraint.failed:
- print("FAILED {}!".format(constraint))
- # constraint fails; remove it from the entailed or awaken
lists if it's there
- if constraint in self.entailed:
- self.entailed.remove(constraint)
- if constraint in awaken:
- awaken.remove(constraint)
-# # penalize the CSpace
-# self.penalty += constraint.weight
- # and remember that it failed
- self.failed.append(constraint)
+# if state != Constraint.failed:
+ for var in changed_vars:
+ try:
+ var.determined(dstore=self.dstore, verbosity=verbosity)
+ except VarError:
+ if verbosity:
+ print("{} CAN'T BE DETERMINED, SO {} MUST
FAIL".format(var, constraint))
+ return Constraint.failed
+# state = Constraint.failed
+# break
# if self.penalty > self.max_penalty:
# # CSpace fails without running other constraints
@@ -124,17 +139,18 @@
# return CSpace.failed
# If the constraint succeeds, add the constraints of its
variables to awaken
- if state not in [Constraint.failed]:
+# if state not in [Constraint.failed]:
# print(" Awakening, # changed vars
{}".format(len(changed_vars)))
- for var in changed_vars:
- # Add constraints for changed var to awaken unless
those constraints are already entailed
- # or failed
- update_cons = {c for c in var.constraints if c not in
self.entailed and c not in self.failed}
+ for var in changed_vars:
+ # Add constraints for changed var to awaken unless those
constraints are already entailed
+ # or failed
+ update_cons = {c for c in var.constraints if c not in
self.entailed and c not in self.failed}
# print(" Awakening {} constraints for
{}".format(len(update_cons), var))
- if var == tracevar and verbosity:
- print('Adding {} constraints for changed variable
{}'.format(len(update_cons), tracevar))
- awaken.update(update_cons)
+ if var == tracevar and verbosity:
+ print('Adding {} constraints for changed variable
{}'.format(len(update_cons), tracevar))
+ awaken.update(update_cons)
# print('update cons {}'.format(update_cons))
+# if verbosity:
print('# changed vars {}'.format(len(all_changed)))
return awaken
=======================================
--- /l3lite/entry.py Sun Apr 27 18:07:59 2014 UTC
+++ /l3lite/entry.py Thu May 1 06:39:44 2014 UTC
@@ -45,6 +45,8 @@
# -- Created simpler Group (with no dependency types), renamed old Group
to MWE.
# 2014.04.20
# -- Matching of group and sentence nodes.
+# 2014.04.30
+# -- Eliminated everything but Groups.
import copy, itertools
import yaml
@@ -137,505 +139,505 @@
raise(EntryError(s.format(trans,
self.name)))
transdict[trans]['c'] += count
-class Lex(Entry):
-
- cloneID = 1
-
- def __init__(self, name, language, cls=None, id=0, group=False):
- """In addition to Entry features, initialize
- depsin, depsout, order, agr, gov, grams, and (for word and lexeme)
class."""
- Entry.__init__(self, name, language, id=id)
- self.depsin = None
- self.depsout = None
- self.order = None
- self.agr = None
-
self.gov = None
- self.grams = None
- self.cls = cls
- self.cloneID = 0
- # Whether entry is part of a group
- self.group = group
-
- def __repr__(self):
- """Print name and a unique identifier."""
- return '<{}:{}{}>'.format(
self.name,
self.id, ';' +
str(self.cloneID) if self.cloneID else '')
-
- ## Cloning
- ## Needed for groups, which consist of copies of lexes and
- ## for L3 node entries
-
- def clone(self, group=True):
- copied = Lex(
self.name, self.language, cls=self.cls, id=
self.id,
group=group)
- copied.depsin = self.depsin
- copied.depsout = self.depsout
- copied.order = self.order
- copied.agr = self.agr
-
copied.gov =
self.gov
- copied.grams = self.grams
- copied.cloneID = Lex.cloneID
- Lex.cloneID += 1
- return copied
-
- ## Serialization
-
- def to_dict(self):
- """Convert the lex to a dictionary to be serialized in a yaml
file."""
- d = Entry.to_dict(self)
- if self.depsin:
- d['depsin'] = copy.deepcopy(self.depsin)
- if self.depsout:
- d['depsout'] = copy.deepcopy(self.depsout)
- if self.order:
- d['order'] = copy.deepcopy(self.order)
- if self.agr:
- d['agr'] = copy.deepcopy(self.agr)
- if
self.gov:
- d['gov'] = copy.deepcopy(
self.gov)
- if self.grams:
- d['grams'] = self.grams.copy()
- if self.cls:
- d['cls'] = self.cls
- return d
-
- @staticmethod
- def from_dict(d, language):
- """Convert a dict (loaded from a yaml file) to a Lex object."""
- l = Lex(d.get('name'), language, cls=d.get('cls'))
- if d.get('depsin'):
- l.depsin = d.get('depsin')
- if d.get('depsout'):
- l.depsout = d.get('depsout')
- if d.get('order'):
- l.order = d.get('order')
- if d.get('agr'):
- l.agr = d.get('agr')
- if d.get('gov'):
-
l.gov = d.get('gov')
- if d.get('grams'):
- l.grams = d.get('grams')
- return l
-
- ## Dependencies (word, lexeme, class entries)
-
- def get_depin(self, label, create=False):
- """Get the dict of features of incoming dependencies with label,
creating
- the dict if it's not there and create is True."""
- if self.depsin is None:
- self.depsin = {}
- if create and label not in self.depsin:
- self.depsin[label] = {}
- self.language.record_label(label)
- return self.depsin.get(label)
-
- def add_depin(self, label, feats):
- """Assign feats (a dictionary) to features for incoming
dependencies with label,
- or update the current features."""
- d = self.get_depin(label, create=True)
- d.update(feats)
-
- def get_depout(self, label, create=False):
- """Get the dict of features of outgoing dependencies with label,
creating
- the dict if it's not there and create is True."""
- if self.depsout is None:
- self.depsout = {}
- if create and label not in self.depsout:
- self.depsout[label] = {}
- self.language.record_label(label)
- return self.depsout.get(label)
-
- def add_depout(self, label, feats):
- """Assign feats (a dictionary) to features for outgoing
dependencies with label,
- or update the current features."""
- d = self.get_depout(label, create=True)
- d.update(feats)
-
- ## Dependency features
- ## A dict with keys
- ## 'min', 'max', 'dflt', 'maxdist'
-
- def set_deps_feat(self, featdict, key, value):
- featdict[key] = value
-
- def get_deps_feat(self, featdict, key):
- return featdict.get(key)
-
- ## Order constraints
- ## A constraint is a tuple of dependency labels and '^' representing
the head
-
- def get_order(self, create=False):
- """Get the set of order constraint tuples, creating the set if
it's not there
- and create is True."""
- if self.order is None and create:
- self.order = []
- return self.order
-
- def add_order(self, constraint):
- """Add an order constraint tuple to the set of order
constraints."""
- order_constraints = self.get_order(create=True)
- order_constraints.append(constraint)
- self.language.record_order(constraint)
-
- ## Grammatical features associated with words, classes, and lexemes
-
- def get_gram(self, feature, create=False):
- """Get the possible values and their counts for grammatical
feature.
- If this is a word, the value is a string; if a class or lexeme, a
dict
- of values and counts."""
- if self.grams is None:
- self.grams = {}
-# if feature not in self.grams and create:
-# self.grams[feature] = {}
- return self.grams.get(feature)
-
- def set_gram(self, feat, values):
- """Set possible values and their counts for grammatical feature.
- values is a dict of values and their counts."""
- if self.grams is None:
- self.grams = {}
- if feat in self.grams:
- s = "Entry for {} already has a constraint for feature {}"
- raise(EntryError(s.format(
self.name, feat)))
- self.grams[feat] = values
-
- def update_gram_value(self, feat, value, count=1):
- """Add count to the current count for feature value."""
- gram = self.get_gram(feat, create=True)
- if value in gram:
- gram[value] += count
- else:
- gram[value] = count
-
- ## Agreement and government
-
- ## An agreement constraint requires a dependency label, a head
feature, and
- ## and a dependent feature.
-
- def add_agr(self, deplabel, head_feat, dep_feat):
- """Add an agreement constraint to the list of constraints in the
entry."""
- if self.agr is None:
- self.agr = []
- self.agr.append([deplabel, head_feat, dep_feat])
-
- ## A government constraint requires a dependency label, a dependent
feature,
- ## and a dependent value.
-
- def add_gov(self, deplabel, dep_feat, dep_value):
- if
self.gov is None:
-
self.gov = []
- self.gov.append([deplabel, dep_feat, dep_value])
-
- ## Inheritance: copying features from classes to lexemes and words
- ## at initialization
-
- def inherit(self):
- if not self.cls:
- return
- cls = self.language.get_class(self.cls)
- if not cls:
- s = "Class {} for {} does not exist"
- raise(EntryError(s.format(self.cls, self)))
- self.inherit_deps(cls)
- self.inherit_order(cls)
- self.inherit_grams(cls)
- self.inherit_agr(cls)
- self.inherit_gov(cls)
- # Also inherit translation?
-
- def inherit_deps(self, cls):
- """Inherit dependency constraints (in and out) from class."""
- # In
- cls_depsin = cls.depsin
- if cls_depsin:
- if self.depsin is None:
- self.depsin = {}
- for label, cls_constraints in cls_depsin.items():
- if label in self.depsin:
- constraints = self.depsin[label]
- for k, v in cls_constraints.items():
- if k in constraints:
- continue
- constraints[k] = v
- else:
- # Should this be a copy of cls_constraints?
- self.depsin[label] = cls_constraints
- # Out
- cls_depsout = cls.depsout
- if cls_depsout:
- if self.depsout is None:
- self.depsout = {}
- for label, cls_constraints in cls_depsout.items():
- if label in self.depsout:
- constraints = self.depsout[label]
- for k, v in cls_constraints.items():
- if k in constraints:
- continue
- constraints[k] = v
- else:
- # Should this be a copy of cls_constraints?
- self.depsout[label] = cls_constraints
-
- def inherit_order(self, cls):
- """Inherit order constraints from class."""
- cls_order = cls.order
- if cls_order:
- my_order = self.get_order(create=True)
- # Just add all constraints (tuples) from the class to ones
- # already there in the word or lexeme; what if there are
- # conflicts?? (sort these out later)
- for co in cls_order:
- if co not in my_order:
- my_order.append(co)
-
- def inherit_grams(self, cls):
- """Inherit grammatical features from class."""
- cls_grams = cls.grams
- if cls_grams:
- if self.grams is None:
- self.grams = {}
- for feature, value in cls_grams.items():
- if feature in self.grams:
- # word/lexeme gram has priority over class, so
- # ignore this
- continue
- # copy any other feature/value constraint
- # (should the value be a copy??)
- self.grams[features] = value
-
- def inherit_agr(self, cls):
- """Inherit agreement constraints from class."""
- cls_agr = cls.agr
- if cls_agr:
- if self.agr is None:
- self.agr = []
- for constraint in cls_agr:
- if constraint not in self.agr:
- self.agr.append(constraint)
-
- def inherit_gov(self, cls):
- """Inherit government constraints from class."""
-
-class MWE(Entry):
- """Multi-word expressions. Each group consists of a head and a set of
nodes,
- possibly connected to other nodes through explicit dependencies and an
explicit
- order of the nodes.
- Variable slots have dedicated names that allow them to be
- referenced in translations.
- MWEs must be created *after* other lexical items.
- {index: [word_obj, {dep/position_feats}...}
- """
-
- def __init__(self, name, language, head, head_feats=None,
head_order=None, head_lexeme=False):
- """name of a MWE is something like acabar_de_V.
- head is the word that is the syntactic head of the group."""
- Entry.__init__(self, name, language)
- # A list of [word feats] pairs; index in the list is the word's
(node's) ID
- self.words = []
- self.word_id = 0
- if head_lexeme:
- self.head_lexeme = True
- head_type = language.get_lexeme(head)
- else:
- self.head_lexeme = False
- head_type = language.get_words(head)
- if not head_type:
-# print("No existing lexical entry in {} for head of group
{}".format(language, name))
- # SHOULD THIS BE RECORDED IN THE WORD LEXICON?
- self.head = language.add_word(head, group=True)
- else:
- # Use the first one if there's more than one
- self.head = head_type[0].clone()
- self.words.append([self.head, {}])
-# self.words[self.word_id] = [self.head, {}]
- if head_order is not None:
- self.words[word_id][1]['o'] = head_order
-# self.words[self.word_id][1]['o'] = head_order
- self.word_id += 1
-
- def __repr__(self):
- """Print name."""
- return '<{}:{}>'.format(
self.name,
self.id)
-
- # Serialization
-
- def to_dict(self):
- """Convert the group to a dictionary to be serialized in a yaml
file."""
- d = Entry.to_dict(self)
- d['head_lexeme'] = self.head_lexeme
-# d['words'] = {}
- d['words'] = []
- w = d['words']
-# for index, lex in self.words.items():
- for lex in enumerate(self.words):
- l = lex[0]
- name =
l.name
- w.append([name])
-# w[index] = [name]
- if len(lex) == 2:
- w[-1].append(copy.deepcopy(lex[1]))
-# w[index].append(copy.deepcopy(lex[1]))
- return d
-
- @staticmethod
- def from_dict(d, language):
- """Convert a dict (loaded from a yaml file) to a MWE object."""
- lexeme = d['head_lexeme']
- m = MWE(d.get('name'), language, d.get('words')[0][0],
head_lexeme=lexeme)
-# for id, word in d.get('words').items():
- for id, word in enumerate(d.get('words')):
- if id == 0:
- # Just handle the dependencies for this case
- deps = word[1]
- m.words[0][1] = copy.deepcopy(deps)
- else:
- name = word[0]
- lex = language.get_words(name)[0]
- if len(word) == 2:
- deps = word[1]
- lex_info = [lex.clone(), copy.deepcopy(deps)]
- else:
- lex_info = [lex.clone()]
- m.words.append(lex_info)
- return m
-
- ## Getters
-
- def get_word(self, index):
- """The lex and features for a word in the group with ID index."""
- if index > len(self.words) - 1:
- s = "No word in {} with internal ID {}"
- raise(EntryError(s.format(self, index)))
- return self.words[index]
-
- def get_word_feats(self, index):
- word = self.get_word(index)
- return word[1]
-
- def get_lex(self, id):
- """Return the Lex with the given index."""
- word = self.get_word(id)
- return word[0]
-
- def get_daughters(self, word_id, dep=None):
- """Return the indices of the daughters of word with id word_id
- of type dep or all daughters if dep is None."""
- feats = self.get_word_feats(word_id)
- if 'd' not in feats:
- return
- daughters = feats['d']
- if dep is not None:
- return daughters.get(dep)
- else:
- # Maybe leave as an iterable object?
- return list(itertools.chain.from_iterable(daughters.values()))
-
- def get_mother(self, word_id):
- """Return the type and index of the internal mother of word with
id word_id.
- If this is the head, return None."""
- feats = self.get_word_feats(word_id)
- if 'm' not in feats:
- return
- return feats['m']
-
- def add_word(self, word, head_id=None, dependency=Entry.dflt_dep,
order=None):
- """Add a word to the group, as dependent on dependency from
head."""
- # For now, use first word entry
- typ = self.language.get_words(word)
- if not typ:
-# print("No existing lexical entry in {} for head of group
{}".format(self.language, word))
- # SHOULD THIS BE RECORDED IN THE WORD LEXICON?
- word = self.language.add_word(word, group=True)
- else:
- # Pick the first lexical entry for now
- word = typ[0].clone()
- self.words.append([word, {}])
-# self.words[self.word_id] = [word, {}]
- if head_id is not None:
- self.add_dep(head_id, self.word_id, dep=dependency)
- if order is not None:
- self.words[self.word_id][1]['o'] = order
- id = self.word_id
- self.word_id += 1
- return id
-
- def add_dep(self, src, dest, dep=Entry.dflt_dep):
- """Make a dependency of type dep from word with id src to word
with id dest."""
- if src >= len(self.words):
- s = "No word in {} with internal ID {}"
- raise(EntryError(s.format(self, src)))
- if dest >= len(self.words):
- s = "No word in {} with internal ID {}"
- raise(EntryError(s.format(self, dest)))
- daughter_dict = self.get_word_feats(dest)
- if 'm' in daughter_dict:
- s = "Word {} in {} already has a mother"
- raise(EntryError(s.format(dest, self)))
- daughter_dict['m'] = (dep, src)
- mother_dict = self.get_word_feats(src)
- if 'd' not in mother_dict:
- mother_dict['d'] = {}
- mother_daughters = mother_dict['d']
- if dep not in mother_daughters:
- mother_daughters[dep] = []
- mother_daughters[dep].append(dest)
-
- ## Translations
- ## A translation of a group is a group in another language, with a
mapping or alignment
- ## between the nodes (words) in the two groups.
- ## The mapping takes the form of a list of target word indices or None
if the corresponding
- ## word is unspecified or -1 if there is not corresponding word
(deletion). If there are
- ## more words/nodes in the target than in the source group, the length
of the list of
- ## is the number of target nodes.
-
- def add_trans(self, language, trans, count=1):
- """Add translation to the translation dictionary for language,
- initializing its count."""
- Entry.add_trans(self, language, trans, count=count)
- transdict = self.get_trans(language, trans)
- transdict['m'] = [None for x in range(len(self.words))]
-
- def get_trans(self, language, trans, create=True):
- alltrans = self.get_translations(language, create=create)
- if not alltrans or trans not in alltrans:
- s = "Attempting to update non-existent translation {} for {}"
- raise(EntryError(s.format(trans,
self.name)))
- return alltrans[trans]
-
- def get_trans_map(self, language, trans):
- """Get the mapping to nodes in translation."""
- tdict = self.get_trans(language, trans)
- return tdict.get('m')
-
- def get_trans_map1(self, language, trans, src_index):
- """Get the mapped index of src_index in translation trans."""
- map = self.get_trans_map(language, trans)
- if not map:
- s = "Attempting to access non-existing mapping for translation
{} of {}"
- raise(EntryError(s.format(trans, self)))
- return map[src_index]
-
- def add_trans_map(self, language, trans, src_id, trg_id):
- """Add a correspondence between source and target nodes in a
translation mapping."""
- tdict = self.get_trans(language, trans)
-# if 'm' not in tdict:
-# tdict['m'] = []
-# tdict['m'].append((src_id, trg_id))
- tdict['m'][src_id] = trg_id
-
- def add_trans_del(self, language, trans, src_id):
- """Record a node in the source group with nothing corresponding to
it in the target group."""
- tdict = self.get_trans(language, trans)
-# if 'm' not in tdict:
-# tdict['m'] = []
-# tdict['m'].append((src_id, -1))
- tdict['m'][src_id] = -1
-
- def add_trans_ins(self, language, trans, trg_id):
- """Record a node in the target group with nothing corresponding to
it in the source group."""
- tdict = self.get_trans(language, trans)
-# if 'm' not in tdict:
-# tdict['m'] = []
- tdict['m'].append(trg_id)
-# tdict['m'].append((-1, trg_id))
+##class Lex(Entry):
+##
+## cloneID = 1
+##
+## def __init__(self, name, language, cls=None, id=0, group=False):
+## """In addition to Entry features, initialize
+## depsin, depsout, order, agr, gov, grams, and (for word and
lexeme) class."""
+## Entry.__init__(self, name, language, id=id)
+## self.depsin = None
+## self.depsout = None
+## self.order = None
+## self.agr = None
+##
self.gov = None
+## self.grams = None
+## self.cls = cls
+## self.cloneID = 0
+## # Whether entry is part of a group
+## self.group = group
+##
+## def __repr__(self):
+## """Print name and a unique identifier."""
+## return '<{}:{}{}>'.format(
self.name,
self.id, ';' +
str(self.cloneID) if self.cloneID else '')
+##
+## ## Cloning
+## ## Needed for groups, which consist of copies of lexes and
+## ## for L3 node entries
+##
+## def clone(self, group=True):
+## copied = Lex(
self.name, self.language, cls=self.cls, id=
self.id,
group=group)
+## copied.depsin = self.depsin
+## copied.depsout = self.depsout
+## copied.order = self.order
+## copied.agr = self.agr
+##
copied.gov =
self.gov
+## copied.grams = self.grams
+## copied.cloneID = Lex.cloneID
+## Lex.cloneID += 1
+## return copied
+##
+## ## Serialization
+##
+## def to_dict(self):
+## """Convert the lex to a dictionary to be serialized in a yaml
file."""
+## d = Entry.to_dict(self)
+## if self.depsin:
+## d['depsin'] = copy.deepcopy(self.depsin)
+## if self.depsout:
+## d['depsout'] = copy.deepcopy(self.depsout)
+## if self.order:
+## d['order'] = copy.deepcopy(self.order)
+## if self.agr:
+## d['agr'] = copy.deepcopy(self.agr)
+## if
self.gov:
+## d['gov'] = copy.deepcopy(
self.gov)
+## if self.grams:
+## d['grams'] = self.grams.copy()
+## if self.cls:
+## d['cls'] = self.cls
+## return d
+##
+## @staticmethod
+## def from_dict(d, language):
+## """Convert a dict (loaded from a yaml file) to a Lex object."""
+## l = Lex(d.get('name'), language, cls=d.get('cls'))
+## if d.get('depsin'):
+## l.depsin = d.get('depsin')
+## if d.get('depsout'):
+## l.depsout = d.get('depsout')
+## if d.get('order'):
+## l.order = d.get('order')
+## if d.get('agr'):
+## l.agr = d.get('agr')
+## if d.get('gov'):
+##
l.gov = d.get('gov')
+## if d.get('grams'):
+## l.grams = d.get('grams')
+## return l
+##
+## ## Dependencies (word, lexeme, class entries)
+##
+## def get_depin(self, label, create=False):
+## """Get the dict of features of incoming dependencies with label,
creating
+## the dict if it's not there and create is True."""
+## if self.depsin is None:
+## self.depsin = {}
+## if create and label not in self.depsin:
+## self.depsin[label] = {}
+## self.language.record_label(label)
+## return self.depsin.get(label)
+##
+## def add_depin(self, label, feats):
+## """Assign feats (a dictionary) to features for incoming
dependencies with label,
+## or update the current features."""
+## d = self.get_depin(label, create=True)
+## d.update(feats)
+##
+## def get_depout(self, label, create=False):
+## """Get the dict of features of outgoing dependencies with label,
creating
+## the dict if it's not there and create is True."""
+## if self.depsout is None:
+## self.depsout = {}
+## if create and label not in self.depsout:
+## self.depsout[label] = {}
+## self.language.record_label(label)
+## return self.depsout.get(label)
+##
+## def add_depout(self, label, feats):
+## """Assign feats (a dictionary) to features for outgoing
dependencies with label,
+## or update the current features."""
+## d = self.get_depout(label, create=True)
+## d.update(feats)
+##
+## ## Dependency features
+## ## A dict with keys
+## ## 'min', 'max', 'dflt', 'maxdist'
+##
+## def set_deps_feat(self, featdict, key, value):
+## featdict[key] = value
+##
+## def get_deps_feat(self, featdict, key):
+## return featdict.get(key)
+##
+## ## Order constraints
+## ## A constraint is a tuple of dependency labels and '^' representing
the head
+##
+## def get_order(self, create=False):
+## """Get the set of order constraint tuples, creating the set if
it's not there
+## and create is True."""
+## if self.order is None and create:
+## self.order = []
+## return self.order
+##
+## def add_order(self, constraint):
+## """Add an order constraint tuple to the set of order
constraints."""
+## order_constraints = self.get_order(create=True)
+## order_constraints.append(constraint)
+## self.language.record_order(constraint)
+##
+## ## Grammatical features associated with words, classes, and lexemes
+##
+## def get_gram(self, feature, create=False):
+## """Get the possible values and their counts for grammatical
feature.
+## If this is a word, the value is a string; if a class or lexeme,
a dict
+## of values and counts."""
+## if self.grams is None:
+## self.grams = {}
+### if feature not in self.grams and create:
+### self.grams[feature] = {}
+## return self.grams.get(feature)
+##
+## def set_gram(self, feat, values):
+## """Set possible values and their counts for grammatical feature.
+## values is a dict of values and their counts."""
+## if self.grams is None:
+## self.grams = {}
+## if feat in self.grams:
+## s = "Entry for {} already has a constraint for feature {}"
+## raise(EntryError(s.format(
self.name, feat)))
+## self.grams[feat] = values
+##
+## def update_gram_value(self, feat, value, count=1):
+## """Add count to the current count for feature value."""
+## gram = self.get_gram(feat, create=True)
+## if value in gram:
+## gram[value] += count
+## else:
+## gram[value] = count
+##
+## ## Agreement and government
+##
+## ## An agreement constraint requires a dependency label, a head
feature, and
+## ## and a dependent feature.
+##
+## def add_agr(self, deplabel, head_feat, dep_feat):
+## """Add an agreement constraint to the list of constraints in the
entry."""
+## if self.agr is None:
+## self.agr = []
+## self.agr.append([deplabel, head_feat, dep_feat])
+##
+## ## A government constraint requires a dependency label, a dependent
feature,
+## ## and a dependent value.
+##
+## def add_gov(self, deplabel, dep_feat, dep_value):
+## if
self.gov is None:
+##
self.gov = []
+## self.gov.append([deplabel, dep_feat, dep_value])
+##
+## ## Inheritance: copying features from classes to lexemes and words
+## ## at initialization
+##
+## def inherit(self):
+## if not self.cls:
+## return
+## cls = self.language.get_class(self.cls)
+## if not cls:
+## s = "Class {} for {} does not exist"
+## raise(EntryError(s.format(self.cls, self)))
+## self.inherit_deps(cls)
+## self.inherit_order(cls)
+## self.inherit_grams(cls)
+## self.inherit_agr(cls)
+## self.inherit_gov(cls)
+## # Also inherit translation?
+##
+## def inherit_deps(self, cls):
+## """Inherit dependency constraints (in and out) from class."""
+## # In
+## cls_depsin = cls.depsin
+## if cls_depsin:
+## if self.depsin is None:
+## self.depsin = {}
+## for label, cls_constraints in cls_depsin.items():
+## if label in self.depsin:
+## constraints = self.depsin[label]
+## for k, v in cls_constraints.items():
+## if k in constraints:
+## continue
+## constraints[k] = v
+## else:
+## # Should this be a copy of cls_constraints?
+## self.depsin[label] = cls_constraints
+## # Out
+## cls_depsout = cls.depsout
+## if cls_depsout:
+## if self.depsout is None:
+## self.depsout = {}
+## for label, cls_constraints in cls_depsout.items():
+## if label in self.depsout:
+## constraints = self.depsout[label]
+## for k, v in cls_constraints.items():
+## if k in constraints:
+## continue
+## constraints[k] = v
+## else:
+## # Should this be a copy of cls_constraints?
+## self.depsout[label] = cls_constraints
+##
+## def inherit_order(self, cls):
+## """Inherit order constraints from class."""
+## cls_order = cls.order
+## if cls_order:
+## my_order = self.get_order(create=True)
+## # Just add all constraints (tuples) from the class to ones
+## # already there in the word or lexeme; what if there are
+## # conflicts?? (sort these out later)
+## for co in cls_order:
+## if co not in my_order:
+## my_order.append(co)
+##
+## def inherit_grams(self, cls):
+## """Inherit grammatical features from class."""
+## cls_grams = cls.grams
+## if cls_grams:
+## if self.grams is None:
+## self.grams = {}
+## for feature, value in cls_grams.items():
+## if feature in self.grams:
+## # word/lexeme gram has priority over class, so
+## # ignore this
+## continue
+## # copy any other feature/value constraint
+## # (should the value be a copy??)
+## self.grams[features] = value
+##
+## def inherit_agr(self, cls):
+## """Inherit agreement constraints from class."""
+## cls_agr = cls.agr
+## if cls_agr:
+## if self.agr is None:
+## self.agr = []
+## for constraint in cls_agr:
+## if constraint not in self.agr:
+## self.agr.append(constraint)
+##
+## def inherit_gov(self, cls):
+## """Inherit government constraints from class."""
+##
+##class MWE(Entry):
+## """Multi-word expressions. Each group consists of a head and a set
of nodes,
+## possibly connected to other nodes through explicit dependencies and
an explicit
+## order of the nodes.
+## Variable slots have dedicated names that allow them to be
+## referenced in translations.
+## MWEs must be created *after* other lexical items.
+## {index: [word_obj, {dep/position_feats}...}
+## """
+##
+## def __init__(self, name, language, head, head_feats=None,
head_order=None, head_lexeme=False):
+## """name of a MWE is something like acabar_de_V.
+## head is the word that is the syntactic head of the group."""
+## Entry.__init__(self, name, language)
+## # A list of [word feats] pairs; index in the list is the word's
(node's) ID
+## self.words = []
+## self.word_id = 0
+## if head_lexeme:
+## self.head_lexeme = True
+## head_type = language.get_lexeme(head)
+## else:
+## self.head_lexeme = False
+## head_type = language.get_words(head)
+## if not head_type:
+### print("No existing lexical entry in {} for head of group
{}".format(language, name))
+## # SHOULD THIS BE RECORDED IN THE WORD LEXICON?
+## self.head = language.add_word(head, group=True)
+## else:
+## # Use the first one if there's more than one
+## self.head = head_type[0].clone()
+## self.words.append([self.head, {}])
+### self.words[self.word_id] = [self.head, {}]
+## if head_order is not None:
+## self.words[word_id][1]['o'] = head_order
+### self.words[self.word_id][1]['o'] = head_order
+## self.word_id += 1
+##
+## def __repr__(self):
+## """Print name."""
+## return '<{}:{}>'.format(
self.name,
self.id)
+##
+## # Serialization
+##
+## def to_dict(self):
+## """Convert the group to a dictionary to be serialized in a yaml
file."""
+## d = Entry.to_dict(self)
+## d['head_lexeme'] = self.head_lexeme
+### d['words'] = {}
+## d['words'] = []
+## w = d['words']
+### for index, lex in self.words.items():
+## for lex in enumerate(self.words):
+## l = lex[0]
+## name =
l.name
+## w.append([name])
+### w[index] = [name]
+## if len(lex) == 2:
+## w[-1].append(copy.deepcopy(lex[1]))
+### w[index].append(copy.deepcopy(lex[1]))
+## return d
+##
+## @staticmethod
+## def from_dict(d, language):
+## """Convert a dict (loaded from a yaml file) to a MWE object."""
+## lexeme = d['head_lexeme']
+## m = MWE(d.get('name'), language, d.get('words')[0][0],
head_lexeme=lexeme)
+### for id, word in d.get('words').items():
+## for id, word in enumerate(d.get('words')):
+## if id == 0:
+## # Just handle the dependencies for this case
+## deps = word[1]
+## m.words[0][1] = copy.deepcopy(deps)
+## else:
+## name = word[0]
+## lex = language.get_words(name)[0]
+## if len(word) == 2:
+## deps = word[1]
+## lex_info = [lex.clone(), copy.deepcopy(deps)]
+## else:
+## lex_info = [lex.clone()]
+## m.words.append(lex_info)
+## return m
+##
+## ## Getters
+##
+## def get_word(self, index):
+## """The lex and features for a word in the group with ID index."""
+## if index > len(self.words) - 1:
+## s = "No word in {} with internal ID {}"
+## raise(EntryError(s.format(self, index)))
+## return self.words[index]
+##
+## def get_word_feats(self, index):
+## word = self.get_word(index)
+## return word[1]
+##
+## def get_lex(self, id):
+## """Return the Lex with the given index."""
+## word = self.get_word(id)
+## return word[0]
+##
+## def get_daughters(self, word_id, dep=None):
+## """Return the indices of the daughters of word with id word_id
+## of type dep or all daughters if dep is None."""
+## feats = self.get_word_feats(word_id)
+## if 'd' not in feats:
+## return
+## daughters = feats['d']
+## if dep is not None:
+## return daughters.get(dep)
+## else:
+## # Maybe leave as an iterable object?
+## return
list(itertools.chain.from_iterable(daughters.values()))
+##
+## def get_mother(self, word_id):
+## """Return the type and index of the internal mother of word with
id word_id.
+## If this is the head, return None."""
+## feats = self.get_word_feats(word_id)
+## if 'm' not in feats:
+## return
+## return feats['m']
+##
+## def add_word(self, word, head_id=None, dependency=Entry.dflt_dep,
order=None):
+## """Add a word to the group, as dependent on dependency from
head."""
+## # For now, use first word entry
+## typ = self.language.get_words(word)
+## if not typ:
+### print("No existing lexical entry in {} for head of group
{}".format(self.language, word))
+## # SHOULD THIS BE RECORDED IN THE WORD LEXICON?
+## word = self.language.add_word(word, group=True)
+## else:
+## # Pick the first lexical entry for now
+## word = typ[0].clone()
+## self.words.append([word, {}])
+### self.words[self.word_id] = [word, {}]
+## if head_id is not None:
+## self.add_dep(head_id, self.word_id, dep=dependency)
+## if order is not None:
+## self.words[self.word_id][1]['o'] = order
+## id = self.word_id
+## self.word_id += 1
+## return id
+##
+## def add_dep(self, src, dest, dep=Entry.dflt_dep):
+## """Make a dependency of type dep from word with id src to word
with id dest."""
+## if src >= len(self.words):
+## s = "No word in {} with internal ID {}"
+## raise(EntryError(s.format(self, src)))
+## if dest >= len(self.words):
+## s = "No word in {} with internal ID {}"
+## raise(EntryError(s.format(self, dest)))
+## daughter_dict = self.get_word_feats(dest)
+## if 'm' in daughter_dict:
+## s = "Word {} in {} already has a mother"
+## raise(EntryError(s.format(dest, self)))
+## daughter_dict['m'] = (dep, src)
+## mother_dict = self.get_word_feats(src)
+## if 'd' not in mother_dict:
+## mother_dict['d'] = {}
+## mother_daughters = mother_dict['d']
+## if dep not in mother_daughters:
+## mother_daughters[dep] = []
+## mother_daughters[dep].append(dest)
+##
+## ## Translations
+## ## A translation of a group is a group in another language, with a
mapping or alignment
+## ## between the nodes (words) in the two groups.
+## ## The mapping takes the form of a list of target word indices or
None if the corresponding
+## ## word is unspecified or -1 if there is not corresponding word
(deletion). If there are
+## ## more words/nodes in the target than in the source group, the
length of the list of
+## ## is the number of target nodes.
+##
+## def add_trans(self, language, trans, count=1):
+## """Add translation to the translation dictionary for language,
+## initializing its count."""
+## Entry.add_trans(self, language, trans, count=count)
+## transdict = self.get_trans(language, trans)
+## transdict['m'] = [None for x in range(len(self.words))]
+##
+## def get_trans(self, language, trans, create=True):
+## alltrans = self.get_translations(language, create=create)
+## if not alltrans or trans not in alltrans:
+## s = "Attempting to update non-existent translation {} for {}"
+## raise(EntryError(s.format(trans,
self.name)))
+## return alltrans[trans]
+##
+## def get_trans_map(self, language, trans):
+## """Get the mapping to nodes in translation."""
+## tdict = self.get_trans(language, trans)
+## return tdict.get('m')
+##
+## def get_trans_map1(self, language, trans, src_index):
+## """Get the mapped index of src_index in translation trans."""
+## map = self.get_trans_map(language, trans)
+## if not map:
+## s = "Attempting to access non-existing mapping for
translation {} of {}"
+## raise(EntryError(s.format(trans, self)))
+## return map[src_index]
+##
+## def add_trans_map(self, language, trans, src_id, trg_id):
+## """Add a correspondence between source and target nodes in a
translation mapping."""
+## tdict = self.get_trans(language, trans)
+### if 'm' not in tdict:
+### tdict['m'] = []
+### tdict['m'].append((src_id, trg_id))
+## tdict['m'][src_id] = trg_id
+##
+## def add_trans_del(self, language, trans, src_id):
+## """Record a node in the source group with nothing corresponding
to it in the target group."""
+## tdict = self.get_trans(language, trans)
***The diff for this file has been truncated for email.***
=======================================
--- /l3lite/language.py Sun Apr 27 18:07:59 2014 UTC
+++ /l3lite/language.py Thu May 1 06:39:44 2014 UTC
@@ -35,6 +35,8 @@
# Lexemes start with %, classes with $.
# 2014.04.17
# -- Analysis and generation dicts for particular wordforms.
+# 2014.04.30
+# -- Eliminated entry types in lexicon other than Groups and forms.
from .entry import *
@@ -60,15 +62,15 @@
self.words = words or {}
# Combine with words in a single lexicon?
# self.lexemes = lexemes or {}
- self.grams = grams or {}
+# self.grams = grams or {}
# self.classes = classes or {}
- self.mwes = mwes or {}
+# self.mwes = mwes or {}
self.forms = forms or {}
self.groups = groups or {}
# Dict of groups with names as keys
self.groupnames = {}
- # Record possibilities for dependency labels, feature values,
order constraints
- self.possible = {}
+# # Record possibilities for dependency labels, feature values,
order constraints
+# self.possible = {}
# Record whether language has changed since last loaded
self.changed = False
# Dictionary of morphologically generated words:
@@ -85,13 +87,13 @@
d = {}
d['name'] =
self.name
d['abbrev'] = self.abbrev
- d['possible'] = self.possible
+# d['possible'] = self.possible
# Entries: each is a dict, whose values must be converted to dicts
- if self.grams:
- grams = {}
- for k, v in self.grams.items():
- grams[k] = v.to_dict()
- d['grams'] = grams
+# if self.grams:
+# grams = {}
+# for k, v in self.grams.items():
+# grams[k] = v.to_dict()
+# d['grams'] = grams
# if self.classes:
# classes = {}
# for k, v in self.classes.items():
@@ -103,17 +105,17 @@
# for k, v in self.lexemes.items():
# lexemes[k] = v.to_dict()
# d['lexemes'] = lexemes
- if self.words:
- words = {}
- for k, v in self.words.items():
- # Words are lists
- words[k] = [lex.to_dict() for lex in v]
- d['words'] = words
- if self.mwes:
- mwes = {}
- for k, v in self.mwes.items():
- mwes[k] = [m.to_dict() for m in v]
- d['mwes'] = mwes
+# if self.words:
+# words = {}
+# for k, v in self.words.items():
+# # Words are lists
+# words[k] = [lex.to_dict() for lex in v]
+# d['words'] = words
+# if self.mwes:
+# mwes = {}
+# for k, v in self.mwes.items():
+# mwes[k] = [m.to_dict() for m in v]
+# d['mwes'] = mwes
if self.groups:
groups = {}
for head, v in self.groups.items():
@@ -138,11 +140,11 @@
"""Convert a dict (loaded from a yaml file) to a Language
object."""
l = Language(d.get('name'), d.get('abbrev'))
l.possible = d.get('possible')
- grams = d.get('grams')
- if grams:
- l.grams = {}
- for k, v in grams.items():
- l.grams[k] = Entry.from_dict(v, l)
+# grams = d.get('grams')
+# if grams:
+# l.grams = {}
+# for k, v in grams.items():
+# l.grams[k] = Entry.from_dict(v, l)
# classes = d.get('classes')
# if classes:
# l.classes = {}
@@ -153,16 +155,16 @@
# l.lexemes = {}
# for k, v in lexemes.items():
# l.lexemes[k] = Lex.from_dict(v, l)
- words = d.get('words')
- if words:
- l.words = {}
- for k, v in words.items():
- l.words[k] = [Lex.from_dict(lex, l) for lex in v]
- mwes = d.get('mwes')
- if mwes:
- l.mwes = {}
- for k, v in mwes.items():
- l.mwes[k] = [MWE.from_dict(m, l) for m in v]
+# words = d.get('words')
+# if words:
+# l.words = {}
+# for k, v in words.items():
+# l.words[k] = [Lex.from_dict(lex, l) for lex in v]
+# mwes = d.get('mwes')
+# if mwes:
+# l.mwes = {}
+# for k, v in mwes.items():
+# l.mwes[k] = [MWE.from_dict(m, l) for m in v]
groups = d.get('groups')
if groups:
l.groups = {}
@@ -220,24 +222,24 @@
### Basic setters. Create entries (dicts) for item. For debugging
purposes, include name
### in entry.
- def add_word(self, word, cls=None, mwe=False):
- entry = Lex(word, self, cls=cls, mwe=mwe)
- if word in self.words:
- self.words[word].append(entry)
- else:
- self.words[word] = [entry]
- self.changed = True
- return entry
-
- def add_lexeme(self, lexeme, cls=None):
- if lexeme in self.words:
- s = "Lexeme {} already in dictionary"
- raise(LanguageError(s.format(lexeme)))
- entry = Lex(lexeme, self, cls=cls)
- # Maybe not a list since there's always only one
- self.words[lexeme] = [entry]
- self.changed = True
- return entry
+## def add_word(self, word, cls=None, mwe=False):
+## entry = Lex(word, self, cls=cls, mwe=mwe)
+## if word in self.words:
+## self.words[word].append(entry)
+## else:
+## self.words[word] = [entry]
+## self.changed = True
+## return entry
+##
+## def add_lexeme(self, lexeme, cls=None):
+## if lexeme in self.words:
+## s = "Lexeme {} already in dictionary"
+## raise(LanguageError(s.format(lexeme)))
+## entry = Lex(lexeme, self, cls=cls)
+## # Maybe not a list since there's always only one
+## self.words[lexeme] = [entry]
+## self.changed = True
+## return entry
def add_form(self, form, dct, reverse=True):
"""Form dict has root, features, cats.
@@ -279,23 +281,23 @@
# new_feat = features.pop(0)
# Language.make_featdict(featdict[feat], new_feat, features, form)
- def add_class(self, cls):
- if cls in self.words:
- s = "Class {} already in dictionary"
- raise(LanguageError(s.format(cls)))
- entry = Lex(cls, self)
- # Maybe not a list since there's always only one
- self.words[cls] = [entry]
- self.changed = True
- return entry
-
- def add_mwe(self, name, head, head_order=None, head_lexeme=False):
- entry = MWE(name, self, head, head_order=head_order,
head_lexeme=head_lexeme)
- if head not in self.mwes:
- self.mwes[head] = []
- self.mwes[head].append(entry)
- self.changed = True
- return entry
+## def add_class(self, cls):
+## if cls in self.words:
+## s = "Class {} already in dictionary"
+## raise(LanguageError(s.format(cls)))
+## entry = Lex(cls, self)
+## # Maybe not a list since there's always only one
+## self.words[cls] = [entry]
+## self.changed = True
+## return entry
+##
+## def add_mwe(self, name, head, head_order=None, head_lexeme=False):
+## entry = MWE(name, self, head, head_order=head_order,
head_lexeme=head_lexeme)
+## if head not in self.mwes:
+## self.mwes[head] = []
+## self.mwes[head].append(entry)
+## self.changed = True
+## return entry
def add_group(self, tokens, head_index=-1, head='', name='',
features=None):
group = Group(tokens, head_index=head_index, head=head,
@@ -330,51 +332,51 @@
self.groups[head][fvs] = []
self.groups[head][fvs].append(group)
- def add_gram(self, gram, feature, count=1):
- """A gram, for example, 'plural', must have a feature, for example,
- 'number'."""
- if gram in self.grams:
- s = "Grammatical morpheme {} already in dictionary"
- raise(LanguageError(s.format(gram)))
- entry = Entry(gram, self)
- self.grams[gram] = entry
- entry.feature = feature
- self.grams[gram] = entry
- self.record_gram(gram, feature, count)
- self.changed = True
- return entry
-
- def record_gram(self, name, feature, count):
- """Record the gram value and its count under its feature name."""
- if 'features' not in self.possible:
- self.possible['features'] = {}
- if feature not in self.possible['features']:
- self.possible['features'][feature] = {}
- self.possible['features'][feature][name] = count
-
- def get_possible_feat_values(self, feature):
- """Possible values and associated counts for grammatical
feature."""
- if 'features' not in self.possible:
- self.possible['features'] = {}
- return self.possible['features'].get(feature)
+## def add_gram(self, gram, feature, count=1):
+## """A gram, for example, 'plural', must have a feature, for
example,
+## 'number'."""
+## if gram in self.grams:
+## s = "Grammatical morpheme {} already in dictionary"
+## raise(LanguageError(s.format(gram)))
+## entry = Entry(gram, self)
+## self.grams[gram] = entry
+## entry.feature = feature
+## self.grams[gram] = entry
+## self.record_gram(gram, feature, count)
+## self.changed = True
+## return entry
+##
+## def record_gram(self, name, feature, count):
+## """Record the gram value and its count under its feature name."""
+## if 'features' not in self.possible:
+## self.possible['features'] = {}
+## if feature not in self.possible['features']:
+## self.possible['features'][feature] = {}
+## self.possible['features'][feature][name] = count
+##
+## def get_possible_feat_values(self, feature):
+## """Possible values and associated counts for grammatical
feature."""
+## if 'features' not in self.possible:
+## self.possible['features'] = {}
+## return self.possible['features'].get(feature)
### Basic getters.
- def get_words(self, word):
- """Returns a list of word entries."""
- return self.words.get(word)
-
- def get_class(self, cls):
- """Returns a single class entry."""
- return self.words.get(cls)[0]
-
- def get_gram(self, gram):
- """Returns a single gram feature value entry."""
- return self.grams.get(gram)
-
- def get_lexeme(self, lexeme):
- """Returns a single lexeme entry."""
- return self.words.get(lexeme)[0]
+## def get_words(self, word):
+## """Returns a list of word entries."""
+## return self.words.get(word)
+##
+## def get_class(self, cls):
+## """Returns a single class entry."""
+## return self.words.get(cls)[0]
+##
+## def get_gram(self, gram):
+## """Returns a single gram feature value entry."""
+## return self.grams.get(gram)
+##
+## def get_lexeme(self, lexeme):
+## """Returns a single lexeme entry."""
+## return self.words.get(lexeme)[0]
### Generation of word forms
@@ -393,42 +395,42 @@
print("No forms found for {}:{}".format(root, features))
return result
- ## Dependencies (word, lexeme, class entries)
-
- def record_label(self, label):
- """Record the dependency label in the set of possible labels."""
- if 'deplabels' not in self.possible:
- self.possible['deplabels'] = []
- if label not in self.possible['deplabels']:
- self.possible['deplabels'].append(label)
-
- def get_possible_labels(self):
- return self.possible.get('deplabels')
-
- ## Order constraints
- ## A constraint is a tuple of dependency labels and '^' representing
the head
-
- def record_order(self, constraint):
- """Record the constraint tuple in the set of possible constraints
for the language."""
- if 'order' not in self.possible:
- self.possible['order'] = []
- if constraint not in self.possible['order']:
- # Append a *copy* of the constraint list
- self.possible['order'].append(constraint[:])
-
- def get_possible_orders(self):
- return self.possible.get('order')
-
- ## Agreement constraints
-
- def record_agr(self, constraint):
- """An agreement constraint is a tuple consisting of
- dep label, head feature, dependent feature."""
- if 'agr' not in self.possible:
- self.possible['agr'] = []
- if constraint not in self.possible['agr']:
- # Append a *copy* of the constraint list
- self.possible['agr'].append(constraint[:])
+## ## Dependencies (word, lexeme, class entries)
+##
+## def record_label(self, label):
+## """Record the dependency label in the set of possible labels."""
+## if 'deplabels' not in self.possible:
+## self.possible['deplabels'] = []
+## if label not in self.possible['deplabels']:
+## self.possible['deplabels'].append(label)
+##
+## def get_possible_labels(self):
+## return self.possible.get('deplabels')
+##
+## ## Order constraints
+## ## A constraint is a tuple of dependency labels and '^' representing
the head
+##
+## def record_order(self, constraint):
+## """Record the constraint tuple in the set of possible
constraints for the language."""
+## if 'order' not in self.possible:
+## self.possible['order'] = []
+## if constraint not in self.possible['order']:
+## # Append a *copy* of the constraint list
+## self.possible['order'].append(constraint[:])
+##
+## def get_possible_orders(self):
+## return self.possible.get('order')
+##
+## ## Agreement constraints
+##
+## def record_agr(self, constraint):
+## """An agreement constraint is a tuple consisting of
+## dep label, head feature, dependent feature."""
+## if 'agr' not in self.possible:
+## self.possible['agr'] = []
+## if constraint not in self.possible['agr']:
+## # Append a *copy* of the constraint list
+## self.possible['agr'].append(constraint[:])
class LanguageError(Exception):
'''Class for errors encountered when attempting to update the
language.'''
=======================================
--- /l3lite/languages/eng.lg Sun Apr 20 19:19:51 2014 UTC
+++ /l3lite/languages/eng.lg Thu May 1 06:39:44 2014 UTC
@@ -21,6 +21,16 @@
root: we
features: {num: 1, prs: 1, case: 1}
cats: [$sbd]
+ them:
+ root: they
+ features: {num: 1, prs: 3, case: 1}
+ song:
+ root: song
+ features: {num: 0, prs: 3}
+ cats: [$singable]
+ sang:
+ root: sing
+ features: {tns: prt}
groups:
end:
@@ -28,7 +38,13 @@
read:
- words: [read, $sbd, the, riot, act]
- words: [read, $sth]
+ sing:
+ - words: [sing, $singable]
boy:
- words: [the, boy]
us:
- words: [us]
+ them:
+ - words: [them]
+ song:
+ - words: [a, song]
=======================================
--- /l3lite/sentence.py Wed Apr 30 06:56:30 2014 UTC
+++ /l3lite/sentence.py Thu May 1 06:39:44 2014 UTC
@@ -55,7 +55,7 @@
self.raw = raw
# Source language: a language object
self.language = language
- # Target language: a language object
+ # Target language: a language object or None
self.target = target
# A list of SNode objects, one for each token
self.nodes = nodes or []
@@ -85,6 +85,16 @@
else:
return '|| {} sentence {} ||'.format(self.language,
self.id)
+ def do(self):
+ """If target language, translate. If not, parse."""
+ if self.initialize():
+ if self.solve():
+ if self.target:
+ for solution in self.solutions:
+ solution.translate()
+ for translation in solution.translations:
+ translation.display()
+
def initialize(self):
"""Things to do before running constraint satisfaction."""
self.tokenize()
@@ -97,6 +107,15 @@
self.create_constraints()
return True
+ def solve(self):
+ """Run constraints and create a single solution."""
+ if self.run():
+ self.create_solution()
+ return True
+ else:
+ print("No solution found")
+ return False
+
def tokenize(self):
"""Segment the sentence string into tokens, analyze them
morphologically,
and create a SNode object for each."""
@@ -138,6 +157,7 @@
return
candidates = []
for node in self.nodes:
+ # Get keys into lexicon for this node
keys = {node.token}
anal = node.analyses
if anal:
@@ -146,9 +166,15 @@
keys.add(a.get('root'))
else:
keys.add(anal.get('root'))
+ # Look up candidate groups in lexicon
for k in keys:
if k in self.language.groups:
for group in self.language.groups[k]:
+ print("Checking group {} for {}".format(group,
node))
+ # Reject group if it doesn't have a translation in
the target language
+ if self.target and not
group.get_translations(self.target.abbrev):
+ print("No translation for {}".format(group))
+ continue
candidates.append((node.index, group))
# Now filter candidates to see if all words are present in the
sentence
# For each group, save a list of list of sentence token indices
that correspond
@@ -157,6 +183,7 @@
for head_i, group in candidates:
# Matching snodes, along with root and unified features if any
snodes = group.match_nodes(self.nodes, head_i)
+ print('Group {} matches snodes {}'.format(group, snodes))
if not snodes:
# This group is out
continue
@@ -176,12 +203,14 @@
## Create IVars and (set) Vars with sentence DS as root DS
- def ivar(self, name, domain):
- self.variables[name] = IVar(name, domain, rootDS=self.dstore)
+ def ivar(self, name, domain, ess=False):
+ self.variables[name] = IVar(name, domain, rootDS=self.dstore,
+ essential=ess)
- def svar(self, name, lower, upper, lower_card=0, upper_card=MAX):
+ def svar(self, name, lower, upper, lower_card=0, upper_card=MAX,
+ ess=False):
self.variables[name] = Var(name, lower, upper, lower_card,
upper_card,
- rootDS=self.dstore)
+ essential=ess, rootDS=self.dstore)
def create_variables(self):
# All abstract (category) and instance (word or lexeme) gnodes
@@ -201,7 +230,8 @@
self.svar('groups', set(), set(range(len(self.groups))),
# At least 1, at most all groups
- 1, len(self.groups))
+ 1, len(self.groups),
+ ess=True)
self.svar('gnodes', set(), set(range(self.ngnodes)),
# At least size of smallest group, at most all
min([len(g.nodes) for g in self.groups]),
@@ -337,6 +367,11 @@
selvars=[g.variables['agnodes_pos'] for g in self.groups],
seqvars=[s.variables['agnodes'] for s in self.nodes],
mainvars=[g.variables['agnodes'] for g in self.groups]))
+# # Complex union selection by groups on positions of all gnodes in
each selected group
+#
self.constraints.append(ComplexUnionSelection(selvar=self.variables['groups'],
+#
selvars=[g.variables['gnodes_pos'] for g in self.groups],
+#
seqvars=[s.variables['gnodes'] for s in self.nodes],
+#
mainvars=[g.variables['gnodes'] for g in self.groups]))
# Set convexity (projectivity) within each group tree
self.constraints.append(ComplexSetConvexity(self.variables['groups'],
[g.variables['tree']
for g in self.groups]))
@@ -346,11 +381,27 @@
"""Run constraint satisfaction on constraints, for now without
search if
no solution is found."""
self.solver.run()
+ print("Solver status after run: {}".format(self.solver.status))
+ return self.solver.status
- def make_solution(self, ginsts, s2gnodes, g2snodes):
- self.solutions.append(Solution(self, ginsts, s2gnodes, g2snodes,
+ def create_solution(self, dstore=None):
+ """Assuming essential variables are determined in a domain store,
make a Solution object."""
+ dstore = dstore or self.dstore
+ # Get the indices of the selected groups
+ groups = self.variables['groups'].get_value(dstore=dstore)
+ ginsts = [self.groups[g] for g in groups]
+ # Get the indices of the GNodes for each SNode
+ s2gnodes = []
+ for node in self.nodes:
+ gnodes = node.variables['gnodes'].get_value(dstore=dstore)
+ s2gnodes.append(gnodes)
+ self.solutions.append(Solution(self, ginsts, s2gnodes,
len(self.solutions)))
+# def make_solution(self, ginsts, s2gnodes):
+# self.solutions.append(Solution(self, ginsts, s2gnodes,
+# len(self.solutions)))
+
class SNode:
"""Sentence token and its associated analyses and variables."""
@@ -378,29 +429,34 @@
## Create IVars and (set) Vars with sentence DS as root DS
- def ivar(self, key, name, domain):
- self.variables[key] = IVar(name, domain,
rootDS=self.sentence.dstore)
+ def ivar(self, key, name, domain, ess=False):
+ self.variables[key] = IVar(name, domain,
rootDS=self.sentence.dstore,
+ essential=ess)
- def svar(self, key, name, lower, upper, lower_card=0, upper_card=MAX):
+ def svar(self, key, name, lower, upper, lower_card=0, upper_card=MAX,
+ ess=False):
self.variables[key] = Var(name, lower, upper, lower_card,
upper_card,
- rootDS=self.sentence.dstore)
+ rootDS=self.sentence.dstore,
essential=ess)
def create_variables(self):
+ # Indices of concrete (inst) and abstract (cat) gnodes
+ cgnodes = {gn.sent_index for gn in self.sentence.gnodes if not
gn.cat}
+ agnodes = {gn.sent_index for gn in self.sentence.gnodes if
gn.cat}
# GNodes associated with this SNode: 0, 1, or 2
self.svar('gnodes', "w{}->gn".format(self.index), set(),
set(range(self.sentence.ngnodes)),
- 0, 2)
- # Concrete GNodes associated with this SNode: 0 or 1
+ 0, 2, ess=True)
+ # Concrete GNodes associated with this SNode: must be 1
self.svar('cgnodes', "w{}->cgn".format(self.index), set(),
- set(range(self.sentence.ngnodes)),
- 0, 1)
+ cgnodes,
+ 1, 1)
# Abstract GNodes associated with this SNode: 0 or 1
self.svar('agnodes', "w{}->agn".format(self.index), set(),
- set(range(self.sentence.ngnodes)),
+ agnodes,
0, 1)
# Merged concrete GNodes associated with this SNode: 0 or 1
self.svar('mgnodes', "w{}->mgn".format(self.index), set(),
- set(range(self.sentence.ngnodes)),
+ cgnodes,
0, 1)
def get_cats(self):
@@ -415,15 +471,16 @@
def match(self, item, features):
"""Does this node match the group item (word, lexeme, category) and
- and any features associated with it?"""
-# print(' SNode {} trying to match item {} with features
{}'.format(self, item, features))
+ any features associated with it?"""
+# print(' SNode {} with features {} trying to match item {} with
features {}'.format(self, self.analyses, item, features))
# If item is a category, don't bother looking at token
if Entry.is_cat(item):
# print(' Cat item, looking in {}'.format(self.cats))
if self.cats and item in self.cats:
# print(" Token {} is in cat {}".format(self.token, item))
if not self.analyses or not features:
- return True
+ # Match; failure would be "fail"
+ return False
else:
for analysis in self.analyses:
node_features = analysis.get('features')
@@ -437,7 +494,7 @@
return False
elif self.token == item:
# item matches this node's token; features are irrelevant
- return True
+ return False
elif self.analyses:
# Check each combination of root and analysis features
for analysis in self.analyses:
@@ -446,10 +503,10 @@
# print(" SNode features {}".format(node_features))
if root == item:
if not features:
- return root, None
+ return root, node_features
# return True
elif not node_features:
- return root, None
+ return root, features
# return True
else:
u_features = node_features.unify(features)
@@ -494,19 +551,19 @@
## Create IVars and (set) Vars with sentence DS as root DS
- def ivar(self, key, name, domain):
- self.variables[key] = IVar(name, domain,
rootDS=self.sentence.dstore)
+ def ivar(self, key, name, domain, ess=False):
+ self.variables[key] = IVar(name, domain,
rootDS=self.sentence.dstore,
+ essential=ess)
- def svar(self, key, name, lower, upper, lower_card=0, upper_card=MAX):
+ def svar(self, key, name, lower, upper, lower_card=0, upper_card=MAX,
+ ess=False):
self.variables[key] = Var(name, lower, upper, lower_card,
upper_card,
- rootDS=self.sentence.dstore)
+ rootDS=self.sentence.dstore,
+ essential=ess)
def create_variables(self):
-# ngnodes = len(self.nodes)
-# ncgnodes = len([n for n in self.nodes if not
n.cat])
ngroups = len(self.sentence.groups)
nsnodes = len(self.sentence.nodes)
-# nanodes = ngnodes - ncgnodes
# GNode indices for this GInst (determined)
self.variables['gnodes'] =
DetVar('g{}->gnodes'.format(self.index), {gn.sent_index for gn in
self.nodes})
# Abstract GNode indices for GInst (determined)
@@ -603,6 +660,7 @@
"""Representation of a single node (word, position) within a GInst
object."""
def __init__(self, ginst, index, snodes):
+ print("Creating GNode with snodes {}".format(snodes))
self.ginst = ginst
self.index = index
self.sentence = ginst.sentence
@@ -629,12 +687,15 @@
## Create IVars and (set) Vars with sentence DS as root DS
- def ivar(self, key, name, domain):
- self.variables[key] = IVar(name, domain,
rootDS=self.sentence.dstore)
+ def ivar(self, key, name, domain, ess=False):
+ self.variables[key] = IVar(name, domain,
rootDS=self.sentence.dstore,
+ essential=ess)
- def svar(self, key, name, lower, upper, lower_card=0, upper_card=MAX):
+ def svar(self, key, name, lower, upper, lower_card=0, upper_card=MAX,
+ ess=False):
self.variables[key] = Var(name, lower, upper, lower_card,
upper_card,
- rootDS=self.sentence.dstore)
+ rootDS=self.sentence.dstore,
+ essential=ess)
def create_variables(self):
nsnodes = len(self.sentence.nodes)
@@ -692,7 +753,7 @@
GNode in a selected group. Created when a complete variable
assignment.get('features'))
is found for a sentence."""
- def __init__(self, sentence, ginsts, s2gnodes, g2snodes, index):
+ def __init__(self, sentence, ginsts, s2gnodes, index):
self.sentence = sentence
# List of sets of gnode indices
self.s2gnodes = s2gnodes
@@ -718,13 +779,11 @@
"""Combine GInsts for each translation in translation products, and
separate gnodes into a dict for each translation."""
translations = itertools.product(*[g.translations for g in
self.ginsts])
- index = 0
- for translation in translations:
+ for index, translation in enumerate(translations):
t = Translation(self, translation, index)
t.initialize()
t.realize()
self.translations.append(t)
- index += 1
def merge_nodes(self):
"""Merge the source features of cat and inst GNodes associated
with each SNode."""
@@ -733,61 +792,63 @@
gnodes = [self.sentence.gnodes[index] for index in gn_indices]
features = []
for gnode in gnodes:
+ print("gnode {}, snode_anal {}".format(gnode,
gnode.snode_anal))
snode_indices = gnode.snode_indices
snode_index = snode_indices.index(snode.index)
snode_anal = gnode.snode_anal[snode_index]
if snode_anal:
+ print("snode_anal {}".format(snode_anal))
features.append(snode_anal[1])
# Could this fail??
features = Features.unify_all(features)
self.snodes.append((gnodes, features))
- def build_translations(self):
- for translation in self.t:
- print('TRANSLATION {}'.format(trans_index))
- trans_nodes = []
- tginsts, tgnodes, trans_index = translation.groups_tnodes,
translation.gnode_dict, translation.index
- # Figure out the target forms for each snode
- for snode, (gnodes, features) in zip(self.sentence.nodes,
self.snodes):
- if len(gnodes) > 1:
- # There are two gnodes for this snode; only the
concrete node
- # can have translations
- gn0 = tgnodes[gnodes[0]]
- gn1 = tgnodes[gnodes[1]]
- tgroups, tokens, targ_feats, agrs, t_index = zip(gn0,
gn1)
- print('snode {}, gnodes {}'.format(snode, gnodes))
- token = False
- i = 0
- # Find the token that's not a cat
- while not token:
- t = tokens[i]
- if not Entry.is_cat(t):
- token = t
- i += 1
- targ_feats = Features.unify_all(targ_feats)
- # Merge the agreements
- agrs = Solution.combine_agrs(agrs)
- t_index = [(tgroups[0], gn0[-1]), (tgroups[1],
gn1[-1])]
- print(' tokens {}, targ_feats {}, agrs {}, t_index
{}'.format(tokens, targ_feats, agrs, t_index))
- else:
- gnode = gnodes[0]
- print('snode {}, gnode {}'.format(snode, gnode))
- tgroup, token, targ_feats, agrs, t_index =
tgnodes[gnode]
- t_index = [(tgroup, t_index)]
- print(' tokens {}, targ_feats {}, agrs {}, t_index
{}'.format(token, targ_feats, agrs, t_index))
- features.agree(targ_feats, agrs)
- gen = self.sentence.target.generate(token, targ_feats)
- print(" Generating {}/{}: {}".format(token, targ_feats,
gen))
- trans_nodes.append((gen, t_index))
- # Add TNode elements
- tgnode_elements = []
- for ginst, tgnodes in tginsts:
- if tgnodes:
- for tgnode in tgnodes:
- forms = tgnode.generate()
- index = [(ginst, tgnode.index)]
- trans_nodes.append((forms, index))
- self.translations.append(trans_nodes)
+## def build_translations(self):
+## for translation in self.t:
+## print('TRANSLATION {}'.format(trans_index))
+## trans_nodes = []
+## tginsts, tgnodes, trans_index = translation.groups_tnodes,
translation.gnode_dict, translation.index
+## # Figure out the target forms for each snode
+## for snode, (gnodes, features) in zip(self.sentence.nodes,
self.snodes):
+## if len(gnodes) > 1:
+## # There are two gnodes for this snode; only the
concrete node
+## # can have translations
+## gn0 = tgnodes[gnodes[0]]
+## gn1 = tgnodes[gnodes[1]]
+## tgroups, tokens, targ_feats, agrs, t_index =
zip(gn0, gn1)
+## print('snode {}, gnodes {}'.format(snode, gnodes))
+## token = False
+## i = 0
+## # Find the token that's not a cat
+## while not token:
+## t = tokens[i]
+## if not Entry.is_cat(t):
+## token = t
+## i += 1
+## targ_feats = Features.unify_all(targ_feats)
+## # Merge the agreements
+## agrs = Solution.combine_agrs(agrs)
+## t_index = [(tgroups[0], gn0[-1]), (tgroups[1],
gn1[-1])]
+## print(' tokens {}, targ_feats {}, agrs {}, t_index
{}'.format(tokens, targ_feats, agrs, t_index))
+## else:
+## gnode = gnodes[0]
+## print('snode {}, gnode {}'.format(snode, gnode))
+## tgroup, token, targ_feats, agrs, t_index =
tgnodes[gnode]
+## t_index = [(tgroup, t_index)]
+## print(' tokens {}, targ_feats {}, agrs {}, t_index
{}'.format(token, targ_feats, agrs, t_index))
+## features.agree(targ_feats, agrs)
+## gen = self.sentence.target.generate(token, targ_feats)
+## print(" Generating {}/{}: {}".format(token, targ_feats,
gen))
+## trans_nodes.append((gen, t_index))
+## # Add TNode elements
+## tgnode_elements = []
+## for ginst, tgnodes in tginsts:
+## if tgnodes:
+## for tgnode in tgnodes:
+## forms = tgnode.generate()
+## index = [(ginst, tgnode.index)]
+## trans_nodes.append((forms, index))
+## self.translations.append(trans_nodes)
class Translation:
"""Representation of a single translation for an input sentence.
@@ -823,6 +884,19 @@
def __repr__(self):
return "{}[{}] ->".format(self.solution, self.index)
+ def display(self):
+ print("Translation {}: {}".format(self, self.out_string()))
+
+ def out_string(self):
+ '''Convert output to a string for pretty printing.'''
+ l = []
+ for word_list in self.output:
+ if len(word_list) == 1:
+ l.append(word_list[0])
+ else:
+ l.append('|'.join(word_list))
+ return ' '.join(l)
+
def initialize(self):
"""Set up everything needed to run the constraints and generate
the translation."""
self.build()
@@ -830,7 +904,7 @@
self.create_variables()
self.create_constraints()
- def build(self):
+ def build(self, verbosity=0):
"""Unify translation features for merged nodes, map agr features
from source to target,
generate surface target forms from resulting roots and features."""
print('Building {}'.format(self))
@@ -858,7 +932,6 @@
t_indices.append((tgroups[0], gn0[-1]))
if len(tgroups[1].tokens) > 1:
t_indices.append((tgroups[1], gn1[-1]))
-# t_index = [(tgroups[0], gn0[-1]), (tgroups[1], gn1[-1])]
else:
gnode = gnodes[0]
tgroup, token, targ_feats, agrs, t_index = tgnodes[gnode]
@@ -869,7 +942,8 @@
features.agree(targ_feats, agrs)
# Generate target forms for this SNode
gen = self.sentence.target.generate(token, targ_feats)
- print(" Generating node form {}/{}: {}".format(token,
targ_feats, gen))
+ if verbosity:
+ print(" Generating node form {}/{}: {}".format(token,
targ_feats, gen))
self.nodes.append((gen, t_indices))
# Add TNode elements
tgnode_elements = []
@@ -877,7 +951,8 @@
if tnodes:
for tnode in tnodes:
forms = tnode.generate()
- print(' Generating tnode form {}/{}:
{}'.format(tnode.token, tnode.features, forms))
+ if verbosity:
+ print(' Generating tnode form {}/{}:
{}'.format(tnode.token, tnode.features, forms))
index = [(ginst, tnode.index)]
self.nodes.append((forms, index))
@@ -917,7 +992,7 @@
def create_variables(self):
"""Create an IVar for each translation node."""
nnodes = len(self.nodes)
- self.variables = [IVar("o{}".format(i), set(range(nnodes)),
rootDS=self.dstore) for i in range(nnodes)]
+ self.variables = [IVar("o{}".format(i), set(range(nnodes)),
rootDS=self.dstore, essential=True) for i in range(nnodes)]
def create_constraints(self):
"""Make order and disjunction constraints."""
@@ -925,12 +1000,12 @@
self.constraints.append(SetPrecedence([self.variables[first],
self.variables[second]]))
self.constraints.extend(Disjoint(self.variables).constraints)
- def realize(self):
+ def realize(self, verbosity=0):
"""Run constraint satisfaction on the order and disjunction
constraints,
and convert variable values to sentence positions."""
# Run constraint satisfaction on the constraints until all
variables are
# determined or nothing happes to the constraints
- self.solver.run()
+ self.solver.run(verbosity=verbosity)
if self.solver.status == Solver.succeeded:
# No more awake constraints and no undetermined variables; get
positions from
# variable values
=======================================
--- /l3lite/variable.py Wed Apr 30 06:56:30 2014 UTC
+++ /l3lite/variable.py Thu May 1 06:39:44 2014 UTC
@@ -495,11 +495,12 @@
def __init__(self, name, domain=None,
problem=None, dstores=None, rootDS=None,
# Vars with low weights are "peripheral".
- weight=1):
+ weight=1, essential=True):
Var.__init__(self, name,
lower_domain=set(), upper_domain=domain,
lower_card=1, upper_card=1,
- problem=problem, dstores=dstores, rootDS=rootDS,
weight=weight)
+ problem=problem, dstores=dstores, rootDS=rootDS,
+ weight=weight, essential=essential)
def __repr__(self):
return '#{}'.format(
self.name)
=======================================
--- /lite.py Wed Apr 30 06:56:30 2014 UTC
+++ /lite.py Thu May 1 06:39:44 2014 UTC
@@ -28,7 +28,7 @@
# 2014.02.09
# -- Created
-__version__ = 0.3
+__version__ = 0.5
import l3lite
@@ -40,16 +40,25 @@
amh, orm = l3lite.Language.load('amh', 'orm')
ss = l3lite.Sentence(raw="አሳ በላ", language=amh, target=orm)
ss.initialize()
- ss.make_solution(ss.groups, [{0, 1}, {2}])
+ ss.solve()
return ss.solutions[0]
def not_eaten_fish():
amh, orm = l3lite.Language.load('amh', 'orm')
- ss = l3lite.Sentence(raw="አሳ በልቶ አያውቅም", language=amh, target=orm)
- ss.initialize()
-# ss.make_solution(ss.groups, [{0, 1}, {2, 3}, {4}], [0, 0, 1, 1, 2])
-# return ss.solutions[0]
- return ss
+ s = l3lite.Sentence(raw="አሳ በልቶ አያውቅም", language=amh, target=orm)
+ s.do()
+# s.initialize()
+# s.solve()
+# s.solutions[0].translate()
+ return s
+
+def cantar_las_cuarenta(trans=True):
+ spa, eng = l3lite.Language.load('spa', 'eng')
+ s = l3lite.Sentence(raw="les cantó las cuarenta", language=spa,
target=eng if trans else None)
+ s.initialize()
+# s.solve()
+# return s.solutions[0]
+ return s
def order():
constraints = [(0,1), (1,2), (1,3), (3,2)]
@@ -457,5 +466,5 @@
print(" Changed:
{}".format(list(changed)[0].pretty_string()))
if __name__ == "__main__":
- print('L^3 Lite, version {}\n'.format(__version__))
+ print('HIIKTUU, version {}\n'.format(__version__))
==============================================================================
Revision: 0d759cb5483e
Branch: default
Author: Michael Gasser <
gas...@cs.indiana.edu>
Date: Fri May 2 20:10:49 2014 UTC
Log: Handling input words that don't match and group in lexicon
http://code.google.com/p/hltdi-l3/source/detail?r=0d759cb5483e
Modified:
/l3lite/cs.py
/l3lite/entry.py
/l3lite/language.py
/l3lite/languages/eng.lg
/l3lite/sentence.py
/lite.py
=======================================
--- /l3lite/cs.py Thu May 1 06:39:44 2014 UTC
+++ /l3lite/cs.py Fri May 2 20:10:49 2014 UTC
@@ -41,9 +41,12 @@
distributable = 3
skipped = 4
- def __init__(self, constraints, dstore):
+ def __init__(self, constraints, dstore,
+ description=''):
self.constraints = constraints
self.dstore = dstore
+ # Used in solver's printname
+ self.description = description
self.entailed = []
self.failed = []
self.status = Solver.running
@@ -51,7 +54,7 @@
Solver.id += 1
def __repr__(self):
- return "Solver {}".format(
self.id)
+ return "Solver{} ({})".format(' ' + self.description if
self.description else '',
self.id)
def exit(self, result):
if result == Constraint.failed:
@@ -86,7 +89,7 @@
awaken = set(self.constraints)
it = 0
while not self.exit(awaken):
- print("\nRunning iteration {}".format(it))
+ print("Running iteration {}".format(it))
awaken = self.run_constraints(awaken, verbosity=verbosity,
tracevar=tracevar)
it += 1
@@ -150,7 +153,7 @@
print('Adding {} constraints for changed variable
{}'.format(len(update_cons), tracevar))
awaken.update(update_cons)
# print('update cons {}'.format(update_cons))
-# if verbosity:
- print('# changed vars {}'.format(len(all_changed)))
+ if verbosity:
+ print('# changed vars {}'.format(len(all_changed)))
return awaken
=======================================
--- /l3lite/entry.py Thu May 1 06:39:44 2014 UTC
+++ /l3lite/entry.py Fri May 2 20:10:49 2014 UTC
@@ -47,6 +47,8 @@
# -- Matching of group and sentence nodes.
# 2014.04.30
# -- Eliminated everything but Groups.
+# 2014.05.01
+# -- Group/node matching fixed.
import copy, itertools
import yaml
@@ -702,33 +704,49 @@
name=name, trans=trans)
return p
- def match_nodes(self, snodes, head_index):
+ def match_nodes(self, snodes, head_sindex, verbosity=0):
"""Attempt to match the group tokens (and features) with snodes
from a sentence,
returning the snode indices and root and unified features if
any."""
# print("Does {} match {}".format(self, snodes))
match_snodes = []
for index, token in enumerate(self.tokens):
match_snodes1 = []
-# print(" Attempting to match {}".format(token))
+ feats = self.features[index] if self.features else None
+ if verbosity:
+ print(" Attempting to match {}".format(token))
matched = False
for node in snodes:
-# print(" Trying {}".format(node))
- if index == node.index == head_index:
- # This is the token corresponding to the group head
- match_snodes1.append((node.index, None))
-# print(" Head matched already".format(node))
- matched = True
- break
+ if verbosity:
+ print(" Trying {}, token index {}, snode index {}
head index {}".format(node, index, node.index, head_sindex))
+ if index == self.head_index:
+ # This token is the head of the group
+ if node.index == head_sindex:
+ # This was already matched in lexicalization
+# if index == node.index == head_sindex:
+ # This is the token corresponding to the group head
+ node_match = node.match(token, feats)
+ if node_match == False:
+ # This has to match, so fail now
+ return False
+ else:
+ match_snodes1.append((node.index, node_match))
+ if verbosity:
+ print(" Head matched
already".format(node))
+ matched = True
+ # Don't look further for an snode to match
this token
+ break
else:
- feats = self.features[index] if self.features else None
node_match = node.match(token, feats)
-# print('Node {} match {}:{}, {}:: {}'.format(node,
token, index, feats, node_match))
- if node_match:
+ if verbosity:
+ print(' Node {} match {}:{}, {}::
{}'.format(node, token, index, feats, node_match))
+ if node_match != False:
match_snodes1.append((node.index, node_match))
-# print(" Matched node {}".format(node))
+ if verbosity:
+ print(" Matched node {}".format(node))
matched = True
if not matched:
-# print(" {} not matched; failed".format(token))
+ if verbosity:
+ print(" {} not matched; failed".format(token))
return False
else:
match_snodes.append(match_snodes1)
=======================================
--- /l3lite/language.py Thu May 1 06:39:44 2014 UTC
+++ /l3lite/language.py Fri May 2 20:10:49 2014 UTC
@@ -380,7 +380,12 @@
### Generation of word forms
- def generate(self, root, features):
+ def generate(self, root, features, verbosity=0):
+ if verbosity:
+ print("Generating {}:{}".format(root, features))
+ if not features:
+ # Just return the "root"
+ return [root]
if root not in self.genforms:
print("Impossible to generate root {}".format(root))
return
=======================================
--- /l3lite/languages/eng.lg Thu May 1 06:39:44 2014 UTC
+++ /l3lite/languages/eng.lg Fri May 2 20:10:49 2014 UTC
@@ -31,6 +31,9 @@
sang:
root: sing
features: {tns: prt}
+ read:
+ root: read
+ features: {tns: prt}
groups:
end:
=======================================
--- /l3lite/sentence.py Thu May 1 06:39:44 2014 UTC
+++ /l3lite/sentence.py Fri May 2 20:10:49 2014 UTC
@@ -34,6 +34,14 @@
# -- Translation class and realization.
# 2014.04.28
# -- Variables for sentence analysis.
+# 2014.04.29-30
+# -- Fixed some constraints and initial variables values.
+# 2014.05.01
+# -- Handling source words that aren't "covered" (no group candidate
matches them):
+# variables and source constraints.
+# 2014.05.02
+# -- Uncovered source words: target ordering constraints.
+# Introduced "chunks": target output units that are not connected.
import itertools
# ui.py loads language, etc.
@@ -70,7 +78,8 @@
# A dict of sentence-level variables
self.variables = {}
# Solver to find solutions
- self.solver = Solver(self.constraints, self.dstore)
+ self.solver = Solver(self.constraints, self.dstore,
+ description='group selection')
# Solutions found during parsing
self.solutions = []
@@ -170,7 +179,7 @@
for k in keys:
if k in self.language.groups:
for group in self.language.groups[k]:
- print("Checking group {} for {}".format(group,
node))
+# print("Checking group {} for {}".format(group,
node))
# Reject group if it doesn't have a translation in
the target language
if self.target and not
group.get_translations(self.target.abbrev):
print("No translation for {}".format(group))
@@ -182,11 +191,13 @@
groups = []
for head_i, group in candidates:
# Matching snodes, along with root and unified features if any
+ print("Matching group {}".format(group))
snodes = group.match_nodes(self.nodes, head_i)
- print('Group {} matches snodes {}'.format(group, snodes))
if not snodes:
# This group is out
+ print("Failed to match")
continue
+ print('Group {} matches snodes {}'.format(group, snodes))
# print('Found indices {} for group {}, head index
{}'.format(indices, group, head_i))
groups.append((head_i, snodes, group))
# Create a GInst object and GNodes for each surviving group
@@ -200,6 +211,16 @@
sent_index += 1
# Number of GNodes
self.ngnodes = sent_index
+ # Record uncovered snodes
+ covered = {}
+ for gnode in self.gnodes:
+ si = gnode.snode_indices
+ for i in si:
+ if i not in covered:
+ covered[i] = []
+ covered[i].append(gnode.sent_index)
+ for snode in self.nodes:
+ snode.gnodes = covered.get(snode.index, [])
## Create IVars and (set) Vars with sentence DS as root DS
@@ -236,7 +257,10 @@
# At least size of smallest group, at most all
min([len(g.nodes) for g in self.groups]),
self.ngnodes)
- self.variables['snodes'] = DetVar('snodes',
set(range(len(self.nodes))))
+ # covered snodes
+ covered_snodes = {sn.index for sn in self.nodes if sn.gnodes}
+ self.variables['snodes'] = DetVar('snodes', covered_snodes)
+# set(range(len(self.nodes))))
# Category (abstract) nodes
self.svar('catgnodes', set(), catnodes)
# Instance gnodes that are merged with catnodes
@@ -259,9 +283,11 @@
def create_constraints(self):
# Relation among abstract, concrete, and all gnodes for each snode
for snode in self.nodes:
- self.constraints.extend(Union([snode.variables['gnodes'],
- snode.variables['cgnodes'],
-
snode.variables['agnodes']]).constraints)
+ if snode.gnodes:
+ # Only do this for covered snodes
+ self.constraints.extend(Union([snode.variables['gnodes'],
+ snode.variables['cgnodes'],
+
snode.variables['agnodes']]).constraints)
# Constraints involved groups with category (abstract) nodes
for group in self.groups:
if group.nanodes > 0:
@@ -342,9 +368,11 @@
gn2s = [gn.variables['snodes'] for gn in self.gnodes]
s2gn = [s.variables['gnodes'] for s in self.nodes]
for snode in self.nodes:
-
self.constraints.append(UnionSelection(DetVar("sn{}".format(snode.index),
{snode.index}),
-
snode.variables['gnodes'],
- gn2s))
+ if snode.gnodes:
+ # Only for covered snodes
+
self.constraints.append(UnionSelection(DetVar("sn{}".format(snode.index),
{snode.index}),
+
snode.variables['gnodes'],
+ gn2s))
# Union of all gnodes used for snodes is all gnodes used
self.constraints.append(UnionSelection(self.variables['gnodes'],
self.variables['snodes'],
@@ -376,7 +404,6 @@
self.constraints.append(ComplexSetConvexity(self.variables['groups'],
[g.variables['tree']
for g in self.groups]))
-
def run(self):
"""Run constraint satisfaction on constraints, for now without
search if
no solution is found."""
@@ -418,6 +445,8 @@
self.sentence = sentence
# We'll need these for multiple matchings
self.cats = self.get_cats()
+ # Indices of candidate gnodes for this snode found during
lexicalization
+ self.gnodes = None
# Dict of variables specific to this SNode
self.variables = {}
## Tokens in target language for this SNode
@@ -439,25 +468,29 @@
rootDS=self.sentence.dstore,
essential=ess)
def create_variables(self):
- # Indices of concrete (inst) and abstract (cat) gnodes
- cgnodes = {gn.sent_index for gn in self.sentence.gnodes if not
gn.cat}
- agnodes = {gn.sent_index for gn in self.sentence.gnodes if
gn.cat}
- # GNodes associated with this SNode: 0, 1, or 2
- self.svar('gnodes', "w{}->gn".format(self.index), set(),
- set(range(self.sentence.ngnodes)),
- 0, 2, ess=True)
- # Concrete GNodes associated with this SNode: must be 1
- self.svar('cgnodes', "w{}->cgn".format(self.index), set(),
- cgnodes,
- 1, 1)
- # Abstract GNodes associated with this SNode: 0 or 1
- self.svar('agnodes', "w{}->agn".format(self.index), set(),
- agnodes,
- 0, 1)
- # Merged concrete GNodes associated with this SNode: 0 or 1
- self.svar('mgnodes', "w{}->mgn".format(self.index), set(),
- cgnodes,
- 0, 1)
+ if not self.gnodes:
+ # Nothing matched this snode; all variables empty
+ self.variables['gnodes'] = EMPTY
+ self.variables['cgnodes'] = EMPTY
+ self.variables['agnodes'] = EMPTY
+ self.variables['mgnodes'] = EMPTY
+ else:
+ # GNodes associated with this SNode: 0, 1, or 2
+ self.svar('gnodes', "w{}->gn".format(self.index), set(),
+ set(range(self.sentence.ngnodes)),
+ 0, 2, ess=True)
+ # Concrete GNodes associated with this SNode: must be 1
+ self.svar('cgnodes', "w{}->cgn".format(self.index), set(),
+ {gn.sent_index for gn in self.sentence.gnodes if not
gn.cat},
+ 1, 1)
+ # Abstract GNodes associated with this SNode: 0 or 1
+ self.svar('agnodes', "w{}->agn".format(self.index), set(),
+ {gn.sent_index for gn in self.sentence.gnodes if
gn.cat},
+ 0, 1)
+ # Merged concrete GNodes associated with this SNode: 0 or 1
+ self.svar('mgnodes', "w{}->mgn".format(self.index), set(),
+ {gn.sent_index for gn in self.sentence.gnodes if not
gn.cat},
+ 0, 1)
def get_cats(self):
"""The set of categories for the node's token, or None."""
@@ -469,18 +502,20 @@
cats.update(analysis['cats'])
return cats
- def match(self, item, features):
+ def match(self, item, features, verbosity=0):
"""Does this node match the group item (word, lexeme, category) and
any features associated with it?"""
-# print(' SNode {} with features {} trying to match item {} with
features {}'.format(self, self.analyses, item, features))
+ if verbosity:
+ print(' SNode {} with features {} trying to match item {}
with features {}'.format(self, self.analyses, item, features))
# If item is a category, don't bother looking at token
if Entry.is_cat(item):
-# print(' Cat item, looking in {}'.format(self.cats))
+ if verbosity:
+ print(' Cat item, looking in {}'.format(self.cats))
if self.cats and item in self.cats:
# print(" Token {} is in cat {}".format(self.token, item))
if not self.analyses or not features:
- # Match; failure would be "fail"
- return False
+ # Match; failure would be False
+ return None
else:
for analysis in self.analyses:
node_features = analysis.get('features')
@@ -491,10 +526,11 @@
# print(" Matching group features {} and sentence
features {}".format(features, node_features))
# if node_features and
node_features.unify(features) != 'fail':
# return True
+ # None succeeded
return False
elif self.token == item:
# item matches this node's token; features are irrelevant
- return False
+ return None
elif self.analyses:
# Check each combination of root and analysis features
for analysis in self.analyses:
@@ -660,7 +696,6 @@
"""Representation of a single node (word, position) within a GInst
object."""
def __init__(self, ginst, index, snodes):
- print("Creating GNode with snodes {}".format(snodes))
self.ginst = ginst
self.index = index
self.sentence = ginst.sentence
@@ -775,7 +810,7 @@
ginst.set_translations()
self.make_translations()
- def make_translations(self):
+ def make_translations(self, verbosity=0):
"""Combine GInsts for each translation in translation products, and
separate gnodes into a dict for each translation."""
translations = itertools.product(*[g.translations for g in
self.ginsts])
@@ -785,19 +820,19 @@
t.realize()
self.translations.append(t)
- def merge_nodes(self):
+ def merge_nodes(self, verbosity=0):
"""Merge the source features of cat and inst GNodes associated
with each SNode."""
for snode, gn_indices in zip(self.sentence.nodes, self.s2gnodes):
# gn_indices is either one or two ints indexing gnodes in
self.gnodes
gnodes = [self.sentence.gnodes[index] for index in gn_indices]
features = []
for gnode in gnodes:
- print("gnode {}, snode_anal {}".format(gnode,
gnode.snode_anal))
+# print("gnode {}, snode_anal {}".format(gnode,
gnode.snode_anal))
snode_indices = gnode.snode_indices
snode_index = snode_indices.index(snode.index)
snode_anal = gnode.snode_anal[snode_index]
if snode_anal:
- print("snode_anal {}".format(snode_anal))
+# print("snode_anal {}".format(snode_anal))
features.append(snode_anal[1])
# Could this fail??
features = Features.unify_all(features)
@@ -868,6 +903,8 @@
self.groups_tnodes.append((tgroup, tnodes))
# form list / order constraint pairs for each sentence position
self.nodes = []
+ # Ordered units: merged groups or uncovered words
+ self.chunks = []
# pairs of node indices representing order constraints
self.order_pairs = []
# Root domain store for variables
@@ -877,7 +914,8 @@
# Order and disjunction constraints
self.constraints = []
# Translation needs a solver to figure out positions of words
- self.solver = Solver(self.constraints, self.dstore)
+ self.solver = Solver(self.constraints, self.dstore,
+ description='target realization')
# Final output
self.output = None
@@ -900,6 +938,7 @@
def initialize(self):
"""Set up everything needed to run the constraints and generate
the translation."""
self.build()
+ self.set_chunks()
self.make_order_pairs()
self.create_variables()
self.create_constraints()
@@ -907,44 +946,55 @@
def build(self, verbosity=0):
"""Unify translation features for merged nodes, map agr features
from source to target,
generate surface target forms from resulting roots and features."""
- print('Building {}'.format(self))
+ if verbosity:
+ print('Building {}'.format(self))
tginsts, tgnodes, trans_index = self.groups_tnodes,
self.gnode_dict, self.index
# Figure out the target forms for each snode
for snode, (gnodes, features) in zip(self.sentence.nodes,
self.solution.snodes):
- t_indices = []
- if len(gnodes) > 1:
- # There are two gnodes for this snode; only the concrete
node
- # can have translations
- gn0, gn1 = tgnodes[gnodes[0]], tgnodes[gnodes[1]]
- tgroups, tokens, targ_feats, agrs, t_index = zip(gn0, gn1)
- token = False
- i = 0
- # Find the token that's not a cat
- while not token:
- t = tokens[i]
- if not Entry.is_cat(t):
- token = t
- i += 1
- targ_feats = Features.unify_all(targ_feats)
- # Merge the agreements
- agrs = Translation.combine_agrs(agrs)
- if len(tgroups[0].tokens) > 1:
- t_indices.append((tgroups[0], gn0[-1]))
- if len(tgroups[1].tokens) > 1:
- t_indices.append((tgroups[1], gn1[-1]))
+ if not gnodes:
+ # snode is not covered by any group
+ self.nodes.append(([snode.token], []))
else:
- gnode = gnodes[0]
- tgroup, token, targ_feats, agrs, t_index = tgnodes[gnode]
- if len(tgroup.tokens) > 1:
- t_indices.append((tgroup, t_index))
+ t_indices = []
+ if len(gnodes) > 1:
+ # There are two gnodes for this snode; only the
concrete node
+ # can have translations
+ gn0, gn1 = tgnodes[gnodes[0]], tgnodes[gnodes[1]]
+ tgroups, tokens, targ_feats, agrs, t_index = zip(gn0,
gn1)
+ token = False
+ i = 0
+ # Find the token that's not a cat
+ while not token:
+ t = tokens[i]
+ if not Entry.is_cat(t):
+ token = t
+ i += 1
+ targ_feats = Features.unify_all(targ_feats)
+ # Merge the agreements
+ agrs = Translation.combine_agrs(agrs)
+ if len(tgroups[0].tokens) > 1:
+ t_indices.append((tgroups[0], gn0[-1]))
+ if len(tgroups[1].tokens) > 1:
+ t_indices.append((tgroups[1], gn1[-1]))
+# print('tgroups {}, token {}, t_indices
{}'.format(tgroups, token, t_indices))
+ else:
+ gnode = gnodes[0]
+ tgroup, token, targ_feats, agrs, t_index =
tgnodes[gnode]
+ if len(tgroup.tokens) > 1:
+ t_indices.append((tgroup, t_index))
+# print('tgroup {}, token {}, t_index
{}'.format(tgroup, token, t_indices))
- # Make target and source features agree as required
- features.agree(targ_feats, agrs)
- # Generate target forms for this SNode
- gen = self.sentence.target.generate(token, targ_feats)
- if verbosity:
- print(" Generating node form {}/{}: {}".format(token,
targ_feats, gen))
- self.nodes.append((gen, t_indices))
+ # Make target and source features agree as required
+ if not targ_feats:
+ targ_feats = Features({})
+ if agrs:
+# print("Feature agree, targ feats {}, agrs
{}".format(targ_feats, agrs))
+ features.agree(targ_feats, agrs)
+ # Generate target forms for this SNode
+ gen = self.sentence.target.generate(token, targ_feats)
+ if verbosity:
+ print(" Generating node form {}/{}: {}".format(token,
targ_feats, gen))
+ self.nodes.append((gen, t_indices))
# Add TNode elements
tgnode_elements = []
for ginst, tnodes in tginsts:
@@ -956,6 +1006,32 @@
index = [(ginst, tnode.index)]
self.nodes.append((forms, index))
+ def set_chunks(self):
+ chunk_attribs = []
+ for index, (tokens, constraints) in enumerate(self.nodes):
+ # Is this an uncovered node/token
+ if not constraints:
+ chunk_attribs.append((tokens[0], {index}))
+ else:
+ # Groups that the node belongs to
+ for g in [c[0] for c in constraints]:
+ # Find previous chunk list if it exists
+ found = False
+ for c, i in chunk_attribs:
+ if c == g:
+ i.add(index)
+ found = True
+ break
+ if not found:
+ chunk_attribs.append((g, {index}))
+ # Merge chunks that share nodes
+ for index, (label, indices) in enumerate(chunk_attribs):
+ if self.chunks and indices.intersection(self.chunks[-1]):
+ # merged this chunk with the last
+ self.chunks[-1].update(indices)
+ else:
+ self.chunks.append(indices)
+
@staticmethod
def combine_agrs(agr_list):
"""Merge agr dicts in agr_list into a single agr dict."""
@@ -974,10 +1050,12 @@
result[k] = v
return result
- def make_order_pairs(self):
- """Convert group/index pairs to integer (index) order pairs."""
+ def make_order_pairs(self, verbosity=0):
+ """Convert group/index pairs to integer (index) order pairs.
+ Constrain chunks to appear in source-language order."""
tgroup_dict = {}
for index, (forms, constraints) in enumerate(self.nodes):
+# print('index {}, forms {}, constraints {}'.format(index,
forms, constraints))
for tgroup, tg_index in constraints:
if tgroup not in tgroup_dict:
tgroup_dict[tgroup] = []
@@ -988,6 +1066,12 @@
# Sort by the target index
pairpair.sort(key=lambda x: x[1])
self.order_pairs.append([x[0] for x in pairpair])
+ # Chunks
+ for ci, indices in enumerate(self.chunks[:-1]):
+ next_indices = self.chunks[ci+1]
+ for index in indices:
+ for next_index in next_indices:
+ self.order_pairs.append([index, next_index])
def create_variables(self):
"""Create an IVar for each translation node."""
=======================================
--- /lite.py Thu May 1 06:39:44 2014 UTC
+++ /lite.py Fri May 2 20:10:49 2014 UTC
@@ -46,19 +46,24 @@
def not_eaten_fish():
amh, orm = l3lite.Language.load('amh', 'orm')
s = l3lite.Sentence(raw="አሳ በልቶ አያውቅም", language=amh, target=orm)
- s.do()
+ s.initialize()
+ s.solve()
+ return s.solutions[0]
+# s.do()
# s.initialize()
# s.solve()
# s.solutions[0].translate()
- return s
def cantar_las_cuarenta(trans=True):
spa, eng = l3lite.Language.load('spa', 'eng')
- s = l3lite.Sentence(raw="les cantó las cuarenta", language=spa,
target=eng if trans else None)
+ s = l3lite.Sentence(raw="Paula les cantó las cuarenta", language=spa,
target=eng if trans else None)
s.initialize()
+ s.solve()
+ return s.solutions[0]
+# s.solve()
+# return s.solutions[0]
# s.solve()
# return s.solutions[0]
- return s
def order():
constraints = [(0,1), (1,2), (1,3), (3,2)]
==============================================================================
Revision: 829e0849435b
Branch: default
Author: Michael Gasser <
gas...@cs.indiana.edu>
Date: Fri May 2 22:53:47 2014 UTC
Log: Solved output order problems; 3 example translations in
hiiktuu.py.
http://code.google.com/p/hltdi-l3/source/detail?r=829e0849435b
Added:
/hiiktuu.py
Deleted:
/lite.py
Modified:
/l3lite/cs.py
/l3lite/language.py
/l3lite/sentence.py
/l3lite/variable.py
=======================================
--- /dev/null
+++ /hiiktuu.py Fri May 2 22:53:47 2014 UTC
@@ -0,0 +1,400 @@
+#!/usr/bin/env python3
+
+# Hiiktuu. Parsing and translation with minimal dependency grammars.
+#
+########################################################################
+#
+# This file is part of the HLTDI L^3 project
+# for parsing, generation, translation, and computer-assisted
+# human translation.
+#
+# Copyright (C) 2014, HLTDI <
gas...@cs.indiana.edu>
+#
+# This program is free software: you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, either version 3 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <
http://www.gnu.org/licenses/>.
+#
+# =========================================================================
+
+# 2014.02.09
+# -- Created
+
+__version__ = 0.75
+
+import l3lite
+
+# Profiling
+#import cProfile
+#import pstats
+
+def ate_fish(verbosity=0):
+ """
+ Amh->Orm
+ አሳ በላ (he ate fish) -> qurxummii nyaate.
+ Illustrates
+ (1) source-target feature agreement
+ """
+ amh, orm = l3lite.Language.load('amh', 'orm')
+ s = l3lite.Sentence(raw="አሳ በላ", language=amh, target=orm,
verbosity=verbosity)
+ s.do(verbosity=verbosity)
+ return s
+
+def never_eaten_fish(verbosity=0):
+ """
+ Amh->Orm
+ አሳ በልቶ አያውቅም (he's never eaten fish) -> qurxummii nyaate hin beeku.
+ Illustrates
+ (1) source-target feature agreement
+ (2) source-target group translation mismatch in word count
+ """
+ amh, orm = l3lite.Language.load('amh', 'orm')
+ s = l3lite.Sentence(raw="አሳ በልቶ አያውቅም", language=amh, target=orm,
+ verbosity=verbosity)
+ s.do(verbosity=verbosity)
+ return s
+
+def cantar_las_cuarenta(trans=True, verbosity=0):
+ """
+ Spa->Eng
+ Paula les cantó las cuarenta -> Paula read them the riot act.
+ Illustrates
+ (1) source-target feature agreement
+ (2) source-target group translation mismatch in word count
+ (3) source-target group translation mismatch in word order
+ (4) source word not associated with any group
+ """
+ spa, eng = l3lite.Language.load('spa', 'eng')
+ s = l3lite.Sentence(raw="Paula les cantó las cuarenta",
+ language=spa, target=eng if trans else None,
+ verbosity=verbosity)
+ s.do(verbosity=verbosity)
+ return s
+
+def ui():
+ """Create a UI and two languages."""
+ u = l3lite.UI()
+ e, s = l3lite.Language("English", 'eng'),
l3lite.Language("español", 'spa')
+ return u, e, s
+
+##def t1():
+## constraints = []
+## ## Groups
+## # 0: read(0) %sbd(1) the(2) riot(3) act(4)
+## # 1: read(5) %sth(6)
+## # 2: the(7) boy(8)
+## # 3: us(9)
+## # 4: the(10) act(11)
+## # 5: the(12) riot(13)
+## # 6: .(14)
+## groups = l3lite.Var('groups', set(), {0, 1, 2, 3, 4, 5, 6}, 2, 7)
+## ### Group node indices
+## ## All GNodes
+## gdetvars = [l3lite.DetVar('g0', {0, 1, 2, 3, 4}), # 1 is abstract
+## l3lite.DetVar('g1', {5, 6}), # 6 is abstract
+## l3lite.DetVar('g2', {7, 8}),
+## l3lite.DetVar('g3', {9}),
+## l3lite.DetVar('g4', {10, 11}),
+## l3lite.DetVar('g5', {12, 13}),
+## l3lite.DetVar('g6', {14})]
+## ## Concrete GNodes
+## gc_detvars = [l3lite.DetVar('g0', {0, 2, 3, 4}), # 1 is abstract
+## l3lite.DetVar('g1', {5}), # 6 is abstract
+## l3lite.DetVar('g2', {7, 8}),
+## l3lite.DetVar('g3', {9}),
+## l3lite.DetVar('g4', {10, 11}),
+## l3lite.DetVar('g5', {12, 13}),
+## l3lite.DetVar('g6', {14})]
+## ## Abstract GNodes
+## ga_detvars = [l3lite.DetVar('g0', {1}),
+## l3lite.DetVar('g1', {6}),
+## l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY, l3lite.EMPTY]
+## ## Words (SNodes)
+## sentence = ['the', 'boy', 'read', 'us', 'the', 'riot', 'act']
+## ## Word positions associated with group nodes (what about unused
gnodes?)
+## gn0w = l3lite.DetIVar('gn0w', 2)
+## gn1w = l3lite.IVar('gn1w', {1, 3}) # abstract
+## gn2w = l3lite.IVar('gn2w', {0, 4})
+## gn3w = l3lite.DetIVar('gn3w', 5)
+## gn4w = l3lite.DetIVar('gn4w', 6)
+## gn5w = l3lite.DetIVar('gn5w', 2)
+## gn6w = l3lite.IVar('gn6w', {5, 6}) # abstract
+## gn7w = l3lite.IVar('gn7w', {0, 4})
+## gn8w = l3lite.DetIVar('gn8w', 1)
+## gn9w = l3lite.DetIVar('gn9w', 3)
+## gn10w = l3lite.IVar('gn10w', {0, 4})
+## gn11w = l3lite.DetIVar('gn11w', 6)
+## gn12w = l3lite.IVar('gn12w', {0, 4})
+## gn13w = l3lite.DetIVar('gn13w', 5)
+## gn14w = l3lite.DetIVar('gn14w', 7)
+## gn_w = [gn0w, gn1w, gn2w, gn3w, gn4w, gn5w, gn6w, gn7w, gn8w, gn9w,
gn10w, gn11w, gn12w, gn13w, gn14w]
+## ## Groups associated with group nodes
+## gn_g = [l3lite.DetIVar('gn0_g', 0),
+## l3lite.DetIVar('gn1_g', 0),
+## l3lite.DetIVar('gn2_g', 0),
+## l3lite.DetIVar('gn3_g', 0),
+## l3lite.DetIVar('gn4_g', 0),
+## l3lite.DetIVar('gn5_g', 1), l3lite.DetIVar('gn6_g', 1),
+## l3lite.DetIVar('gn7_g', 2), l3lite.DetIVar('gn8_g', 2),
+## l3lite.DetIVar('gn9_g', 3),
+## l3lite.DetIVar('gn10_g', 4), l3lite.DetIVar('gn11_g', 4),
+## l3lite.DetIVar('gn12_g', 5), l3lite.DetIVar('gn13_g', 5),
+## l3lite.DetIVar('gn14_g', 6)]
+## ## Group nodes associated with words
+## w0gn = l3lite.IVar('w0gn', {2, 7, 10, 12}) # the
+## w1gn = l3lite.Var('w1gn', set(), {1, 8}, 1, 2) # boy
+## w2gn = l3lite.IVar('w2gn', {0, 5}) # read
+## w3gn = l3lite.Var('w3gn', set(), {1, 9}, 1, 2) # us
+## w4gn = l3lite.IVar('w4gn', {2, 7, 10, 12}) # the
+## w5gn = l3lite.Var('w5gn', set(), {3, 6, 13}, 1, 2) # riot
+## w6gn = l3lite.Var('w6gn', set(), {4, 6, 11}, 1, 2) # act
+## w7gn = l3lite.DetIVar('w7gn', 14)
+## w2gnodes = [w0gn, w1gn, w2gn, w3gn, w4gn, w5gn, w6gn, w7gn]
+## # Union selection on gnodes for each snode
+## constraints.append(l3lite.UnionSelection(l3lite.DetVar('one', {1}),
w1gn, gn_w))
+## constraints.append(l3lite.UnionSelection(l3lite.DetVar('three',
{3}), w3gn, gn_w))
+## ### Word (SNode) ->
+## ## Concrete group nodes associated with words
+## w0cgn = w0gn
+## w1cgn = l3lite.DetIVar('w1cgn', 8)
+## w2cgn = w2gn
+## w3cgn = l3lite.DetIVar('w3cgn', 9)
+## w4cgn = w4gn
+## w5cgn = l3lite.IVar('w5cgn', {3, 13})
+## w6cgn = l3lite.IVar('w6cgn', {4, 11})
+## w7cgn = w7gn
+## w2cgnodes = [w0cgn, w1cgn, w2cgn, w3cgn, w4cgn, w5cgn, w6cgn, w7cgn]
+## ## w->m Merged concrete group nodes associated with words
+## w1gnm = l3lite.Var('w1gnm', set(), {8}, 0, 1) # boy (merged
with sbd)
+## w3gnm = l3lite.Var('w3gnm', set(), {9}, 0, 1) # us (merged
with sbd)
+## w5gnm = l3lite.Var('w5gnm', set(), {13}, 0, 1) # riot (merged
with sth)
+## w6gnm = l3lite.Var('w6gnm', set(), {11}, 0, 1) # act (merged
with sth)
+## w2gnm_nodes = [l3lite.EMPTY, w1gnm, l3lite.EMPTY, w3gnm,
l3lite.EMPTY, w5gnm, w6gnm, l3lite.EMPTY]
+## ## w->a Abstract group nodes associated with words
+## w1ga = l3lite.Var('w1ga', set(), {1}, 0, 1)
+## w3ga = l3lite.Var('w3ga', set(), {1}, 0, 1)
+## w5ga = l3lite.Var('w5ga', set(), {6}, 0, 1)
+## w6ga = l3lite.Var('w6ga', set(), {6}, 0, 1)
+## w2ga_nodes = [l3lite.EMPTY, w1ga, l3lite.EMPTY, w3ga, l3lite.EMPTY,
w5ga, w6ga, l3lite.EMPTY]
+## ## All word nodes
+## nodes = l3lite.DetVar('nodes', {0, 1, 2, 3, 4, 5, 6, 7})
+## ## Union selection on the gnodes of all words
+## # Words 1 (boy), 3 (us), 5 (riot), 6 (act) can have abstract nodes
+## constraints.extend(l3lite.Union([w1gn, w1cgn, w1ga]).constraints)
+## constraints.extend(l3lite.Union([w3gn, w3cgn, w3ga]).constraints)
+## constraints.extend(l3lite.Union([w5gn, w5cgn, w5ga]).constraints)
+## constraints.extend(l3lite.Union([w6gn, w6cgn, w6ga]).constraints)
+## # Union of all group nodes used
+## gnodeU = l3lite.Var('gnodeU', set(),
+## {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14}, 7, 9)
+## constraints.append(l3lite.UnionSelection(gnodeU, nodes, w2gnodes))
+## ## Union selection on the gnodes of all groups
+## constraints.append(l3lite.UnionSelection(gnodeU, groups, gdetvars))
+## ## Union selection on the words associated with each group node
+## constraints.append(l3lite.UnionSelection(nodes, gnodeU, gn_w))
+## # Positions of gnodes in each group
+## g0snode = l3lite.Var('g0snode', set(), {0, 1, 2, 3, 4, 5, 6}, 5, 5)
# read sbd the riot act
+## g1snode = l3lite.Var('g1snode', set(), {2, 5, 6}, 2, 2)
# read sth
+## g2snode = l3lite.Var('g2snode', set(), {0, 1, 4}, 2, 2)
# the boy
+## g3snode = l3lite.DetIVar('g3snode', 3)
# us
+## g4snode = l3lite.Var('g4snode', set(), {0, 4, 6}, 2, 2)
# the act
+## g5snode = l3lite.Var('g5snode', set(), {0, 4, 5}, 2, 2)
# the riot
+## g6snode = l3lite.DetIVar('g6snode', 7)
# .
+## gsnodes = [g0snode, g1snode, g2snode, g3snode, g4snode, g5snode,
g6snode]
+## # Positions of concrete gnodes in each group
+## g0cw = l3lite.Var('g0cw', set(), {0, 1, 2, 3, 4, 5, 6}, 4, 4)
+## g1cw = l3lite.DetVar('g1cw', {2})
+## g2cw = g2snode
+## g3cw = g3snode
+## g4cw = g4snode
+## g5cw = g5snode
+## g6cw = g6snode
+## g_cwords = [g0cw, g1cw, g2cw, g3cw, g4cw, g5cw, g6cw]
+## # Positions of abstract gnodes in each group
+## g0aw = l3lite.IVar('g0aw', {0, 1, 2, 3, 4, 5, 6})
+## g1aw = l3lite.IVar('g1aw', {0, 1, 2, 3, 4, 5, 6})
+## g_awords = [g0aw, g1aw,
+## l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY]
+## # Union constraints for group abstract and concrete gnodes
+## constraints.extend(l3lite.Union([g0snode, g0cw, g0aw]).constraints)
+## constraints.extend(l3lite.Union([g1snode, g1cw, g1aw]).constraints)
+## ## Groups merged with groups
+## g0m = l3lite.Var('g0m', set(), {2, 3}, 0, 1)
+## g1m = l3lite.Var('g1m', set(), {4, 5}, 0, 1)
+## ## Groups merged with groups, including the group itself
+## g0gm = l3lite.Var('g0gm', {0}, {0, 2, 3}, 1, 2)
+## g1gm = l3lite.Var('g1gm', {1}, {1, 4, 5}, 1, 2)
+## g2gm = l3lite.DetVar('g2gm', {2})
+## g3gm = l3lite.DetVar('g3gm', {3})
+## g4gm = l3lite.DetVar('g4gm', {4})
+## g5gm = l3lite.DetVar('g5gm', {5})
+## g6gm = l3lite.DetVar('g6gm', {6})
+## # Relation between g->m only and g->m + self
+## constraints.extend(l3lite.Union([g0gm, g0m, l3lite.DetIVar('zero',
0)]).constraints)
+## constraints.extend(l3lite.Union([g1gm, g1m, l3lite.DetIVar('one',
1)]).constraints)
+## ## Abstract group node mergers (empty set possible if not selected)
+## # a->m
+## # Merge %sbd with boy or us
+## gan1m = l3lite.Var('gan1m', set(), {8, 9}, 0, 1)
+## # Merge %sth with riot or act
+## gan6m = l3lite.Var('gan6m', set(), {11, 13}, 0, 1)
+## gan_m = [l3lite.EMPTY, gan1m, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY,
+## l3lite.EMPTY, gan6m, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY,
+## l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY]
+## # Merged gnodes must be among selected gnodes
+## constraints.extend(l3lite.Inclusion([gan1m, gnodeU]).constraints)
+## constraints.extend(l3lite.Inclusion([gan6m, gnodeU]).constraints)
+## # Relate merged gnodes to merged groups; selection variables should
+## # be the union of a->m variables for each group that has any
+## # Group 0, abstract gnode 1
+## constraints.append(l3lite.UnionSelection(g0m, gan1m, gn_g))
+## # Group 1, abstract gnode 6
+## constraints.append(l3lite.UnionSelection(g1m, gan6m, gn_g))
+## ## m-> a; Abstract nodes associated with concrete gnodes
+## gm8ga = l3lite.Var('gm8ga', set(), {1}, 0, 1)
+## gm9ga = l3lite.Var('gm9ga', set(), {1}, 0, 1)
+## gm11ga = l3lite.Var('gm11ga', set(), {6}, 0, 1)
+## gm13ga = l3lite.Var('gm13ga', set(), {6}, 0, 1)
+## gm_ga = [l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY,
+## l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, gm8ga, gm9ga,
l3lite.EMPTY,
+## gm11ga, l3lite.EMPTY, gm13ga,l3lite.EMPTY]
+## ## a->w Words associated with abstract group nodes
+## ga1w = l3lite.Var('ga1w', set(), {1, 3}, 0, 1)
+## ga6w = l3lite.Var('ga6w', set(), {5, 6}, 0, 1)
+## ga_w = [l3lite.EMPTY, ga1w, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY, l3lite.EMPTY,
+## ga6w, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY, l3lite.EMPTY,
+## l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY]
+## ## m->w Words associated with merged group nodes
+## gnm8w = l3lite.Var('gnm8w', set(), {1}, 0, 1)
+## gnm9w = l3lite.Var('gnm9w', set(), {3}, 0, 1)
+## gnm11w = l3lite.Var('gnm11w', set(), {6}, 0, 1)
+## gnm13w = l3lite.Var('gnm13w', set(), {5}, 0, 1)
+## gnm_w = [l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY,
+## l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
+## gnm8w, gnm9w, l3lite.EMPTY, gnm11w, l3lite.EMPTY,
+## gnm13w, l3lite.EMPTY]
+## # Merging means the gnodes have the same sentence position
+## # gn1w = gn8w | gn9w ; gn6w = gn11w | gn13w
+## # Abstract nodes in used groups
+## anodes = l3lite.Var('anodes', set(), {1, 6})
+## constraints.append(l3lite.UnionSelection(anodes, groups,
[l3lite.DetVar('g0a', {1}),
+##
l3lite.DetVar('g1a', {6}),
+##
l3lite.EMPTY, l3lite.EMPTY,
+##
l3lite.EMPTY, l3lite.EMPTY,
+##
l3lite.EMPTY]))
+## # Word nodes that involve a merging of two gnodes
+## mwords = l3lite.Var('mwords', set(), {1, 3, 5, 6}, 0, 2)
+## # Set of all concrete gnodes that are merged with abstract nodes
+## merged_gnodes = l3lite.Var('merged_gnodes', set(), {0, 2, 3, 4, 5,
7, 8, 9, 10, 11, 12, 13, 14},
+## 0, 2)
+## ## UnionSelection constraints relating words, abstract nodes, and
merged gnodes
+## # Set of merged nodes is the union of the merged nodes for the set
of selected abstract nodes
+## constraints.append(l3lite.UnionSelection(merged_gnodes, anodes,
gan_m))
+## # Set of merged nodes is the union of the merged nodes of all merge
words
+## constraints.append(l3lite.UnionSelection(merged_gnodes, mwords,
w2gnm_nodes))
+## # Set of abstract nodes is the union of the abstract nodes of all
merge words
+## constraints.append(l3lite.UnionSelection(anodes, mwords, w2ga_nodes))
+## # Set of abstract nodes is the union of the abstract nodes of all
gnodes
+## constraints.append(l3lite.UnionSelection(anodes, merged_gnodes,
gm_ga))
+## # Set of merge words is the union of the words associated with all
selected abstract nodes
+## constraints.append(l3lite.UnionSelection(mwords, anodes, ga_w))
+## # Set of merge words is the union of the words associated with all
merge group nodes
+## constraints.append(l3lite.UnionSelection(mwords, merged_gnodes,
gnm_w))
+## ## Constraints on merged and abstract nodes
+## # Set of merged nodes must be a subset of all used gnodes
+## constraints.append(l3lite.Inclusion([merged_gnodes,
gnodeU]).constraints[0])
+## # All words must have different abstract nodes
+## constraints.extend(l3lite.Disjoint(w2ga_nodes).constraints)
+## # All concrete nodes must nodes have different abstract nodes
+## constraints.extend(l3lite.Disjoint(gm_ga).constraints)
+## ## Trees under gnode heads
+## g0tree = l3lite.Var('g0tree', set(), {0, 1, 2, 3, 4, 5, 6}, 5) #
at least 5 snodes
+## g1tree = l3lite.Var('g1tree', set(), {0, 1, 2, 3, 4, 5, 6}, 2) #
at least 2 snodes
+## g2tree = g2snode
+## g3tree = g3snode
+## g4tree = g4snode
+## g5tree = g5snode
+## g6tree = g6snode
+## gtrees = [g0tree, g1tree, g2snode, g3snode, g4snode, g5snode,
g6snode]
+## # g1 tree is the union of g0snode and the tree under whatever group
+## # merges with gnode 1 (sbd)
+## constraints.append(l3lite.UnionSelection(g0tree, g0gm, gsnodes))
+## constraints.append(l3lite.UnionSelection(g1tree, g1gm, gsnodes))
+## # Relate gnode merger with group merger
+## # If gnode 1 merges with gnode 8, then group 0 merges with group 2;
+## # if gnode 1 merges with gnode 9, then group 0 merges with group 3
+## # Select one or the other
+## # UnionSelection(choice, IVar('chooser', {0, 1}), [choice1, choice2])
+#### g0merge_sel = l3lite.IVar('g0m_sel', {0, 1, 2})
+#### constraints.append(l3lite.UnionSelection(g0gm, g0merge_sel,
[l3lite.DetVar('g0g2', {0, 2}),
+####
l3lite.DetVar('g0g3', {0, 3}),
+####
l3lite.DetVar('g0_', {0})]))
+#### constraints.append(l3lite.UnionSelection(gan1m, g0merge_sel,
+#### [l3lite.DetVar('gan1g8',
{8}),
+#### l3lite.DetVar('gan1g9',
{9}),
+#### l3lite.EMPTY]))
+#### g1merge_sel = l3lite.IVar('g1m_sel', {0, 1, 2})
+#### constraints.append(l3lite.UnionSelection(g1gm, g1merge_sel,
[l3lite.DetVar('g1g4', {1, 4}),
+####
l3lite.DetVar('g1g5', {1, 5}),
+####
l3lite.DetVar('g1_', {1})]))
+#### constraints.append(l3lite.UnionSelection(gan6m, g1merge_sel,
+#### [l3lite.DetVar('gan6g11',
{11}),
+#### l3lite.DetVar('gan6g13',
{13}),
+#### l3lite.EMPTY]))
+## ### Order
+## # Position pair constraints
+## gposcons = l3lite.Var('gposcons', set(),
+## {(0, 1), (1, 2), (2, 3), (3, 4), (0, 2), (1,
3), (2, 4), (0, 3), (1, 4),
+## (5, 6), (7, 8), (10, 11), (12, 13)})
+## # Union selection on position pair constraints
+## gp0 = l3lite.DetVar('gp0', {(0, 1), (1, 2), (2, 3), (3, 4), (0, 2),
(1, 3), (2, 4), (0, 3), (1, 4)})
+## gp1 = l3lite.DetVar('gp1', {(5, 6)})
+## gp2 = l3lite.DetVar('gp2', {(7, 8)})
+## gp3 = l3lite.DetVar('gp3', set())
+## gp4 = l3lite.DetVar('gp4', {(10, 11)})
+## gp5 = l3lite.DetVar('gp5', {(12, 13)})
+## gp6 = l3lite.DetVar('gp6', set())
+## gpos = [gp0, gp1, gp2, gp3, gp4, gp5, gp6]
+## constraints.append(l3lite.PrecedenceSelection(gposcons,
+## [gn0w, gn1w, gn2w,
gn3w, gn4w, gn5w,
+## gn6w, gn7w, gn8w,
gn9w, gn10w, gn11w, gn12w, gn13w, gn14w]))
+## # Union selection on position pair constraints
+## constraints.append(l3lite.UnionSelection(gposcons, groups, gpos))
+## # Complex union selection on positions of all concrete nodes in each
selected group
+## constraints.append(l3lite.ComplexUnionSelection(selvar=groups,
+## selvars=g_cwords,
+## seqvars=w2cgnodes,
+## mainvars=gc_detvars))
+## # Complex union selection on positions of all abstract gnodes in
each selected group
+## constraints.append(l3lite.ComplexUnionSelection(selvar=groups,
+## selvars=g_awords,
+## seqvars=w2ga_nodes,
+## mainvars=ga_detvars))
+## ### Projectivity
+## # Complex set convexity
+## constraints.append(l3lite.ComplexSetConvexity(groups, gtrees))
+## return constraints
+##
+##def run_constraints(constraints, times=10):
+## for t in range(times):
+## print("\nITERATION {}".format(t))
+## for c in constraints:
+## state, changed = c.run()
+## if state == 0:
+## print('FAILED: constraint {}'.format(c))
+## return
+## if changed:
+## print("Running {}".format(c))
+## print(" Changed:
{}".format(list(changed)[0].pretty_string()))
+
+if __name__ == "__main__":
+ print('HIIKTUU, version {}\n'.format(__version__))
+
=======================================
--- /lite.py Fri May 2 20:10:49 2014 UTC
+++ /dev/null
@@ -1,475 +0,0 @@
-#!/usr/bin/env python3
-
-# L3Lite. Parsing and translation with dependency grammars.
-#
-########################################################################
-#
-# This file is part of the HLTDI L^3 project
-# for parsing, generation, translation, and computer-assisted
-# human translation.
-#
-# Copyright (C) 2014, HLTDI <
gas...@cs.indiana.edu>
-#
-# This program is free software: you can redistribute it and/or
-# modify it under the terms of the GNU General Public License as
-# published by the Free Software Foundation, either version 3 of
-# the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <
http://www.gnu.org/licenses/>.
-#
-# =========================================================================
-
-# 2014.02.09
-# -- Created
-
-__version__ = 0.5
-
-import l3lite
-
-# Profiling
-#import cProfile
-#import pstats
-
-def eat_fish():
- amh, orm = l3lite.Language.load('amh', 'orm')
- ss = l3lite.Sentence(raw="አሳ በላ", language=amh, target=orm)
- ss.initialize()
- ss.solve()
- return ss.solutions[0]
-
-def not_eaten_fish():
- amh, orm = l3lite.Language.load('amh', 'orm')
- s = l3lite.Sentence(raw="አሳ በልቶ አያውቅም", language=amh, target=orm)
- s.initialize()
- s.solve()
- return s.solutions[0]
-# s.do()
-# s.initialize()
-# s.solve()
-# s.solutions[0].translate()
-
-def cantar_las_cuarenta(trans=True):
- spa, eng = l3lite.Language.load('spa', 'eng')
- s = l3lite.Sentence(raw="Paula les cantó las cuarenta", language=spa,
target=eng if trans else None)
- s.initialize()
- s.solve()
- return s.solutions[0]
-# s.solve()
-# return s.solutions[0]
-# s.solve()
-# return s.solutions[0]
-
-def order():
- constraints = [(0,1), (1,2), (1,3), (3,2)]
- vrs = [l3lite.IVar('v0', {0, 1, 2, 3}),
- l3lite.IVar('v1', {0, 1, 2, 3}),
- l3lite.IVar('v2', {0, 1, 2, 3}),
- l3lite.IVar('v3', {0, 1, 2, 3})]
- cs = []
- for v1, v2 in constraints:
- cs.append(l3lite.SetPrecedence([vrs[v1], vrs[v2]]))
- cs.extend(l3lite.Disjoint(vrs).constraints)
- return cs, vrs
-
-def language(name, abbrev):
- return l3lite.Language(name, abbrev)
-
-##def phrases():
-## eng = language('English', 'eng')
-## esp = language('español', 'spa')
-## amh = language('አማርኛ', 'amh')
-## rra = eng.add_phrase(['%read', '$sbd', 'the', 'riot', 'act'],
head='%read')
-### clc = esp.add_phrase(['%cantar', 'a', '$algn', 'las', 'cuarenta'],
0)
-## vo_awq = amh.add_phrase(['$vb', "%'wq"],
-## head="%'wq",
-## features=[{'tam': 'ger'},
{'tam': 'imf', 'pol': 'neg'}],
-## name="%%በልቶ_አያቅም")
-## return rra, vo_awq
-
-##def eg():
-## ### Spanish
-## e = l3lite.Language('español', 'spa')
-## ## Words
-## mm = e.add_word('mujer', cls='$sus')
-## la = e.add_word('la', cls='$det')
-## ## Classes
-## ss = e.add_class('$sus')
-## ss.add_depin('sj', {'min': 0, 'max': 1, 'dflt': 0})
-## ss.add_depin('oj', {'min': 0, 'max': 1, 'dflt': 0})
-## ss.add_depout('det', {'min': 0, 'max': 1, 'dflt': 1})
-## ss.add_order(['det', '^'])
-## ss.set_gram('num', {'sing': 2, 'plur': 1})
-## ss.set_gram('gen', {'masc': 1, 'fem': 1})
-## ss.add_agr('det', 'gen', 'gen')
-## dd = e.add_class('$det')
-## dd.add_depin('det', {'min': 1, 'max': 1})
-## # alguien
-## e.add_class('$alg')
-## # Some grammatical features
-## e.add_gram('plur', 'num')
-## e.add_gram('sing', 'num')
-## e.add_gram('pres', 'tmp')
-## e.add_gram('pret', 'tmp')
-## ## Translations
-## mm.add_trans('grn', 'kuña')
-## mm.add_trans('grn', 'kuñakarai')
-## mm.set_gram('num', 'sing')
-## mm.set_gram('gen', 'fem')
-## la.set_gram('num', 'sing')
-## la.set_gram('gen', 'fem')
-## # Inheritance (after class creation)
-## la.inherit()
-## mm.inherit()
-## ### Guarani
-## g = l3lite.Language('guarani', 'grn')
-## ## Words
-## kk = g.add_word('kuña')
-## ### English
-## E = l3lite.Language('English', 'eng')
-## ## Words
-## rr = E.add_word('read')
-## tt = E.add_word('the')
-## nn = E.add_class('$sbd')
-## ## Groups
-## # English: read sbd. the riot act
-## rra = E.add_group('read_sbd_the_riot_act', 'read')
-## rra.add_word('$sbd')
-## rra.add_word('the')
-## rra.add_word('riot')
-## rra.add_word('act')
-## # Spanish: cantar (a) alg. las cuarenta
-## clc = e.add_group('cantar_alg_las_cuarenta', '%cantar')
-## clc.add_word('$alg')
-## clc.add_word('las')
-## clc.add_word('cuarenta')
-## # Translation: Eng->Spa
-## rra.add_trans('spa', 'cantar_alg_las_cuarenta')
-## rra.add_trans_map('spa', 'cantar_alg_las_cuarenta', 0, 0)
-## rra.add_trans_map('spa', 'cantar_alg_las_cuarenta', 1, 1)
-## rra.add_trans_del('spa', 'cantar_alg_las_cuarenta', 3)
-## return e, g, E
-
-def ui():
- u = l3lite.UI()
- e, s = l3lite.Language("English", 'eng'),
l3lite.Language("español", 'spa')
- return u, e, s
-
-def t1():
- constraints = []
- ## Groups
- # 0: read(0) %sbd(1) the(2) riot(3) act(4)
- # 1: read(5) %sth(6)
- # 2: the(7) boy(8)
- # 3: us(9)
- # 4: the(10) act(11)
- # 5: the(12) riot(13)
- # 6: .(14)
- groups = l3lite.Var('groups', set(), {0, 1, 2, 3, 4, 5, 6}, 2, 7)
- ### Group node indices
- ## All GNodes
- gdetvars = [l3lite.DetVar('g0', {0, 1, 2, 3, 4}), # 1 is abstract
- l3lite.DetVar('g1', {5, 6}), # 6 is abstract
- l3lite.DetVar('g2', {7, 8}),
- l3lite.DetVar('g3', {9}),
- l3lite.DetVar('g4', {10, 11}),
- l3lite.DetVar('g5', {12, 13}),
- l3lite.DetVar('g6', {14})]
- ## Concrete GNodes
- gc_detvars = [l3lite.DetVar('g0', {0, 2, 3, 4}), # 1 is abstract
- l3lite.DetVar('g1', {5}), # 6 is abstract
- l3lite.DetVar('g2', {7, 8}),
- l3lite.DetVar('g3', {9}),
- l3lite.DetVar('g4', {10, 11}),
- l3lite.DetVar('g5', {12, 13}),
- l3lite.DetVar('g6', {14})]
- ## Abstract GNodes
- ga_detvars = [l3lite.DetVar('g0', {1}),
- l3lite.DetVar('g1', {6}),
- l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY]
- ## Words (SNodes)
- sentence = ['the', 'boy', 'read', 'us', 'the', 'riot', 'act']
- ## Word positions associated with group nodes (what about unused
gnodes?)
- gn0w = l3lite.DetIVar('gn0w', 2)
- gn1w = l3lite.IVar('gn1w', {1, 3}) # abstract
- gn2w = l3lite.IVar('gn2w', {0, 4})
- gn3w = l3lite.DetIVar('gn3w', 5)
- gn4w = l3lite.DetIVar('gn4w', 6)
- gn5w = l3lite.DetIVar('gn5w', 2)
- gn6w = l3lite.IVar('gn6w', {5, 6}) # abstract
- gn7w = l3lite.IVar('gn7w', {0, 4})
- gn8w = l3lite.DetIVar('gn8w', 1)
- gn9w = l3lite.DetIVar('gn9w', 3)
- gn10w = l3lite.IVar('gn10w', {0, 4})
- gn11w = l3lite.DetIVar('gn11w', 6)
- gn12w = l3lite.IVar('gn12w', {0, 4})
- gn13w = l3lite.DetIVar('gn13w', 5)
- gn14w = l3lite.DetIVar('gn14w', 7)
- gn_w = [gn0w, gn1w, gn2w, gn3w, gn4w, gn5w, gn6w, gn7w, gn8w, gn9w,
gn10w, gn11w, gn12w, gn13w, gn14w]
- ## Groups associated with group nodes
- gn_g = [l3lite.DetIVar('gn0_g', 0),
- l3lite.DetIVar('gn1_g', 0),
- l3lite.DetIVar('gn2_g', 0),
- l3lite.DetIVar('gn3_g', 0),
- l3lite.DetIVar('gn4_g', 0),
- l3lite.DetIVar('gn5_g', 1), l3lite.DetIVar('gn6_g', 1),
- l3lite.DetIVar('gn7_g', 2), l3lite.DetIVar('gn8_g', 2),
- l3lite.DetIVar('gn9_g', 3),
- l3lite.DetIVar('gn10_g', 4), l3lite.DetIVar('gn11_g', 4),
- l3lite.DetIVar('gn12_g', 5), l3lite.DetIVar('gn13_g', 5),
- l3lite.DetIVar('gn14_g', 6)]
- ## Group nodes associated with words
- w0gn = l3lite.IVar('w0gn', {2, 7, 10, 12}) # the
- w1gn = l3lite.Var('w1gn', set(), {1, 8}, 1, 2) # boy
- w2gn = l3lite.IVar('w2gn', {0, 5}) # read
- w3gn = l3lite.Var('w3gn', set(), {1, 9}, 1, 2) # us
- w4gn = l3lite.IVar('w4gn', {2, 7, 10, 12}) # the
- w5gn = l3lite.Var('w5gn', set(), {3, 6, 13}, 1, 2) # riot
- w6gn = l3lite.Var('w6gn', set(), {4, 6, 11}, 1, 2) # act
- w7gn = l3lite.DetIVar('w7gn', 14)
- w2gnodes = [w0gn, w1gn, w2gn, w3gn, w4gn, w5gn, w6gn, w7gn]
- # Union selection on gnodes for each snode
- constraints.append(l3lite.UnionSelection(l3lite.DetVar('one', {1}),
w1gn, gn_w))
- constraints.append(l3lite.UnionSelection(l3lite.DetVar('three', {3}),
w3gn, gn_w))
- ### Word (SNode) ->
- ## Concrete group nodes associated with words
- w0cgn = w0gn
- w1cgn = l3lite.DetIVar('w1cgn', 8)
- w2cgn = w2gn
- w3cgn = l3lite.DetIVar('w3cgn', 9)
- w4cgn = w4gn
- w5cgn = l3lite.IVar('w5cgn', {3, 13})
- w6cgn = l3lite.IVar('w6cgn', {4, 11})
- w7cgn = w7gn
- w2cgnodes = [w0cgn, w1cgn, w2cgn, w3cgn, w4cgn, w5cgn, w6cgn, w7cgn]
- ## w->m Merged concrete group nodes associated with words
- w1gnm = l3lite.Var('w1gnm', set(), {8}, 0, 1) # boy (merged
with sbd)
- w3gnm = l3lite.Var('w3gnm', set(), {9}, 0, 1) # us (merged
with sbd)
- w5gnm = l3lite.Var('w5gnm', set(), {13}, 0, 1) # riot (merged
with sth)
- w6gnm = l3lite.Var('w6gnm', set(), {11}, 0, 1) # act (merged
with sth)
- w2gnm_nodes = [l3lite.EMPTY, w1gnm, l3lite.EMPTY, w3gnm, l3lite.EMPTY,
w5gnm, w6gnm, l3lite.EMPTY]
- ## w->a Abstract group nodes associated with words
- w1ga = l3lite.Var('w1ga', set(), {1}, 0, 1)
- w3ga = l3lite.Var('w3ga', set(), {1}, 0, 1)
- w5ga = l3lite.Var('w5ga', set(), {6}, 0, 1)
- w6ga = l3lite.Var('w6ga', set(), {6}, 0, 1)
- w2ga_nodes = [l3lite.EMPTY, w1ga, l3lite.EMPTY, w3ga, l3lite.EMPTY,
w5ga, w6ga, l3lite.EMPTY]
- ## All word nodes
- nodes = l3lite.DetVar('nodes', {0, 1, 2, 3, 4, 5, 6, 7})
- ## Union selection on the gnodes of all words
- # Words 1 (boy), 3 (us), 5 (riot), 6 (act) can have abstract nodes
- constraints.extend(l3lite.Union([w1gn, w1cgn, w1ga]).constraints)
- constraints.extend(l3lite.Union([w3gn, w3cgn, w3ga]).constraints)
- constraints.extend(l3lite.Union([w5gn, w5cgn, w5ga]).constraints)
- constraints.extend(l3lite.Union([w6gn, w6cgn, w6ga]).constraints)
- # Union of all group nodes used
- gnodeU = l3lite.Var('gnodeU', set(),
- {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
14}, 7, 9)
- constraints.append(l3lite.UnionSelection(gnodeU, nodes, w2gnodes))
- ## Union selection on the gnodes of all groups
- constraints.append(l3lite.UnionSelection(gnodeU, groups, gdetvars))
- ## Union selection on the words associated with each group node
- constraints.append(l3lite.UnionSelection(nodes, gnodeU, gn_w))
- # Positions of gnodes in each group
- g0snode = l3lite.Var('g0snode', set(), {0, 1, 2, 3, 4, 5, 6}, 5, 5) #
read sbd the riot act
- g1snode = l3lite.Var('g1snode', set(), {2, 5, 6}, 2, 2) #
read sth
- g2snode = l3lite.Var('g2snode', set(), {0, 1, 4}, 2, 2) #
the boy
- g3snode = l3lite.DetIVar('g3snode', 3) #
us
- g4snode = l3lite.Var('g4snode', set(), {0, 4, 6}, 2, 2) #
the act
- g5snode = l3lite.Var('g5snode', set(), {0, 4, 5}, 2, 2) #
the riot
- g6snode = l3lite.DetIVar('g6snode', 7)
# .
- gsnodes = [g0snode, g1snode, g2snode, g3snode, g4snode, g5snode,
g6snode]
- # Positions of concrete gnodes in each group
- g0cw = l3lite.Var('g0cw', set(), {0, 1, 2, 3, 4, 5, 6}, 4, 4)
- g1cw = l3lite.DetVar('g1cw', {2})
- g2cw = g2snode
- g3cw = g3snode
- g4cw = g4snode
- g5cw = g5snode
- g6cw = g6snode
- g_cwords = [g0cw, g1cw, g2cw, g3cw, g4cw, g5cw, g6cw]
- # Positions of abstract gnodes in each group
- g0aw = l3lite.IVar('g0aw', {0, 1, 2, 3, 4, 5, 6})
- g1aw = l3lite.IVar('g1aw', {0, 1, 2, 3, 4, 5, 6})
- g_awords = [g0aw, g1aw,
- l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY]
- # Union constraints for group abstract and concrete gnodes
- constraints.extend(l3lite.Union([g0snode, g0cw, g0aw]).constraints)
- constraints.extend(l3lite.Union([g1snode, g1cw, g1aw]).constraints)
- ## Groups merged with groups
- g0m = l3lite.Var('g0m', set(), {2, 3}, 0, 1)
- g1m = l3lite.Var('g1m', set(), {4, 5}, 0, 1)
- ## Groups merged with groups, including the group itself
- g0gm = l3lite.Var('g0gm', {0}, {0, 2, 3}, 1, 2)
- g1gm = l3lite.Var('g1gm', {1}, {1, 4, 5}, 1, 2)
- g2gm = l3lite.DetVar('g2gm', {2})
- g3gm = l3lite.DetVar('g3gm', {3})
- g4gm = l3lite.DetVar('g4gm', {4})
- g5gm = l3lite.DetVar('g5gm', {5})
- g6gm = l3lite.DetVar('g6gm', {6})
- # Relation between g->m only and g->m + self
- constraints.extend(l3lite.Union([g0gm, g0m, l3lite.DetIVar('zero',
0)]).constraints)
- constraints.extend(l3lite.Union([g1gm, g1m, l3lite.DetIVar('one',
1)]).constraints)
- ## Abstract group node mergers (empty set possible if not selected)
- # a->m
- # Merge %sbd with boy or us
- gan1m = l3lite.Var('gan1m', set(), {8, 9}, 0, 1)
- # Merge %sth with riot or act
- gan6m = l3lite.Var('gan6m', set(), {11, 13}, 0, 1)
- gan_m = [l3lite.EMPTY, gan1m, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
- l3lite.EMPTY, gan6m, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
- l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY]
- # Merged gnodes must be among selected gnodes
- constraints.extend(l3lite.Inclusion([gan1m, gnodeU]).constraints)
- constraints.extend(l3lite.Inclusion([gan6m, gnodeU]).constraints)
- # Relate merged gnodes to merged groups; selection variables should
- # be the union of a->m variables for each group that has any
- # Group 0, abstract gnode 1
- constraints.append(l3lite.UnionSelection(g0m, gan1m, gn_g))
- # Group 1, abstract gnode 6
- constraints.append(l3lite.UnionSelection(g1m, gan6m, gn_g))
- ## m-> a; Abstract nodes associated with concrete gnodes
- gm8ga = l3lite.Var('gm8ga', set(), {1}, 0, 1)
- gm9ga = l3lite.Var('gm9ga', set(), {1}, 0, 1)
- gm11ga = l3lite.Var('gm11ga', set(), {6}, 0, 1)
- gm13ga = l3lite.Var('gm13ga', set(), {6}, 0, 1)
- gm_ga = [l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY,
- l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, gm8ga, gm9ga,
l3lite.EMPTY,
- gm11ga, l3lite.EMPTY, gm13ga,l3lite.EMPTY]
- ## a->w Words associated with abstract group nodes
- ga1w = l3lite.Var('ga1w', set(), {1, 3}, 0, 1)
- ga6w = l3lite.Var('ga6w', set(), {5, 6}, 0, 1)
- ga_w = [l3lite.EMPTY, ga1w, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY,
- ga6w, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY,
- l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY]
- ## m->w Words associated with merged group nodes
- gnm8w = l3lite.Var('gnm8w', set(), {1}, 0, 1)
- gnm9w = l3lite.Var('gnm9w', set(), {3}, 0, 1)
- gnm11w = l3lite.Var('gnm11w', set(), {6}, 0, 1)
- gnm13w = l3lite.Var('gnm13w', set(), {5}, 0, 1)
- gnm_w = [l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
l3lite.EMPTY,
- l3lite.EMPTY, l3lite.EMPTY, l3lite.EMPTY,
- gnm8w, gnm9w, l3lite.EMPTY, gnm11w, l3lite.EMPTY,
- gnm13w, l3lite.EMPTY]
- # Merging means the gnodes have the same sentence position
- # gn1w = gn8w | gn9w ; gn6w = gn11w | gn13w
- # Abstract nodes in used groups
- anodes = l3lite.Var('anodes', set(), {1, 6})
- constraints.append(l3lite.UnionSelection(anodes, groups,
[l3lite.DetVar('g0a', {1}),
-
l3lite.DetVar('g1a', {6}),
-
l3lite.EMPTY, l3lite.EMPTY,
-
l3lite.EMPTY, l3lite.EMPTY,
-
l3lite.EMPTY]))
- # Word nodes that involve a merging of two gnodes
- mwords = l3lite.Var('mwords', set(), {1, 3, 5, 6}, 0, 2)
- # Set of all concrete gnodes that are merged with abstract nodes
- merged_gnodes = l3lite.Var('merged_gnodes', set(), {0, 2, 3, 4, 5, 7,
8, 9, 10, 11, 12, 13, 14},
- 0, 2)
- ## UnionSelection constraints relating words, abstract nodes, and
merged gnodes
- # Set of merged nodes is the union of the merged nodes for the set of
selected abstract nodes
- constraints.append(l3lite.UnionSelection(merged_gnodes, anodes, gan_m))
- # Set of merged nodes is the union of the merged nodes of all merge
words
- constraints.append(l3lite.UnionSelection(merged_gnodes, mwords,
w2gnm_nodes))
- # Set of abstract nodes is the union of the abstract nodes of all
merge words
- constraints.append(l3lite.UnionSelection(anodes, mwords, w2ga_nodes))
- # Set of abstract nodes is the union of the abstract nodes of all
gnodes
- constraints.append(l3lite.UnionSelection(anodes, merged_gnodes, gm_ga))
- # Set of merge words is the union of the words associated with all
selected abstract nodes
- constraints.append(l3lite.UnionSelection(mwords, anodes, ga_w))
- # Set of merge words is the union of the words associated with all
merge group nodes
- constraints.append(l3lite.UnionSelection(mwords, merged_gnodes, gnm_w))
- ## Constraints on merged and abstract nodes
- # Set of merged nodes must be a subset of all used gnodes
- constraints.append(l3lite.Inclusion([merged_gnodes,
gnodeU]).constraints[0])
- # All words must have different abstract nodes
- constraints.extend(l3lite.Disjoint(w2ga_nodes).constraints)
- # All concrete nodes must nodes have different abstract nodes
- constraints.extend(l3lite.Disjoint(gm_ga).constraints)
- ## Trees under gnode heads
- g0tree = l3lite.Var('g0tree', set(), {0, 1, 2, 3, 4, 5, 6}, 5) # at
least 5 snodes
- g1tree = l3lite.Var('g1tree', set(), {0, 1, 2, 3, 4, 5, 6}, 2) # at
least 2 snodes
- g2tree = g2snode
- g3tree = g3snode
- g4tree = g4snode
- g5tree = g5snode
- g6tree = g6snode
- gtrees = [g0tree, g1tree, g2snode, g3snode, g4snode, g5snode, g6snode]
- # g1 tree is the union of g0snode and the tree under whatever group
- # merges with gnode 1 (sbd)
- constraints.append(l3lite.UnionSelection(g0tree, g0gm, gsnodes))
- constraints.append(l3lite.UnionSelection(g1tree, g1gm, gsnodes))
- # Relate gnode merger with group merger
- # If gnode 1 merges with gnode 8, then group 0 merges with group 2;
- # if gnode 1 merges with gnode 9, then group 0 merges with group 3
- # Select one or the other
- # UnionSelection(choice, IVar('chooser', {0, 1}), [choice1, choice2])
-## g0merge_sel = l3lite.IVar('g0m_sel', {0, 1, 2})
-## constraints.append(l3lite.UnionSelection(g0gm, g0merge_sel,
[l3lite.DetVar('g0g2', {0, 2}),
-##
l3lite.DetVar('g0g3', {0, 3}),
-##
l3lite.DetVar('g0_', {0})]))
-## constraints.append(l3lite.UnionSelection(gan1m, g0merge_sel,
-## [l3lite.DetVar('gan1g8',
{8}),
-## l3lite.DetVar('gan1g9',
{9}),
-## l3lite.EMPTY]))
-## g1merge_sel = l3lite.IVar('g1m_sel', {0, 1, 2})
-## constraints.append(l3lite.UnionSelection(g1gm, g1merge_sel,
[l3lite.DetVar('g1g4', {1, 4}),
-##
l3lite.DetVar('g1g5', {1, 5}),
-##
l3lite.DetVar('g1_', {1})]))
-## constraints.append(l3lite.UnionSelection(gan6m, g1merge_sel,
-## [l3lite.DetVar('gan6g11',
{11}),
-## l3lite.DetVar('gan6g13',
{13}),
-## l3lite.EMPTY]))
- ### Order
- # Position pair constraints
- gposcons = l3lite.Var('gposcons', set(),
- {(0, 1), (1, 2), (2, 3), (3, 4), (0, 2), (1, 3),
(2, 4), (0, 3), (1, 4),
- (5, 6), (7, 8), (10, 11), (12, 13)})
- # Union selection on position pair constraints
- gp0 = l3lite.DetVar('gp0', {(0, 1), (1, 2), (2, 3), (3, 4), (0, 2),
(1, 3), (2, 4), (0, 3), (1, 4)})
- gp1 = l3lite.DetVar('gp1', {(5, 6)})
- gp2 = l3lite.DetVar('gp2', {(7, 8)})
- gp3 = l3lite.DetVar('gp3', set())
- gp4 = l3lite.DetVar('gp4', {(10, 11)})
- gp5 = l3lite.DetVar('gp5', {(12, 13)})
- gp6 = l3lite.DetVar('gp6', set())
- gpos = [gp0, gp1, gp2, gp3, gp4, gp5, gp6]
- constraints.append(l3lite.PrecedenceSelection(gposcons,
- [gn0w, gn1w, gn2w, gn3w,
gn4w, gn5w,
- gn6w, gn7w, gn8w, gn9w,
gn10w, gn11w, gn12w, gn13w, gn14w]))
- # Union selection on position pair constraints
- constraints.append(l3lite.UnionSelection(gposcons, groups, gpos))
- # Complex union selection on positions of all concrete nodes in each
selected group
- constraints.append(l3lite.ComplexUnionSelection(selvar=groups,
- selvars=g_cwords,
- seqvars=w2cgnodes,
- mainvars=gc_detvars))
- # Complex union selection on positions of all abstract gnodes in each
selected group
- constraints.append(l3lite.ComplexUnionSelection(selvar=groups,
- selvars=g_awords,
- seqvars=w2ga_nodes,
- mainvars=ga_detvars))
- ### Projectivity
- # Complex set convexity
- constraints.append(l3lite.ComplexSetConvexity(groups, gtrees))
- return constraints
-
-def run_constraints(constraints, times=10):
- for t in range(times):
- print("\nITERATION {}".format(t))
- for c in constraints:
- state, changed = c.run()
- if state == 0:
- print('FAILED: constraint {}'.format(c))
- return
- if changed:
- print("Running {}".format(c))
- print(" Changed:
{}".format(list(changed)[0].pretty_string()))
-
-if __name__ == "__main__":
- print('HIIKTUU, version {}\n'.format(__version__))
-
=======================================
--- /l3lite/cs.py Fri May 2 20:10:49 2014 UTC
+++ /l3lite/cs.py Fri May 2 22:53:47 2014 UTC
@@ -42,11 +42,12 @@
skipped = 4
def __init__(self, constraints, dstore,
- description=''):
+ description='', verbosity=0):
self.constraints = constraints
self.dstore = dstore
# Used in solver's printname
self.description = description
+ self.verbosity=verbosity
self.entailed = []
self.failed = []
self.status = Solver.running
@@ -63,8 +64,9 @@
return self.fixed_point(result)
def fixed_point(self, awaken, verbosity=0):
- s = "# constraints to awaken: {}, # variables to determine: {}|{}"
- print(s.format(len(awaken), len(self.dstore.ess_undet),
len(self.dstore.undetermined)))
+ if verbosity > 1:
+ s = "# constraints to awaken: {}, # variables to determine: {}|
{}"
+ print(s.format(len(awaken), len(self.dstore.ess_undet),
len(self.dstore.undetermined)))
if self.dstore.is_determined():
# All essential variables are determined
self.status = Solver.succeeded
@@ -84,12 +86,14 @@
def run(self, verbosity=0, tracevar=[]):
"""Run the constraints until CS fails or a fixed point is
reached."""
- s = "Running {} with {}|{} undetermined variables, {} constraints"
- print(s.format(self, len(self.dstore.ess_undet),
len(self.dstore.undetermined), len(self.constraints)))
+ if verbosity:
+ s = "Running {} with {}|{} undetermined variables, {}
constraints"
+ print(s.format(self, len(self.dstore.ess_undet),
len(self.dstore.undetermined), len(self.constraints)))
awaken = set(self.constraints)
it = 0
while not self.exit(awaken):
- print("Running iteration {}".format(it))
+ if verbosity > 1:
+ print("Running iteration {}".format(it))
awaken = self.run_constraints(awaken, verbosity=verbosity,
tracevar=tracevar)
it += 1
@@ -109,7 +113,8 @@
awaken.remove(constraint)
if state == Constraint.failed:
- print("FAILED {}".format(constraint))
+ if verbosity:
+ print("FAILED {}".format(constraint))
return Constraint.failed
# # constraint fails; remove it from the entailed or awaken
lists if it's there
# if constraint in self.entailed:
@@ -153,7 +158,7 @@
print('Adding {} constraints for changed variable
{}'.format(len(update_cons), tracevar))
awaken.update(update_cons)
# print('update cons {}'.format(update_cons))
- if verbosity:
+ if verbosity > 1:
print('# changed vars {}'.format(len(all_changed)))
return awaken
=======================================
--- /l3lite/language.py Fri May 2 20:10:49 2014 UTC
+++ /l3lite/language.py Fri May 2 22:53:47 2014 UTC
@@ -214,6 +214,7 @@
try:
language = Language.read(path)
languages.append(language)
+ print("Loading language {}".format(language))
except IOError:
print("That language doesn't seem to exist.")
return
=======================================
--- /l3lite/sentence.py Fri May 2 20:10:49 2014 UTC
+++ /l3lite/sentence.py Fri May 2 22:53:47 2014 UTC
@@ -57,7 +57,8 @@
def __init__(self, raw='', language=None,
tokens=None, analyses=None,
- nodes=None, groups=None, target=None):
+ nodes=None, groups=None, target=None,
+ verbosity=0):
self.set_id()
# A string representing the raw sentence
self.raw = raw
@@ -69,6 +70,8 @@
self.nodes = nodes or []
# A list of candidate groups (realized as GInst objects) found
during lexicalization
self.groups = groups or []
+ # Control messages
+ self.verbosity = verbosity
# GNodes in GInsts
self.gnodes = []
# A list of constraints to run
@@ -79,9 +82,11 @@
self.variables = {}
# Solver to find solutions
self.solver = Solver(self.constraints, self.dstore,
- description='group selection')
+ description='for group selection',
verbosity=verbosity)
# Solutions found during parsing
self.solutions = []
+ if verbosity:
+ print("Created Sentence object {}".format(self))
def set_id(self):
self.id = Sentence.id
@@ -94,40 +99,55 @@
else:
return '|| {} sentence {} ||'.format(self.language,
self.id)
- def do(self):
+ def do(self, verbosity=0):
"""If target language, translate. If not, parse."""
- if self.initialize():
- if self.solve():
+# if verbosity:
+ if self.target:
+ print("Attempting to translate sentence {}".format(self))
+ else:
+ print("Attempting to parse sentence {}".format(self))
+ if self.initialize(verbosity=verbosity):
+ if self.solve(verbosity=verbosity):
if self.target:
for solution in self.solutions:
- solution.translate()
+ solution.translate(verbosity=verbosity)
+ print("Translations found:")
for translation in solution.translations:
translation.display()
- def initialize(self):
+ def initialize(self, verbosity=0):
"""Things to do before running constraint satisfaction."""
- self.tokenize()
- self.lexicalize()
+ if verbosity:
+ print("Initializing {}".format(self))
+ self.tokenize(verbosity=verbosity)
+ self.lexicalize(verbosity=verbosity)
if not self.groups:
print("No groups found for {}".format(self))
return False
else:
- self.create_variables()
- self.create_constraints()
+ self.create_variables(verbosity=verbosity)
+ self.create_constraints(verbosity=verbosity)
return True
- def solve(self):
+ def solve(self, verbosity=0):
"""Run constraints and create a single solution."""
- if self.run():
- self.create_solution()
+ if verbosity:
+ print("Attempting to find solutions for {}".format(self))
+ if self.run(verbosity=verbosity):
+ self.create_solution(verbosity=verbosity)
+ if verbosity:
+ print("Found solution {}".format(self.solutions[0]))
return True
else:
- print("No solution found")
+ if verbosity:
+ print("No solution found")
return False
- def tokenize(self):
+ def tokenize(self, verbosity=0):
"""Segment the sentence string into tokens, analyze them
morphologically,
and create a SNode object for each."""
+ if verbosity:
+ print("Tokenizing {}".format(self))
if not self.nodes:
# (Otherwise it's already done.)
# Split at spaces by default (later allow for dedicated
language-specific tokenizers).
@@ -159,8 +179,10 @@
self.nodes.append(SNode(token, index, formdict,
self))
index += 1
- def lexicalize(self):
+ def lexicalize(self, verbosity=0):
"""Find and instantiate all groups that are compatible with the
tokens in the sentence."""
+ if verbosity:
+ print("Lexicalizing {}".format(self))
if not self.nodes:
print("Tokenization must precede lexicalization.")
return
@@ -191,14 +213,16 @@
groups = []
for head_i, group in candidates:
# Matching snodes, along with root and unified features if any
- print("Matching group {}".format(group))
+ if verbosity > 1:
+ print("Matching group {}".format(group))
snodes = group.match_nodes(self.nodes, head_i)
if not snodes:
# This group is out
- print("Failed to match")
+ if verbosity > 1:
+ print("Failed to match")
continue
- print('Group {} matches snodes {}'.format(group, snodes))
-# print('Found indices {} for group {}, head index
{}'.format(indices, group, head_i))
+ if verbosity > 1:
+ print('Group {} matches snodes {}'.format(group, snodes))
groups.append((head_i, snodes, group))
# Create a GInst object and GNodes for each surviving group
self.groups = [GInst(group, self, head_i, snodes, index) for
index, (head_i, snodes, group) in enumerate(groups)]
@@ -233,7 +257,7 @@
self.variables[name] = Var(name, lower, upper, lower_card,
upper_card,
essential=ess, rootDS=self.dstore)
- def create_variables(self):
+ def create_variables(self, verbosity=0):
# All abstract (category) and instance (word or lexeme) gnodes
catnodes = set()
instnodes = set()
@@ -260,7 +284,6 @@
# covered snodes
covered_snodes = {sn.index for sn in self.nodes if sn.gnodes}
self.variables['snodes'] = DetVar('snodes', covered_snodes)
-# set(range(len(self.nodes))))
# Category (abstract) nodes
self.svar('catgnodes', set(), catnodes)
# Instance gnodes that are merged with catnodes
@@ -280,7 +303,9 @@
for gnode in self.gnodes:
gnode.create_variables()
- def create_constraints(self):
+ def create_constraints(self, verbosity=0):
+ if verbosity:
+ print("Creating constraints for {}".format(self))
# Relation among abstract, concrete, and all gnodes for each snode
for snode in self.nodes:
if snode.gnodes:
@@ -404,14 +429,15 @@
self.constraints.append(ComplexSetConvexity(self.variables['groups'],
[g.variables['tree']
for g in self.groups]))
- def run(self):
+ def run(self, verbosity=0):
"""Run constraint satisfaction on constraints, for now without
search if
no solution is found."""
- self.solver.run()
- print("Solver status after run: {}".format(self.solver.status))
+ self.solver.run(verbosity=verbosity)
+ if verbosity:
+ print("Solver status after run: {}".format(self.solver.status))
return self.solver.status
- def create_solution(self, dstore=None):
+ def create_solution(self, dstore=None, verbosity=0):
"""Assuming essential variables are determined in a domain store,
make a Solution object."""
dstore = dstore or self.dstore
# Get the indices of the selected groups
@@ -425,10 +451,6 @@
self.solutions.append(Solution(self, ginsts, s2gnodes,
len(self.solutions)))
-# def make_solution(self, ginsts, s2gnodes):
-# self.solutions.append(Solution(self, ginsts, s2gnodes,
-# len(self.solutions)))
-
class SNode:
"""Sentence token and its associated analyses and variables."""
@@ -467,7 +489,7 @@
self.variables[key] = Var(name, lower, upper, lower_card,
upper_card,
rootDS=self.sentence.dstore,
essential=ess)
- def create_variables(self):
+ def create_variables(self, verbosity=0):
if not self.gnodes:
# Nothing matched this snode; all variables empty
self.variables['gnodes'] = EMPTY
@@ -597,7 +619,7 @@
rootDS=self.sentence.dstore,
essential=ess)
- def create_variables(self):
+ def create_variables(self, verbosity=0):
ngroups = len(self.sentence.groups)
nsnodes = len(self.sentence.nodes)
# GNode indices for this GInst (determined)
@@ -650,7 +672,7 @@
# at least as long as the number of self's nodes
set(), set(range(nsnodes)), self.ngnodes, nsnodes)
- def set_translations(self):
+ def set_translations(self, verbosity=0):
"""Find the translations of the group in the target language."""
translations = self.group.get_translations(self.target.abbrev,
False)
# If alignments are missing, add default alignment
@@ -732,7 +754,7 @@
rootDS=self.sentence.dstore,
essential=ess)
- def create_variables(self):
+ def create_variables(self, verbosity=0):
nsnodes = len(self.sentence.nodes)
# SNode index for this GNode
self.ivar('snodes', "gn{}->w".format(self.sent_index),
set(self.snode_indices))
@@ -771,7 +793,7 @@
self.sentence = ginst.sentence
self.index = index
- def generate(self):
+ def generate(self, verbosity=0):
"""Generate forms for the TNode."""
if self.features:
return self.sentence.target.generate(self.token, self.features)
@@ -803,25 +825,29 @@
def __repr__(self):
return "|< {} >|({})".format(self.sentence.raw, self.index)
- def translate(self):
+ def translate(self, verbosity=0):
"""Do everything you need to create the translation."""
- self.merge_nodes()
+ self.merge_nodes(verbosity=verbosity)
for ginst in self.ginsts:
- ginst.set_translations()
- self.make_translations()
+ ginst.set_translations(verbosity=verbosity)
+ self.make_translations(verbosity=verbosity)
def make_translations(self, verbosity=0):
"""Combine GInsts for each translation in translation products, and
separate gnodes into a dict for each translation."""
+ if verbosity:
+ print("Making translations for {}".format(self))
translations = itertools.product(*[g.translations for g in
self.ginsts])
for index, translation in enumerate(translations):
- t = Translation(self, translation, index)
- t.initialize()
- t.realize()
+ t = Translation(self, translation, index, verbosity=verbosity)
+ t.initialize(verbosity=verbosity)
+ t.realize(verbosity=verbosity)
self.translations.append(t)
def merge_nodes(self, verbosity=0):
"""Merge the source features of cat and inst GNodes associated
with each SNode."""
+ if verbosity:
+ print("Merging target nodes for {}".format(self))
for snode, gn_indices in zip(self.sentence.nodes, self.s2gnodes):
# gn_indices is either one or two ints indexing gnodes in
self.gnodes
gnodes = [self.sentence.gnodes[index] for index in gn_indices]
@@ -838,61 +864,15 @@
features = Features.unify_all(features)
self.snodes.append((gnodes, features))
-## def build_translations(self):
-## for translation in self.t:
-## print('TRANSLATION {}'.format(trans_index))
-## trans_nodes = []
-## tginsts, tgnodes, trans_index = translation.groups_tnodes,
translation.gnode_dict, translation.index
-## # Figure out the target forms for each snode
-## for snode, (gnodes, features) in zip(self.sentence.nodes,
self.snodes):
-## if len(gnodes) > 1:
-## # There are two gnodes for this snode; only the
concrete node
-## # can have translations
-## gn0 = tgnodes[gnodes[0]]
-## gn1 = tgnodes[gnodes[1]]
-## tgroups, tokens, targ_feats, agrs, t_index =
zip(gn0, gn1)
-## print('snode {}, gnodes {}'.format(snode, gnodes))
-## token = False
-## i = 0
-## # Find the token that's not a cat
-## while not token:
-## t = tokens[i]
-## if not Entry.is_cat(t):
-## token = t
-## i += 1
-## targ_feats = Features.unify_all(targ_feats)
-## # Merge the agreements
-## agrs = Solution.combine_agrs(agrs)
-## t_index = [(tgroups[0], gn0[-1]), (tgroups[1],
gn1[-1])]
-## print(' tokens {}, targ_feats {}, agrs {}, t_index
{}'.format(tokens, targ_feats, agrs, t_index))
-## else:
-## gnode = gnodes[0]
-## print('snode {}, gnode {}'.format(snode, gnode))
-## tgroup, token, targ_feats, agrs, t_index =
tgnodes[gnode]
-## t_index = [(tgroup, t_index)]
-## print(' tokens {}, targ_feats {}, agrs {}, t_index
{}'.format(token, targ_feats, agrs, t_index))
-## features.agree(targ_feats, agrs)
-## gen = self.sentence.target.generate(token, targ_feats)
-## print(" Generating {}/{}: {}".format(token, targ_feats,
gen))
-## trans_nodes.append((gen, t_index))
-## # Add TNode elements
-## tgnode_elements = []
-## for ginst, tgnodes in tginsts:
-## if tgnodes:
-## for tgnode in tgnodes:
-## forms = tgnode.generate()
-## index = [(ginst, tgnode.index)]
-## trans_nodes.append((forms, index))
-## self.translations.append(trans_nodes)
-
class Translation:
"""Representation of a single translation for an input sentence.
Multiple translations are possible with a single Solution."""
- def __init__(self, solution, attribs, index):
+ def __init__(self, solution, attribs, index, verbosity=0):
self.solution = solution
self.index = index
self.sentence = solution.sentence
+ self.verbosity = verbosity
# Create GNode dict and list of target group, gnodes and tnodes
# from attributes
self.gnode_dict = {}
@@ -915,7 +895,8 @@
self.constraints = []
# Translation needs a solver to figure out positions of words
self.solver = Solver(self.constraints, self.dstore,
- description='target realization')
+ description='for target realization',
+ verbosity=verbosity)
# Final output
self.output = None
@@ -923,7 +904,7 @@
return "{}[{}] ->".format(self.solution, self.index)
def display(self):
- print("Translation {}: {}".format(self, self.out_string()))
+ print("{} {}".format(self, self.out_string()))
def out_string(self):
'''Convert output to a string for pretty printing.'''
@@ -935,13 +916,15 @@
l.append('|'.join(word_list))
return ' '.join(l)
- def initialize(self):
+ def initialize(self, verbosity=0):
"""Set up everything needed to run the constraints and generate
the translation."""
- self.build()
- self.set_chunks()
- self.make_order_pairs()
- self.create_variables()
- self.create_constraints()
+ if verbosity:
+ print("Initializing translation {}".format(self))
+ self.build(verbosity=verbosity)
+ self.set_chunks(verbosity=verbosity)
+ self.make_order_pairs(verbosity=verbosity)
+ self.create_variables(verbosity=verbosity)
+ self.create_constraints(verbosity=verbosity)
def build(self, verbosity=0):
"""Unify translation features for merged nodes, map agr features
from source to target,
@@ -992,7 +975,7 @@
features.agree(targ_feats, agrs)
# Generate target forms for this SNode
gen = self.sentence.target.generate(token, targ_feats)
- if verbosity:
+ if verbosity > 1:
print(" Generating node form {}/{}: {}".format(token,
targ_feats, gen))
self.nodes.append((gen, t_indices))
# Add TNode elements
@@ -1006,7 +989,8 @@
index = [(ginst, tnode.index)]
self.nodes.append((forms, index))
- def set_chunks(self):
+ def set_chunks(self, verbosity=0):
+ """Find output chunks: a list of sets of snode indices."""
chunk_attribs = []
for index, (tokens, constraints) in enumerate(self.nodes):
# Is this an uncovered node/token
@@ -1073,13 +1057,15 @@
for next_index in next_indices:
self.order_pairs.append([index, next_index])
- def create_variables(self):
+ def create_variables(self, verbosity=0):
"""Create an IVar for each translation node."""
nnodes = len(self.nodes)
self.variables = [IVar("o{}".format(i), set(range(nnodes)),
rootDS=self.dstore, essential=True) for i in range(nnodes)]
- def create_constraints(self):
+ def create_constraints(self, verbosity=0):
"""Make order and disjunction constraints."""
+ if verbosity:
+ print("Creating constraints for {}".format(self))
for first, second in self.order_pairs:
self.constraints.append(SetPrecedence([self.variables[first],
self.variables[second]]))
self.constraints.extend(Disjoint(self.variables).constraints)
@@ -1087,9 +1073,13 @@
def realize(self, verbosity=0):
"""Run constraint satisfaction on the order and disjunction
constraints,
and convert variable values to sentence positions."""
+ if verbosity:
+ print("Realizing {}".format(self))
# Run constraint satisfaction on the constraints until all
variables are
# determined or nothing happes to the constraints
self.solver.run(verbosity=verbosity)
+ if verbosity:
+ print("Solver status after run: {}".format(self.solver.status))
if self.solver.status == Solver.succeeded:
# No more awake constraints and no undetermined variables; get
positions from
# variable values
=======================================
--- /l3lite/variable.py Thu May 1 06:39:44 2014 UTC
+++ /l3lite/variable.py Fri May 2 22:53:47 2014 UTC
@@ -53,16 +53,18 @@
def __repr__(self):
return '@ {}/{}'.format(
self.name, self.level)
- def is_determined(self, essential=True):
+ def is_determined(self, essential=True, verbosity=0):
"""Are all variables in dstore determined that need to be
determined?"""
if essential:
if self.ess_undet:
- print('{} has {} undetermined variables'.format(self,
len(self.ess_undet)))
+ if verbosity:
+ print('{} has {} undetermined variables'.format(self,
len(self.ess_undet)))
return False
else:
return True
elif self.undetermined:
- print('{} has {} undetermined variables'.format(self,
len(self.undetermined)))
+ if verbosity:
+ print('{} has {} undetermined variables'.format(self,
len(self.undetermined)))
return False
return True