Ask Your Question

Revision history [back]

As far as I understand it, when you call CombinatorialFreeModule(R, basis_keys), it doesn't call its __init__ method right away: first it calls its __classcall_private__ method, which does things like convert basis_keys to a hashable type:

    if isinstance(basis_keys, (list, tuple)):
        basis_keys = FiniteEnumeratedSet(basis_keys)

Then this gets passed to the __init__ method. As a result, giving the same names for the generators results in the identical object (not just equal, but identical):

sage: M1 = CombinatorialFreeModule(ZZ, ['a', 'b', 'c'])
sage: M2 = CombinatorialFreeModule(ZZ, ('a', 'b', 'c'))
sage: M1 is M2

This works for me:

from sage.combinat.free_module import CombinatorialFreeModule, CombinatorialFreeModuleElement

class TestRingElement(CombinatorialFreeModuleElement):

    def __init__(self, M, x):
        super(TestRingElement, self).__init__(M, x) = x

class TestRing(CombinatorialFreeModule):

    Element = TestRingElement

    def __classcall__(self, R, G):
        from sage.sets.all import FiniteEnumeratedSet
        if isinstance(G, (list, tuple)):
            G = FiniteEnumeratedSet(G)
        return super(TestRing, self).__classcall__(self, R, G)

    def __init__(self, R, G):
        CombinatorialFreeModule.__init__(self, R, G)

You could check the Sage library for other classes which are based on CombinatorialFreeModule. I wrote the code in sage.algebras.steenrod.steenrod_algebra, for instance. A simpler example is in sage.categories.examples.hopf_algebras_with_basis.