natural language toolkit combinatory categorial grammar c 20012023 nltk project graeme gange ggangecsse unimelb edu au url https www nltk org for license information see license txt ccg combinators abstract class for representing a binary combinator merely defines functions for checking if the function and argument are able to be combined and what the resulting category is note that as no assumptions are made as to direction the unrestricted combinators can perform all backward forward and crossed variations of the combinators these restrictions must be added in the rule class wrapper for the undirected binary combinator it takes left and right categories and decides which is to be the function and which the argument it then decides whether or not they can be combined class representing combinators where the primary functor is on the left takes an undirected combinator and a predicate which adds constraints restricting the cases in which it may apply the backward equivalent of the forwardcombinator class class representing function application implements rules of the form xy y x and the corresponding backwards application rule predicates for function application ensures the left functor takes an argument on the right ensures the right functor takes an argument on the left application combinator instances functional composition harmonic combinator implements rules of the form xy yz xz b and the corresponding backwards and crossed variations can only combine two functions and both functions must allow composition predicates for restricting application of straight composition predicates for crossed composition the functors must be crossed inwards permuting combinators must be allowed the resulting argument category is restricted to be primitive straight composition combinators backward crossed composition def cancombineself function argument if function isprimitive or argument isprimitive return false these could potentially be moved to the predicates as the constraints may not be general to all languages if function res isprimitive return false if not function arg isprimitive return false if not function dir cancompose and argument dir cancompose return false return function res arg argument res and function arg argument arg def combineself function argument if self cancombinefunction argument yield functionalcategory function res res argument arg argument dir def strself return s predicate for forward substitution def forwardsconstraintleft right if not bothforwardleft right return false return left res dir isforward and left arg isprimitive predicate for backward crossed substitution def backwardsxconstraintleft right if not left dir cancross and right dir cancross return false if not bothforwardleft right return false return right res dir isbackward and right arg isprimitive instances of substitution combinators forwardsubstitution forwardcombinatorundirectedsubstitution forwardsconstraint backwardsx backwardcombinatorundirectedsubstitution backwardsxconstraint x retrieves the leftmost functional category ie nnsnp nn def innermostfunctioncateg while categ res isfunction categ categ res return categ class undirectedtyperaiseundirectedbinarycombinator def cancombineself function arg the argument must be a function the restriction that arg res must be a function merely reduces redundant typeraising if arg res is primitive we have x yx t yyx yx y which is equivalent to x yx y if not arg isfunction and arg res isfunction return false arg innermostfunctionarg left argcateg are undefined subs left canunifyargcateg arg if subs is not none return true return false def combineself function arg if not function isprimitive and arg isfunction and arg res isfunction return typeraising matches only the innermost application arg innermostfunctionarg subs function canunifyarg arg if subs is not none xcat arg res substitutesubs yield functionalcategory xcat functionalcategoryxcat function arg dir arg dir def strself return t predicates for typeraising the direction of the innermost category must be towards the primary functor the restriction that the variable must be primitive is not common to all versions of ccgs some s have other restrictions def forwardtconstraintleft right arg innermostfunctionright return arg dir isbackward and arg res isprimitive def backwardtconstraintleft right arg innermostfunctionleft return arg dir isforward and arg res isprimitive instances of typeraising combinators forwardt forwardcombinatorundirectedtyperaise forwardtconstraint backwardt backwardcombinatorundirectedtyperaise backwardtconstraint natural language toolkit combinatory categorial grammar c 2001 2023 nltk project graeme gange ggange csse unimelb edu au url https www nltk org for license information see license txt ccg combinators abstract class for representing a binary combinator merely defines functions for checking if the function and argument are able to be combined and what the resulting category is note that as no assumptions are made as to direction the unrestricted combinators can perform all backward forward and crossed variations of the combinators these restrictions must be added in the rule class wrapper for the undirected binary combinator it takes left and right categories and decides which is to be the function and which the argument it then decides whether or not they can be combined class representing combinators where the primary functor is on the left takes an undirected combinator and a predicate which adds constraints restricting the cases in which it may apply the backward equivalent of the forwardcombinator class class representing function application implements rules of the form x y y x and the corresponding backwards application rule predicates for function application ensures the left functor takes an argument on the right ensures the right functor takes an argument on the left application combinator instances functional composition harmonic combinator implements rules of the form x y y z x z b and the corresponding backwards and crossed variations can only combine two functions and both functions must allow composition predicates for restricting application of straight composition predicates for crossed composition the functors must be crossed inwards permuting combinators must be allowed the resulting argument category is restricted to be primitive straight composition combinators backward crossed composition substitution permutation combinator implements rules of the form y z x y z x z sx and other variations these could potentially be moved to the predicates as the constraints may not be general to all languages predicate for forward substitution predicate for backward crossed substitution instances of substitution combinators retrieves the left most functional category ie n n s np n n undirected combinator for type raising the argument must be a function the restriction that arg res must be a function merely reduces redundant type raising if arg res is primitive we have x y x t y y x y x y which is equivalent to x y x y left arg_categ are undefined type raising matches only the innermost application predicates for type raising the direction of the innermost category must be towards the primary functor the restriction that the variable must be primitive is not common to all versions of ccgs some s have other restrictions instances of type raising combinators
from abc import ABCMeta, abstractmethod

from nltk.ccg.api import FunctionalCategory


class UndirectedBinaryCombinator(metaclass=ABCMeta):
    

    @abstractmethod
    def can_combine(self, function, argument):
        pass

    @abstractmethod
    def combine(self, function, argument):
        pass


class DirectedBinaryCombinator(metaclass=ABCMeta):
    

    @abstractmethod
    def can_combine(self, left, right):
        pass

    @abstractmethod
    def combine(self, left, right):
        pass


class ForwardCombinator(DirectedBinaryCombinator):
    

    def __init__(self, combinator, predicate, suffix=""):
        self._combinator = combinator
        self._predicate = predicate
        self._suffix = suffix

    def can_combine(self, left, right):
        return self._combinator.can_combine(left, right) and self._predicate(
            left, right
        )

    def combine(self, left, right):
        yield from self._combinator.combine(left, right)

    def __str__(self):
        return f">{self._combinator}{self._suffix}"


class BackwardCombinator(DirectedBinaryCombinator):
    

    def __init__(self, combinator, predicate, suffix=""):
        self._combinator = combinator
        self._predicate = predicate
        self._suffix = suffix

    def can_combine(self, left, right):
        return self._combinator.can_combine(right, left) and self._predicate(
            left, right
        )

    def combine(self, left, right):
        yield from self._combinator.combine(right, left)

    def __str__(self):
        return f"<{self._combinator}{self._suffix}"


class UndirectedFunctionApplication(UndirectedBinaryCombinator):
    

    def can_combine(self, function, argument):
        if not function.is_function():
            return False

        return not function.arg().can_unify(argument) is None

    def combine(self, function, argument):
        if not function.is_function():
            return

        subs = function.arg().can_unify(argument)
        if subs is None:
            return

        yield function.res().substitute(subs)

    def __str__(self):
        return ""





def forwardOnly(left, right):
    return left.dir().is_forward()



def backwardOnly(left, right):
    return right.dir().is_backward()



ForwardApplication = ForwardCombinator(UndirectedFunctionApplication(), forwardOnly)
BackwardApplication = BackwardCombinator(UndirectedFunctionApplication(), backwardOnly)


class UndirectedComposition(UndirectedBinaryCombinator):
    

    def can_combine(self, function, argument):
        
        
        if not (function.is_function() and argument.is_function()):
            return False
        if function.dir().can_compose() and argument.dir().can_compose():
            return not function.arg().can_unify(argument.res()) is None
        return False

    def combine(self, function, argument):
        if not (function.is_function() and argument.is_function()):
            return
        if function.dir().can_compose() and argument.dir().can_compose():
            subs = function.arg().can_unify(argument.res())
            if subs is not None:
                yield FunctionalCategory(
                    function.res().substitute(subs),
                    argument.arg().substitute(subs),
                    argument.dir(),
                )

    def __str__(self):
        return "B"



def bothForward(left, right):
    return left.dir().is_forward() and right.dir().is_forward()


def bothBackward(left, right):
    return left.dir().is_backward() and right.dir().is_backward()



def crossedDirs(left, right):
    return left.dir().is_forward() and right.dir().is_backward()


def backwardBxConstraint(left, right):
    
    if not crossedDirs(left, right):
        return False
    
    if not left.dir().can_cross() and right.dir().can_cross():
        return False
    
    return left.arg().is_primitive()



ForwardComposition = ForwardCombinator(UndirectedComposition(), forwardOnly)
BackwardComposition = BackwardCombinator(UndirectedComposition(), backwardOnly)


BackwardBx = BackwardCombinator(
    UndirectedComposition(), backwardBxConstraint, suffix="x"
)


class UndirectedSubstitution(UndirectedBinaryCombinator):
    r

    def can_combine(self, function, argument):
        if function.is_primitive() or argument.is_primitive():
            return False

        
        
        if function.res().is_primitive():
            return False
        if not function.arg().is_primitive():
            return False

        if not (function.dir().can_compose() and argument.dir().can_compose()):
            return False
        return (function.res().arg() == argument.res()) and (
            function.arg() == argument.arg()
        )

    def combine(self, function, argument):
        if self.can_combine(function, argument):
            yield FunctionalCategory(
                function.res().res(), argument.arg(), argument.dir()
            )

    def __str__(self):
        return "S"



def forwardSConstraint(left, right):
    if not bothForward(left, right):
        return False
    return left.res().dir().is_forward() and left.arg().is_primitive()



def backwardSxConstraint(left, right):
    if not left.dir().can_cross() and right.dir().can_cross():
        return False
    if not bothForward(left, right):
        return False
    return right.res().dir().is_backward() and right.arg().is_primitive()



ForwardSubstitution = ForwardCombinator(UndirectedSubstitution(), forwardSConstraint)
BackwardSx = BackwardCombinator(UndirectedSubstitution(), backwardSxConstraint, "x")




def innermostFunction(categ):
    while categ.res().is_function():
        categ = categ.res()
    return categ


class UndirectedTypeRaise(UndirectedBinaryCombinator):
    

    def can_combine(self, function, arg):
        
        
        
        
        
        
        
        if not (arg.is_function() and arg.res().is_function()):
            return False

        arg = innermostFunction(arg)

        
        subs = left.can_unify(arg_categ.arg())
        if subs is not None:
            return True
        return False

    def combine(self, function, arg):
        if not (
            function.is_primitive() and arg.is_function() and arg.res().is_function()
        ):
            return

        
        arg = innermostFunction(arg)

        subs = function.can_unify(arg.arg())
        if subs is not None:
            xcat = arg.res().substitute(subs)
            yield FunctionalCategory(
                xcat, FunctionalCategory(xcat, function, arg.dir()), -(arg.dir())
            )

    def __str__(self):
        return "T"







def forwardTConstraint(left, right):
    arg = innermostFunction(right)
    return arg.dir().is_backward() and arg.res().is_primitive()


def backwardTConstraint(left, right):
    arg = innermostFunction(left)
    return arg.dir().is_forward() and arg.res().is_primitive()



ForwardT = ForwardCombinator(UndirectedTypeRaise(), forwardTConstraint)
BackwardT = BackwardCombinator(UndirectedTypeRaise(), backwardTConstraint)