Danger Will Robinson: Inherited LinearCode classes do not automatically become Modules
It is clearly a running assumption in LinearCode
that it inherits from module.Module
. In LinearCode.__init__
there are the lines:
cat = Modules(base_ring).FiniteDimensional().WithBasis().Finite()
facade_for = generator_matrix.row(0).parent()
self.Element = type(generator_matrix.row(0)) # for when we make this a non-facade parent
Parent.__init__(self, base=base_ring, facade=facade_for, category=cat)
This initialises LinearCode
as a valid member of the Sage category framework (a "category" is a matemathical term pertaining to the theory of how sets/rings/etc. operate wrt. each other. A sensible computer algebra system must be build around this theory at least to a certain extend. Sage was majorly reformed a few years ago to do this.).
This code must be run by any sub-class of LinearCode
- otherwise, various functionality in LinearCode
just won't work, and codes will interact badly with the rest of Sage (in, admittedly, probably relatively obscure uses.).
A way to test if a code has been properly initialised in the category framework is to ask for its parent:
sage: print LinearCode(random_matrix(GF(2), 2, 10)).parent()
<class 'sage.coding.linear_code.LinearCode_with_category'>
Notice the prefixed _with_category
on the class name!
If you want to know more, there is a long thread with some pedagogy on the trac ticket which fixed the problem originally in LinearCode
(found using git-blame on the line in question.): trac #16644.
Some, but not all, category-related functions fail on the current GRS code, e.g. cardinality()
. One thing that should have turned on an alarm bell in your brain is the following:
sage: print GeneralizedReedSolomonCode(GF(29), range(0, 10), 3).base_ring()
None
which you must have noticed, since your LinearCode.field()
is written with the strange if
-statement in case self.base_ring()
indeed returns None
.
In relation to the programmatic solution to this, this issue is related to #55.
The category initialisation code could be put in the same __init__linear_code
method.
Comments (6)
-
reporter -
reporter This text introduces the category framework really well: http://www.sagemath.org/doc/reference/categories/sage/categories/primer.html
-
repo owner I read the trac thread and the documentation, and I understand the issue here, there is definitely something that must be fixed in my constructions.
I have a question though : in the code you quote above, there is the following lines :
facade_for = generator_matrix.row(0).parent() self.Element = type(generator_matrix.row(0)) # for when we make this a non-facade parent
I understand these lines works well for the actual LinearCode class, as it takes GeneratorMatrix as a parameter. But these cannot be ran for subclasses construction, as generator_matrix is not built at the time the code initializes. And I can't figure out something general enough to cover all the "families" we have now, and the one that will follow. Well, I thought about something like that :
facade_for = VectorSpace(self.base_field, self._length)
but I'm not sure it is correct...Am I missing something?
-
reporter Hmm, I think you've got it write. What you're writing is one possibility, but it requires that a sub class sets its
base_field
before calling this super-initialiser. For instance, we could have the following method inLinearCode
:def __init_linear_code(self): F = self._base_field cat = Modules(F).FiniteDimensional().WithBasis().Finite() facade_for = VectorSpace(F, self._length) self.Element = type(facade_for.an_element()) # for when we make this a non-facade parent Parent.__init__(self, base=F, facade=facade_for, category=cat) <initialise the cache dictionaries>
And in e.g. GRS code constructor, we would write something like
def __init__(self, eval_points, dimension): F = eval_points[0].parent() self._base_field = F self._length = len(eval_points) self.__init_linear_code()
Alternatively,
LinearCode.__init_linear_code
should take the field and the length as arguments and then also set those two fields itself. That might save a few lines in most sub-classes. We could then write for the GRS codedef __init__(self, eval_points, dimension): self.__init_linear_code(eval_points[0].parent(), len(eval_points)) # self._base_field/base_ring and self._length are now all set
-
repo owner What you're writing is one possibility, but it requires that a sub class sets its base_field before calling this super-initialiser.
That was my concern too... So I take your proposal which sets field and length as arguments for the super-initialiser.
-
repo owner - changed status to resolved
Resolved issue
#60→ <<cset c39b6f84b8e6>>
- Log in to comment
Ok, I now see
cardinality
fails just becausebase_ring
does, so it was not a very good foundational example.