Hierarchies in the matrix code

I’m working on fixing a bug in the matrix code that makes 1 non-inflecting rule per case-assigning adposition in a grammar. The goal is to have it create one non-inflecting rule that assigns a case value that is underspecified between the possible values an adposition can assign, rather than one per adposition.

In order to do this I need to create a new supertype in the case hierarchy. However, the hierarchies are not exposed to the method that is adding these rules (here’s an excerpt from customize.py, lines 501-526 (with some taken out for conciseness)):

    # The following might modify hierarchies in some way, so it's best
    # to customize those components and only have them contribute their
    # information to lexical rules when we customize inflection.
    lexical_items.customize_lexicon(
        mylang, ch, lexicon, trigger, hierarchies, rules)
    ...
    case.customize_case(mylang, ch, hierarchies)

    # after all structures have been customized, customize inflection,
    # but provide the methods the components above have for their own
    # contributions to the lexical rules

    ...

    add_lexrules_methods = [case.add_lexrules,
                            argument_optionality.add_lexrules,
                            valence_change.add_lexrules,
                            direct_inverse.add_lexrules,
                            wh_ques.add_lexrules]
    to_cfv = morphotactics.customize_inflection(ch, add_lexrules_methods,
                                                mylang, irules, lrules, lexicon)

So it looks like in the top portion, it does a bunch of customizing for a number of “components," and it’s here that hierarchies can be updated. But then after this when lexical rules are added, the hierarchies don’t get passed on. The method I’m working with is one of these add_lexrules methods. There are probably a number of ways to go about handling this, but I thought I’d see if anyone had any immediate, obvious suggestions before going into a rabbit hole.

I think I’d need more details about input (e.g. in the form of choices) and (the desired) output (in the form of TDL), but I can say a couple things:

  1. This is related to this, right? Adposition typedef merging And did you end up checking whether simply restoring the old version of the code fixes things (without breaking other things)? Just curious.

  2. Maybe take a look at the method called write_rules in morphotactics.py. It gives a few examples of library-specific behavior wrt lexical rules. For example, I added a method called write_interrogative_rules (which is called by write_rules) which adds supertypes. The supertypes themselves can really live anywhere; I believe the hierarchies variable has a different function (although I am not 100% sure at the moment). But if you do need to change the case hierarchy as it is initialized, then there is a method init_case_hierarchy in case.py.

It’s a different problem, this is something that “works” and doesn’t produce grammars with improper tdl, but causes excess ambiguity so I’m tackling it hoping to reduce that. Restoring the old version of the code fixed that issue, though.

I’ll look into those, thanks!

1 Like

I might be using the wrong terminology, but here’s the issue I’m having now.

I need a case hierarchy like the following:

case := *top*.
nom+gen := case. ;supertype exists because there's an affix that can mark either nom or gen
nom := nom+gen.
gen := nom+gen.
acc+dat := case. ;supertype exists because adpositions can mark either of these cases and a non-inflecting lexical rule that marks a noun as underspecified between either must be created
acc := non-nom+gen.
dat := non-nom+gen.

I made an addition to the part of the code that creates the nom+gen case for an affix that can mark either of those cases, and I was able to get the hierarchy I wanted, but the function that calculates the supertypes gave the acc+dat type a name I was not expecting:

case := *top*.
nom+gen := case.
nom := nom+gen.
gen := nom+gen.
non-nom+gen := case.
acc := non-nom+gen.
dat := non-nom+gen.

So in the code that adds the lexical rule I’m stuck:

        adp_cases = []
        cases = case_names(ch)
        for c in cases:
            if ch.has_adp_case(c[0]):
                adp_cases.append(canon_to_abbr(c[0], cases))
        adp_cases.sort()
        adp_case_name = "+".join(adp_cases)

        idx = ch[pc.full_key + '_lrt'].next_iter_num()
        lrt_key = pc.full_key + '_lrt' + str(idx)
        ch[lrt_key + '_name'] = get_name(pc) + '-synth-adp'
        ch[lrt_key + '_feat1_name'] = 'case'
        ch[lrt_key + '_feat1_value'] = adp_case_name <-- need the supertype discussed above
        ch[lrt_key + '_lri1_inflecting'] = 'no'
        ch[lrt_key + '_lri1_orth'] = ''

In this code, I get the case names marked by each adposition, sort them, and make a string, in this grammar this turns out to be “acc+dat.” But because the code that updates the hierarchy is actually coming up with an unexpected name (non-nom+gen), I can’t reliably predict what the appropriate type is for this lexical rule, and so I am starting to think passing around the hierarchies would be a good idea, because that way instead of making guesses as to what types exist, I can use the information from the choices object then cross check it with the established hierarchy. But this would be a pretty large change and I’d have to pass it to all the add_lexrules functions, even though as it stands now only the one I’m messing with needs it.

Perhaps another alternative could be tacking it onto the Choices object? I’m not sure.

I can’t reliably predict what the appropriate type is for this lexical rule, and so I am starting to think passing around the hierarchies would be a good idea, because that way instead of making guesses as to what types exist, I can use the information from the choices object then cross check it with the established hierarchy. But this would be a pretty large change and I’d have to pass it to all the add_lexrules functions, even though as it stands now only the one I’m messing with needs it.

I probably still don’t have the full detailed understanding but all in all, passing around a hierarchies object does not seem like a terrible idea, even if it is not currently done. It certainly sounds better than additional convoluted code that is trying to guess things, for example.

That being said, historically, every time I tried modifying the morphotactics library, I had to retreat because every time it would turn out that it stores things in such a way that I can’t figure out how to access them, regardless of what I pass around. But, it is certainly worth a try, if it sounds like adding an parameter to a few functions could help.

Which code is that, specifically?

The function is called get_type_covering in tdlhierarchy.py. It’s a function in the TDLHierarchy class, and given the object itself and a set of types it updates the hierarchy appropriately.

And would you say, in your case, this function is given correct input and gives incorrect output? Or is it the case that it is given input which it does not expect? In the first case, it should be possible to fix the function; in the latter case, it probably should not be called. (Or, it could still be possible to fix it but first you’d need to establish whether it should even be called.)

It’s not that the output is incorrect, it’s that it was giving output that I didn’t personally expect.

In my example above, there are two case marking adpositions, one that marks “acc” and one that marks “dat.” So in the function that writes non-inflecting rules to mark nouns for these cases (I’m not really sure why this exists, it’s just part of the case library), it used to make one rule for each possible adpositional case. So a non-inflecting rule that marked the noun’s case as “acc” and one that did “dat.” But Emily and I decided there should be one rule that marks the case as “acc+dat,” which reduces ambiguity.

When I was doing this, because I couldn’t access the hierarchy, I was just assigning the rule’s case value as “acc+dat” because that’s what I thought it would be, but then the portion of the code that updates the hierarchy came up with “non-nom+gen.” The structure of the hierarchy was correct, but the name was “unexpected”/unpredictable, so I needed the hierarchy when making the rule to be sure I was giving my new rule a valid value.

So now I updated the Choices object to carry the hierarchies with it so I could access it in the function I was updating. Everything is working as I intended now and the regression tests are passing, so that’s promising.

1 Like