eja: ensure that Sage doesn't think EJAs are associative.
It turns out that the FiniteDimensionalAlgebrasWithBasis category
somehow has both the legacy Algebras() category and the newer
MagmaticAlgebras() category as super-categories. Problem is, the
legacy one is associative! To fix that, we now use MagmaticAlgebras
directly.
Of course, we have to reimplement some of the stuff that was done for
us before... and we have to add a bunch of hacks for parts of Sage
that break when you don't have a ring.... and we can't use a matrix
for our multiplication table any more. But it was all doable.
This way, we don't need to either regenerate it when someone calls
multiplication_table(), or cache a second copy of it. Besides,
matrices are efficient and indexing one is probably faster than
indexing a list of lists.
eja: switch some index orderings to agree with row-then-column semantics.
This doesn't actually affect anything in these cases, but the row "i"
indices should be on the outside whenever we loop through a
two-dimensional array that corresponds to a matrix.
eja: move away from using matrices as our "multiplication table."
Instead of passing around a bunch of matrices that can be applied to
vectors to figure out how multiplication works, it's simpler (and
probably faster) to use a two-dimensional array whose (i,j)th entry
contains the answer to "how do we multiply basis elements i and j?"
When we feed these arrays into the EJA constructor, they must contain
vectors: there's no algebra yet, so it's not like we have any algebra
elements to pass in. However, in the long run, it's much more
convenient to have the multiplication table *stored* in terms of
algebra elements; that way we don't have to convert back and forth
every time we want to multiply two algebra elements. The algebra
constructor now performs this conversion and stores a table containing
algebra elements. This makes product_on_basis() a simple table lookup.
eja: move the element constructor into the parent algebra class.
Instead of using the element's __init__(), we're now using the
algebra's _element_constructor_() method that only gets called
after the parent tries to coerce the argument and fails. This
is somewhat cleaner because we don't have to handle the "usual"
case.
Michael Orlitzky [Sun, 28 Jul 2019 04:36:43 +0000 (00:36 -0400)]
eja: rewrite the operator class again to eliminate VectorSpaceMorphisms.
Wherever possible, I'd like to eliminate row-vector APIs from sneaking
into userland. There's no reason for us to subclass VectorSpaceMorphism,
because left-multiplication by a scalar doesn't work anyway. So, just
drop it, and keep our own matrix variable around instead.
Michael Orlitzky [Sat, 27 Jul 2019 15:05:42 +0000 (11:05 -0400)]
eja: replace the Hom stuff with a custom EJA operator class.
Implementing homsets as linear operators was always a hack, since
linear operators will in general not be algebra homomorphisms. We
wound up re-implementing addition, subtraction, etc. of operators
anyway, so in hindsight, there is no extra difficulty in creating our
own EJA operator class as a subclass of VectorSpaceMorphism.
This commit throws out the EJA morphism stuff, and replaces it with
an equivalent EJA operator class to remain mathematically sound.
Michael Orlitzky [Sat, 27 Jul 2019 00:44:42 +0000 (20:44 -0400)]
eja: fix powers of zero for operators.
When you raise a morphism to the power of zero, it returns an object
(the identity morphism) in a much more general and useless space. We
need to be able to add/subtract these things. So now we have our own
one() method for the Homset, and our own __pow__ for morphisms.
Michael Orlitzky [Fri, 26 Jul 2019 17:03:37 +0000 (13:03 -0400)]
eja: add composition (multiplication) for morphisms.
The whatever-superclass composition of morphisms returned a formal
composition, which can't be treated like a morphism itself. Since all
of our morphisms are represented by matrices, we can simply perform
the composition ourself via matrix multiplication to return something
useful.
Michael Orlitzky [Tue, 23 Jul 2019 03:17:04 +0000 (23:17 -0400)]
eja: use the standard basis in characteristic_polynomial().
I couldn't see a reason why we needed to do a change-of-basis in the
characteristic_polynomial() function (a la Faraut and Koranyi), so I
tried it with the standard basis. And, everything seems to work? Cool.
Michael Orlitzky [Sun, 21 Jul 2019 02:53:41 +0000 (22:53 -0400)]
eja: drop the inner_product argument to the EJA constructor.
Beware, this will make the test suite fail if we ever get R^n as our
EJA and try to take a (default trace) inner product involving an
irregular element.