#!/usr/bin/python

# Finite multi-mappings, like the stuff I wrote about "mvfs:
# multivalued functions" in 2003, using + and * for (commutative,
# associative) union and (non-commutative, associative) composition,
# and supporting transitive closure.

# This code only supports finite mvfs, and unlike what I'd originally
# written, it suppresses duplicates.  .values() from my old post is
# called .items() to match Python usage here; .cut() and .paste() are
# not included (but they aren't sufficient anyway); "compose" is
# written "*"; and lists (or other sequences) are mapped to finite
# multimappings with list indices as the keys, not "None".

class FiniteMultiMapping(object):
    def __init__(self, *pairs, **morepairs):
        pairs = list(pairs) + morepairs.items()
        pairs.sort()
        self.mapping, previtem = {}, None
        for item in pairs:
            if item != previtem:
                previtem = key, val = item
                if not self.mapping.has_key(key): self.mapping[key] = []
                self.mapping[key].append(val)
        self._keys = self.mapping.keys()
        self._keys.sort()
    def __getitem__(self, key):
        try: return self.mapping[key]
        except KeyError: return []
    def keys(self): return self._keys
    def items(self):
        return sum([[(key, val) for val in self[key]] for key in self.keys()], 
[])
    def __eq__(self, other):
        if not hasattr(other, 'mapping') or not hasattr(other, 'items'): return 
False
        return self.items() == other.items()
    def __ne__(self, other): return not (self == other)
    def __add__(self, other):
        return FiniteMultiMapping(*(self.items() + other.items()))
    def __mul__(self, other):
        rv = sum([[(key, val2) for val2 in other[val]] for key, val in 
self.items()], [])
        return FiniteMultiMapping(*rv)
    def __repr__(self):
        return '<ffm {%s}>' % ', '.join(['%r -> %r' % item for item in 
self.items()])
    def invert(self):
        return FiniteMultiMapping(*[(val, key) for key, val in self.items()])
    def tclosure(self):
        rv = self
        while 1:
            nrv = rv + rv * rv
            if nrv == rv: return nrv
            rv = nrv

def ffm_from_list(inlist):
    return FiniteMultiMapping(*[(ii, inlist[ii]) for ii in range(len(inlist))])

def ok(a, b): assert a == b, (a, b)
def test():
    x = FiniteMultiMapping()
    assert x[1] == []
    x = FiniteMultiMapping(x=1, y=2)
    assert x[1] == []
    assert x['x'] == [1]
    assert x.items() == [('x', 1), ('y', 2)]

    # ==, !=
    assert x == x
    assert not (x != x)
    assert x != FiniteMultiMapping()
    assert not (x == FiniteMultiMapping()) 
    assert x == FiniteMultiMapping(x=1, y=2)
    assert x == FiniteMultiMapping(('y', 2), ('x', 1))
    assert not (x == {'x': 1, 'y': 2})
    assert x != {'x': 1, 'y': 2}
    assert FiniteMultiMapping(x=5) != {'x': 5}
    assert not (FiniteMultiMapping(x=5) == {'x': 5})
    assert not (x == 3)

    # initialization
    assert FiniteMultiMapping(x=45) == FiniteMultiMapping(('x', 45))
    ok(len(FiniteMultiMapping(('x', 45), ('x', 45)).items()), 1)
    ok(len(FiniteMultiMapping(('x', 45), ('x', 46)).items()), 2)

    # addition
    assert x + {} == x
    assert x + x == x
    y = x + {'x': 3}
    ok(y['x'], [1, 3])
    ok(x['x'], [1])

    # multiplication (composition)
    rot13fw = FiniteMultiMapping(a='n', b='o', c='p')
    rot13bw = FiniteMultiMapping(n='a', o='b', p='c')
    rot13 = rot13fw + rot13bw
    ok(rot13.items(), [('a', 'n'), ('b', 'o'), ('c', 'p'),
                       ('n', 'a'), ('o', 'b'), ('p', 'c')])
    bono = ffm_from_list('bonocabana')
    ok(bono.items(), [(0, 'b'), (1, 'o'), (2, 'n'), (3, 'o'), (4, 'c'),
                      (5, 'a'), (6, 'b'), (7, 'a'), (8, 'n'), (9, 'a')])

    ok(bono * rot13, ffm_from_list('obabpnonan'))
    ok((bono * rot13) * rot13, bono)
    ok(bono * (rot13 * rot13), bono)
    ok(rot13 * rot13, FiniteMultiMapping(a='a', b='b', c='c',
                                         n='n', o='o', p='p'))

    # inversion
    ok(rot13fw.invert(), rot13bw)
    ok(rot13.invert(), rot13)

    # transitive closure
    succ = FiniteMultiMapping((0, 1), (1, 2), (2, 3))
    gt = succ.tclosure()
    ok(gt[0], [1, 2, 3])
    ok(gt.items(), [(0, 1), (0, 2), (0, 3),
                    (1, 2), (1, 3),
                    (2, 3)])

test()

Reply via email to