Monday, June 4, 2012

Preventing FreeModule from coercing to its base_ring

I've been working on the class structure for lattices recently. As described in the project description and already suggested in a previous discussion on lattices in Sage, the generic Lattice class should inherit from FreeModule in some way. In our current approach, lattices will always have a basis, which is why the lattice base class Lattice_with_basis should probably inherit from FreeModule_submodule_with_basis_pid.

However, the issue with this is that, apparently, FreeModule expects the basis to be in the fraction field of the coefficient ring (if not in the coefficient ring itself). (The coefficient ring is called base_ring in FreeModule, which is a little confusing because this is not the ring the basis has to lie in.) For instance, a ZZ-lattice in RR^n would be constructed as (an inherited version of)
FreeModule(ZZ^n, <basis matrix>)
with a real-valued basis matrix, but then the basis matrix would be coerced to a matrix over QQ^n first, discarding the information about the original field that the lattice was embedded in. This is not desired here—we want to keep the original vector space, e.g. keep calculating with floating point numbers in RR.

After playing around with the way FreeModules are constructed, I implemented a small workaround: When calling the inherited constructor from FreeModule_submodule_with_basis_pid, echelonization and checking (whether the basis vectors are actually in the free module) are disabled to keep the basis "as is". Consequently, they have to be converted from their given form (usually lists) to elements of a vector space; this is already done in the factory function Lattice.

Furthermore, the element_class has to be overridden, because it is used in the constructor to convert the basis elements (even when echelonization and checking are disabled). Apparently, even the semi-private member _element_class has to be set because it is used instead of the interface function element_class, e.g., when random_element is called. Nevertheless, the function element_class in free_module has to be used to get the actual element class (the underlying ring being changed though), because it wraps the Element structure around the ring.

Now, the following works with Lattice:
sage: Lattice([[1.0, 0, 0], [0, 1, 0]], ZZ)
Real-embedded integer lattice of degree 3 and rank 2
Basis matrix:
[ 1.00000000000000 0.000000000000000 0.000000000000000]
[0.000000000000000  1.00000000000000 0.000000000000000]
While using FreeModule coerces to rationals:
sage: FreeModule(ZZ, 3).span([[1.0, 0, 0], [0, 1, 0]])
Free module of degree 3 and rank 2 over Integer Ring
Echelon basis matrix:
[1 0 0]
[0 1 0]
I know this workaround is not an optimal solution—maybe FreeModule itself should be modified if this behaviour is desired. Please let me know if you have any ideas.

Update: This still doesn't work. I came across an error (a pretty severe one, actually) when running the TestSuite on lattices, namely:
sage: L = Lattice([[i, 0], [0, 1]])
sage: + L.an_element()

Unhandled SIGSEGV: A segmentation fault occurred in Sage.
This probably occurred because a *compiled* component of Sage has a bug
in it and is not properly wrapped with sig_on(), sig_off(). You might
want to run Sage under gdb with 'sage -gdb' to debug this.
Sage will now terminate.
/Applications/sage/spkg/bin/sage: line 312:  4708 Segmentation fault
So while constructing lattices this way works perfectly, working with their elements does not. It seems elements still rely on L.basis_ring() being their own "parent" to some extent, which is not true e.g. for ZZ-lattices in RR^n. I really don't know whether the whole idea of subclassing from FreeModule this way is what FreeModule was designed for, so I asked on sage-devel.

No comments:

Post a Comment