#8800: Doctest coverage of categories - numerous coercion fixes
-----------------------------------------+----------------------------------
Reporter: SimonKing | Owner: Simon King
Type: defect | Status: needs_review
Priority: major | Milestone: sage-4.7
Component: categories | Keywords: categories doctests
Author: Simon King | Upstream: N/A
Reviewer: Luis Felipe Tabera Alonso | Merged:
Work_issues: |
-----------------------------------------+----------------------------------
Description changed by jdemeyer:
Old description:
> According to William, the doctest coverage of categories is too low:
>
> {{{
> action.pyx: 0% (0 of 31)
> functor.pyx: 17% (3 of 17)
> map.pyx: 27% (10 of 37)
> morphism.pyx: 20% (5 of 24)
> pushout.py: 24% (19 of 77)
> }}}
>
> The original purpose of this ticket was to provide full doctest coverage
> for `functor.pyx` and `pushout.py`. '''The doctest coverage of both files
> is now 100%'''.
>
> However, the attempt to create meaningful doctests uncovered many bugs in
> various parts of Sage, and also motivated the implementation of coercion
> for various algebraic structures for which this has not been done before.
>
> This a-posteriori ticket description lists the bugs killed and the
> features added by the patch, which should apply (with a little fuzz)
> after the patch from #8807. For more details on the bugs, see the
> comments below.
>
> 1. Bug: Creating `ForgetfulFunctor` fails.
>
> Was:
> {{{
> sage: abgrps = CommutativeAdditiveGroups()
> sage: ForgetfulFunctor(abgrps, abgrps)
> ---------------------------------------------------------------------------
> TypeError Traceback (most recent call
> last)
>
> /home/king/SAGE/patches/doku/english/<ipython console> in <module>()
>
> /home/king/SAGE/sage-4.3.1/local/lib/python2.6/site-
> packages/sage/categories/functor.so in
> sage.categories.functor.ForgetfulFunctor
> (sage/categories/functor.c:2083)()
>
> TypeError: IdentityFunctor() takes exactly one argument (2 given)
> }}}
>
> Now:
> {{{
> sage: abgrps = CommutativeAdditiveGroups()
> sage: ForgetfulFunctor(abgrps, abgrps)
> The identity functor on Category of commutative additive groups
> }}}
>
> 2. Bug: Applying `ForgetfulFunctor` returns `None`.
>
> Was:
> {{{
> sage: fields = Fields()
> sage: rings = Rings()
> sage: F = ForgetfulFunctor(fields,rings)
> sage: F(QQ)
> }}}
>
> Now:
> {{{
> sage: fields = Fields()
> sage: rings = Rings()
> sage: F = ForgetfulFunctor(fields,rings)
> sage: F(QQ)
> Rational Field
> }}}
>
> 3. Bug: Applying a functor does not complain if the argument is not
> contained in the domain.
>
> Was:
> {{{
> sage: fields = Fields()
> sage: rings = Rings()
> sage: F = ForgetfulFunctor(fields,rings)
> # Yields None, see previous bug
> sage: F(ZZ['x','y'])
> }}}
>
> Now:
> {{{
> sage: fields = Fields()
> sage: rings = Rings()
> sage: F = ForgetfulFunctor(fields,rings)
> sage: F(ZZ['x','y'])
> Traceback (most recent call last):
> ...
> TypeError: x (=Multivariate Polynomial Ring in x, y over Integer Ring) is
> not in Category of fields
> }}}
>
> 4. Bug: Comparing identity functor with any functor only checks domain
> and codomain
>
> Was:
> {{{
> sage: F = QQ['x'].construction()[0]
> sage: F
> Poly[x]
> sage: F == IdentityFunctor(Rings())
> False
> sage: IdentityFunctor(Rings()) == F
> True
> }}}
>
> Now:
> {{{
> sage: F = QQ['x'].construction()[0]
> sage: F
> Poly[x]
> sage: F == IdentityFunctor(Rings())
> False
> sage: IdentityFunctor(Rings()) == F
> False
> }}}
>
> 5. Bug: Comparing identity functor with anything that is not a functor
> produces an error
>
> Was:
> {{{
> sage: IdentityFunctor(Rings()) == QQ
> Traceback (most recent call last):
> ...
> AttributeError: 'RationalField_with_category' object has no attribute
> 'domain'
> }}}
>
> Now:
> {{{
> sage: IdentityFunctor(Rings()) == QQ
> False
> }}}
>
> 6. Bug: The matrix functor is ill defined; moreover, ill-definedness does
> not result in an error.
>
> Was:
> {{{
> sage: F = MatrixSpace(ZZ,2,3).construction()[0]
> sage: F(RR) in F.codomain()
> False
> # The codomain is wrong for non-square matrices!
> sage: F.codomain()
> Category of rings
> }}}
>
> Now:
> {{{
> sage: F = MatrixSpace(ZZ,2,3).construction()[0]
> sage: F.codomain()
> Category of commutative additive groups
> sage: F(RR) in F.codomain()
> True
> sage: F = MatrixSpace(ZZ,2,2).construction()[0]
> sage: F.codomain()
> Category of rings
> sage: F(RR) in F.codomain()
> True
> }}}
>
> 7. Bug: Wrong domain for `VectorFunctor`; and again, functors don't test
> if the domain is appropriate
>
> Was:
> {{{
> sage: F = FreeModule(ZZ,3).construction()[0]
> sage: F
> VectorFunctor
> sage: F.domain()
> Category of objects
> sage: F.codomain()
> Category of objects
> sage: Set([1,2,3]) in F.domain()
> True
> sage: F(Set([1,2,3]))
> Traceback (most recent call last):
> ...
> AttributeError: 'Set_object_enumerated' object has no attribute
> 'is_commutative'
> }}}
>
> Now:
> {{{
> sage: F = FreeModule(ZZ,3).construction()[0]
> sage: F
> VectorFunctor
> sage: F.domain()
> Category of commutative rings
> sage: Set([1,2,3]) in F.domain()
> False
> sage: F(Set([1,2,3]))
> Traceback (most recent call last):
> ...
> TypeError: x (={1, 2, 3}) is not in Category of commutative rings
> }}}
>
> 8. Bug: `BlackBoxConstructionFunctor` is completely unusable
>
> `BlackBoxConstructionFunctor` should be a class, but is defined as a
> function. Moreover, the given init method is not using the init method of
> `ConstructionFunctor`. And the cmp method would raise an error if the
> second argument has no attribute `.box`.
>
> The following did not work at all:
> {{{
> sage: from sage.categories.pushout import BlackBoxConstructionFunctor
> sage: FG = BlackBoxConstructionFunctor(gap)
> sage: FS = BlackBoxConstructionFunctor(singular)
> sage: FG
> BlackBoxConstructionFunctor
> sage: FG(ZZ)
> Integers
> sage: FG(ZZ).parent()
> Gap
> sage: FS(QQ['t'])
> // characteristic : 0
> // number of vars : 1
> // block 1 : ordering lp
> // : names t
> // block 2 : ordering C
> sage: FG == FS
> False
> sage: FG == loads(dumps(FG))
> True
> }}}
>
> 9. Nitpicking: The `LocalizationFunctor` is nowhere used (yet)
>
> Hence, I removed it.
>
> 10. Bug / New Feature: Make completion and and fraction field
> construction functors commute.
>
> The result of them not commuting is the following coercion bug.
>
> Was:
> {{{
> sage: R1.<x> = Zp(5)[]
> sage: R2 = Qp(5)
> sage: R2(1)+x
> Traceback (most recent call last):
> ...
> TypeError: unsupported operand parent(s) for '+': '5-adic Field with
> capped relative precision 20' and 'Univariate Polynomial Ring in x over
> 5-adic Ring with capped relative precision 20'
> }}}
>
> Now:
> {{{
> sage: R1.<x> = Zp(5)[]
> sage: R2 = Qp(5)
> sage: R2(1)+x
> (1 + O(5^20))*x + (1 + O(5^20))
> }}}
>
> 11. New feature: Make the completion functor work on some objects that do
> not provide a completion method.
>
> The idea is to use that the completion functor may commute with the
> construction of the given argument. That may safe the day.
>
> Was:
> {{{
> sage: P.<x> = ZZ[]
> sage: C = P.completion(x).construction()[0]
> sage: R = FractionField(P)
> sage: hasattr(R,'completion')
> False
> sage: C(R)
> Traceback (most recent call last):
> ...
> AttributeError: 'FractionField_generic' object has no attribute
> 'completion'
> }}}
>
> Now:
> {{{
> sage: P.<x> = ZZ[]
> sage: C = P.completion(x).construction()[0]
> sage: R = FractionField(P)
> sage: hasattr(R,'completion')
> False
> sage: C(R)
> Fraction Field of Power Series Ring in x over Integer Ring
> }}}
>
> 12. Bug / new feature: Coercion for free modules, taking into account a
> user-defined inner product
>
> Was:
> {{{
> sage: P.<t> = ZZ[]
> sage: M1 = FreeModule(P,3)
> sage: M2 = QQ^3
> sage: M2([1,1/2,1/3]) + M1([t,t^2+t,3]) # This is ok
> (t + 1, t^2 + t + 1/2, 10/3)
> sage: M3 = FreeModule(P,3, inner_product_matrix = Matrix(3,3,range(9)))
> sage: M2([1,1/2,1/3]) + M3([t,t^2+t,3]) # This is ok
> (t + 1, t^2 + t + 1/2, 10/3)
> # The user defined inner product matrix is lost! Bug
> sage: parent(M2([1,1/2,1/3]) + M3([t,t^2+t,3])).inner_product_matrix()
> [1 0 0]
> [0 1 0]
> [0 0 1]
> }}}
>
> Now:
> {{{
> sage: parent(M2([1,1/2,1/3]) + M3([t,t^2+t,3])).inner_product_matrix()
> [0 1 2]
> [3 4 5]
> [6 7 8]
> }}}
>
> However, the real problem is that modules are not part of the coercion
> model. I tried to implement it, but that turned out to be a can of worms.
> So, '''I suggest to deal with it on a different ticket'''. Here is one
> bug that isn't removed, yet:
> {{{
> sage: M4 = FreeModule(P,3, inner_product_matrix =
> Matrix(3,3,[1,1,1,0,1,1,0,0,1]))
> sage: M3([t,1,t^2]) + M4([t,t^2+t,3]) # This should result in an
> error
> (2*t, t^2 + t + 1, t^2 + 3)
> }}}
> Note that there should be no coercion between `M3` and `M4`, since they
> have different user-defined inner product matrices.
>
> 13. Bug / new feature: Quotient rings of univariate polynomial rings do
> not have a construction method.
>
> Was:
> {{{
> sage: P.<x> = QQ[]
> sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
> sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
> sage: from sage.categories.pushout import pushout
> sage: pushout(Q1,Q2)
> Traceback (most recent call last):
> ...
> CoercionException: No common base
> }}}
>
> Now:
> {{{
> sage: P.<x> = QQ[]
> sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
> sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
> sage: from sage.categories.pushout import pushout
> sage: pushout(Q1,Q2)
> Univariate Quotient Polynomial Ring in xbar over Rational Field with
> modulus x^4 + 2*x^2 + 1
> }}}
>
> 14. Insufficient coercion of quotient rings, if one modulus divides the
> other
>
> Was:
> {{{
> sage: P5.<x> = GF(5)[]
> sage: Q = P5.quo([(x^2+1)^2])
> sage: P.<x> = ZZ[]
> sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
> sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
> sage: Q.has_coerce_map_from(Q1)
> False
> }}}
>
> Now: There is a coercion from `Q1` to `Q`.
>
> 15. Coercion of `GF(p)` versus `Integers(p)`
>
> I am not sure if this is really a bug.
>
> Was:
> {{{
> sage: from sage.categories.pushout import pushout
> sage: pushout(GF(5), Integers(5))
> Ring of integers modulo 5
> }}}
>
> Now
> {{{
> sage: from sage.categories.pushout import pushout
> sage: pushout(GF(5), Integers(5))
> Finite Field of size 5
> }}}
>
> 16. Bug / new feature: Construction for QQbar was missing.
>
> Now:
> {{{
> sage: QQbar.construction()
> (AlgebraicClosureFunctor, Rational Field)
> }}}
>
> 17. Bug / new feature: Construction for number fields is missing.
>
> This became a rather complicated topic, including "coercions for embedded
> versus non-embedded number fields and coercion for an order from a
> coercion from the ambient field", "pushout for number fields",
> "comparison of fractional ideals", "identity of residue fields". See
> three discussions on sage-algebra and sage-nt
> * [http://groups.google.com/group/sage-
> nt/browse_thread/thread/32b65a5173f43267 Bidirectional coercions]
> * [http://groups.google.com/group/sage-
> nt/browse_thread/thread/5c376dbf7e99ea97 Coercions for number fields]
> * [http://groups.google.com/group/sage-
> nt/browse_thread/thread/54c1e33872d14334 Comparison of fractional ideals]
>
> __Coercion__
>
> Important for the discussion is: What will we do with embeddings?
>
> Currently, the embedding of two number fields is used to construct a
> coercion (compatible with the embedding). Of course, the given embedding
> is also used as a coerce map.
>
> It was discussed to additionally have a "forgetful" coercion from an
> embedded to a non-embedded number field.
>
> It turned out that with bidirectional and forgetful coercions together,
> one can construct examples in which the coercions do not form a
> commutative diagram. Hence, we do ''not'' introduce forgetful coercions
> here.
>
> However, some improvement of the existing implementation was needed.
>
> Was:
> {{{
> sage: K.<r4> = NumberField(x^4-2)
> sage: L1.<r2_1> = NumberField(x^2-2, embedding = r4**2)
> sage: L2.<r2_2> = NumberField(x^2-2, embedding = -r4**2)
> sage: r2_1+r2_2 # indirect doctest
> ERROR: An unexpected error occurred while tokenizing input
> The following traceback may be corrupted or invalid
> The error message is: ('EOF in multi-line statement', (1109, 0))
>
> ERROR: An unexpected error occurred while tokenizing input
> The following traceback may be corrupted or invalid
> The error message is: ('EOF in multi-line statement', (1109, 0))
>
> ...
> sage: K.has_coerce_map_from(L1.maximal_order())
> False # that's the wrong direction.
> sage: L1.has_coerce_map_from(K.maximal_order())
> True
> }}}
>
> Now:
> {{{
> sage: K.<r4> = NumberField(x^4-2)
> sage: L1.<r2_1> = NumberField(x^2-2, embedding = r4**2)
> sage: L2.<r2_2> = NumberField(x^2-2, embedding = -r4**2)
> sage: r2_1+r2_2 # indirect doctest
> 0
> sage: (r2_1+r2_2).parent() is L1
> True
> sage: (r2_2+r2_1).parent() is L2
> True
> sage: K.has_coerce_map_from(L1.maximal_order())
> True
> sage: L1.has_coerce_map_from(K.maximal_order())
> False
> }}}
>
> __Pushout__
>
> Was:
> {{{
> sage: P.<x> = QQ[]
> sage: L.<b> = NumberField(x^8-x^4+1, embedding=CDF.0)
> sage: M1.<c1> = NumberField(x^2+x+1, embedding=b^4-1)
> sage: M2.<c2> = NumberField(x^2+1, embedding=-b^6)
> sage: M1.coerce_map_from(M2)
> sage: M2.coerce_map_from(M1)
> sage: c1+c2; parent(c1+c2) #indirect doctest
> Traceback (most recent call last):
> ...
> TypeError: unsupported operand parent(s) for '+': 'Number Field in c1
> with defining polynomial x^2 + x + 1' and 'Number Field in c2 with
> defining polynomial x^2 + 1'
> sage: from sage.categories.pushout import pushout
> sage: pushout(M1['x'],M2['x'])
> Traceback (most recent call last):
> ...
> CoercionException: No common base
> }}}
>
> Now: Note that we will only have a pushout if the codomains of the
> embeddings are number fields. Hence, in the second example, we won't use
> `CDF` as a pushout.
> {{{
> sage: P.<x> = QQ[]
> sage: L.<b> = NumberField(x^8-x^4+1, embedding=CDF.0)
> sage: M1.<c1> = NumberField(x^2+x+1, embedding=b^4-1)
> sage: M2.<c2> = NumberField(x^2+1, embedding=-b^6)
> sage: M1.coerce_map_from(M2)
> sage: M2.coerce_map_from(M1)
> sage: c1+c2; parent(c1+c2) #indirect doctest
> -b^6 + b^4 - 1
> Number Field in b with defining polynomial x^8 - x^4 + 1
> sage: from sage.categories.pushout import pushout
> sage: pushout(M1['x'],M2['x'])
> Univariate Polynomial Ring in x over Number Field in b with defining
> polynomial x^8 - x^4 + 1
> sage: K.<a> = NumberField(x^3-2, embedding=CDF(1/2*I*2^(1/3)*sqrt(3) -
> 1/2*2^(1/3)))
> sage: L.<b> = NumberField(x^6-2, embedding=1.1)
> sage: L.coerce_map_from(K)
> sage: K.coerce_map_from(L)
> sage: pushout(K,L)
> Traceback (most recent call last):
> ...
> CoercionException: ('Ambiguous Base Extension', Number Field in a with
> defining polynomial x^3 - 2, Number Field in b with defining polynomial
> x^6 - 2)
> }}}
>
> __Comparison of fractional ideals / identity of Residue Fields__
>
> Fractional ideals have a `__cmp__` method that only took into account
> the Hermite normal form. Originally, the comparison of fractional ideals
> by "==" and by "cmp" yields different results. Since "==" of fractional
> ideals is used for caching residue fields, but "cmp" was used for
> comparing residue fields, the residue fields did not provide unique
> parents.
>
> Was:
> {{{
> sage: L.<b> = NumberField(x^8-x^4+1)
> sage: F_2 = L.fractional_ideal(b^2-1)
> sage: F_4 = L.fractional_ideal(b^4-1)
> sage: F_2==F_4
> True
> sage: K.<r4> = NumberField(x^4-2)
> sage: L.<r4> = NumberField(x^4-2, embedding=CDF.0)
> sage: FK = K.fractional_ideal(K.0)
> sage: FL = L.fractional_ideal(L.0)
> sage: FK != FL
> True
> sage: RL = ResidueField(FL)
> sage: RK = ResidueField(FK)
> sage: RK is RL
> False
> sage: RK == RL
> True
> }}}
>
> Now:
> {{{
> sage: L.<b> = NumberField(x^8-x^4+1)
> sage: F_2 = L.fractional_ideal(b^2-1)
> sage: F_4 = L.fractional_ideal(b^4-1)
> sage: F_2==F_4
> True
> sage: K.<r4> = NumberField(x^4-2)
> sage: L.<r4> = NumberField(x^4-2, embedding=CDF.0)
> sage: FK = K.fractional_ideal(K.0)
> sage: FL = L.fractional_ideal(L.0)
> sage: FK != FL
> True
> sage: RL = ResidueField(FL)
> sage: RK = ResidueField(FK)
> sage: RK is RL
> False
> sage: RK == RL
> False
> }}}
>
> Since `RL` is defined with the embedded field `L`, not with the
> unembedded `K`, there is no coercion from the order of `K` to `RL`.
> However, ''conversion'' works (this used to fail!):
>
> {{{
> sage: OK = K.maximal_order()
> sage: RL.has_coerce_map_from(OK)
> False
> sage: RL(OK.1)
> 0
> }}}
>
> Note that I also had to change some arithmetic stuff in the `_tate`
> method of elliptic curves: The old implementation relied on the
> assumption that fractional ideals in an embedded field and in a non-
> embedded field can't be equal. This assumption should still hold (since
> we do not introduce forgetful coercion), but I think it is OK to keep the
> change in _tate.
>
> Apply:
>
> 1. 8800_functor_pushout_doc_and_fixes.patch[BR]
> 2. referee.patch
New description:
According to William, the doctest coverage of categories is too low:
{{{
action.pyx: 0% (0 of 31)
functor.pyx: 17% (3 of 17)
map.pyx: 27% (10 of 37)
morphism.pyx: 20% (5 of 24)
pushout.py: 24% (19 of 77)
}}}
The original purpose of this ticket was to provide full doctest coverage
for `functor.pyx` and `pushout.py`. '''The doctest coverage of both files
is now 100%'''.
However, the attempt to create meaningful doctests uncovered many bugs in
various parts of Sage, and also motivated the implementation of coercion
for various algebraic structures for which this has not been done before.
This a-posteriori ticket description lists the bugs killed and the
features added by the patch, which should apply (with a little fuzz) after
the patch from #8807. For more details on the bugs, see the comments
below.
1. Bug: Creating `ForgetfulFunctor` fails.
Was:
{{{
sage: abgrps = CommutativeAdditiveGroups()
sage: ForgetfulFunctor(abgrps, abgrps)
---------------------------------------------------------------------------
TypeError Traceback (most recent call
last)
/home/king/SAGE/patches/doku/english/<ipython console> in <module>()
/home/king/SAGE/sage-4.3.1/local/lib/python2.6/site-
packages/sage/categories/functor.so in
sage.categories.functor.ForgetfulFunctor
(sage/categories/functor.c:2083)()
TypeError: IdentityFunctor() takes exactly one argument (2 given)
}}}
Now:
{{{
sage: abgrps = CommutativeAdditiveGroups()
sage: ForgetfulFunctor(abgrps, abgrps)
The identity functor on Category of commutative additive groups
}}}
2. Bug: Applying `ForgetfulFunctor` returns `None`.
Was:
{{{
sage: fields = Fields()
sage: rings = Rings()
sage: F = ForgetfulFunctor(fields,rings)
sage: F(QQ)
}}}
Now:
{{{
sage: fields = Fields()
sage: rings = Rings()
sage: F = ForgetfulFunctor(fields,rings)
sage: F(QQ)
Rational Field
}}}
3. Bug: Applying a functor does not complain if the argument is not
contained in the domain.
Was:
{{{
sage: fields = Fields()
sage: rings = Rings()
sage: F = ForgetfulFunctor(fields,rings)
# Yields None, see previous bug
sage: F(ZZ['x','y'])
}}}
Now:
{{{
sage: fields = Fields()
sage: rings = Rings()
sage: F = ForgetfulFunctor(fields,rings)
sage: F(ZZ['x','y'])
Traceback (most recent call last):
...
TypeError: x (=Multivariate Polynomial Ring in x, y over Integer Ring) is
not in Category of fields
}}}
4. Bug: Comparing identity functor with any functor only checks domain and
codomain
Was:
{{{
sage: F = QQ['x'].construction()[0]
sage: F
Poly[x]
sage: F == IdentityFunctor(Rings())
False
sage: IdentityFunctor(Rings()) == F
True
}}}
Now:
{{{
sage: F = QQ['x'].construction()[0]
sage: F
Poly[x]
sage: F == IdentityFunctor(Rings())
False
sage: IdentityFunctor(Rings()) == F
False
}}}
5. Bug: Comparing identity functor with anything that is not a functor
produces an error
Was:
{{{
sage: IdentityFunctor(Rings()) == QQ
Traceback (most recent call last):
...
AttributeError: 'RationalField_with_category' object has no attribute
'domain'
}}}
Now:
{{{
sage: IdentityFunctor(Rings()) == QQ
False
}}}
6. Bug: The matrix functor is ill defined; moreover, ill-definedness does
not result in an error.
Was:
{{{
sage: F = MatrixSpace(ZZ,2,3).construction()[0]
sage: F(RR) in F.codomain()
False
# The codomain is wrong for non-square matrices!
sage: F.codomain()
Category of rings
}}}
Now:
{{{
sage: F = MatrixSpace(ZZ,2,3).construction()[0]
sage: F.codomain()
Category of commutative additive groups
sage: F(RR) in F.codomain()
True
sage: F = MatrixSpace(ZZ,2,2).construction()[0]
sage: F.codomain()
Category of rings
sage: F(RR) in F.codomain()
True
}}}
7. Bug: Wrong domain for `VectorFunctor`; and again, functors don't test
if the domain is appropriate
Was:
{{{
sage: F = FreeModule(ZZ,3).construction()[0]
sage: F
VectorFunctor
sage: F.domain()
Category of objects
sage: F.codomain()
Category of objects
sage: Set([1,2,3]) in F.domain()
True
sage: F(Set([1,2,3]))
Traceback (most recent call last):
...
AttributeError: 'Set_object_enumerated' object has no attribute
'is_commutative'
}}}
Now:
{{{
sage: F = FreeModule(ZZ,3).construction()[0]
sage: F
VectorFunctor
sage: F.domain()
Category of commutative rings
sage: Set([1,2,3]) in F.domain()
False
sage: F(Set([1,2,3]))
Traceback (most recent call last):
...
TypeError: x (={1, 2, 3}) is not in Category of commutative rings
}}}
8. Bug: `BlackBoxConstructionFunctor` is completely unusable
`BlackBoxConstructionFunctor` should be a class, but is defined as a
function. Moreover, the given init method is not using the init method of
`ConstructionFunctor`. And the cmp method would raise an error if the
second argument has no attribute `.box`.
The following did not work at all:
{{{
sage: from sage.categories.pushout import BlackBoxConstructionFunctor
sage: FG = BlackBoxConstructionFunctor(gap)
sage: FS = BlackBoxConstructionFunctor(singular)
sage: FG
BlackBoxConstructionFunctor
sage: FG(ZZ)
Integers
sage: FG(ZZ).parent()
Gap
sage: FS(QQ['t'])
// characteristic : 0
// number of vars : 1
// block 1 : ordering lp
// : names t
// block 2 : ordering C
sage: FG == FS
False
sage: FG == loads(dumps(FG))
True
}}}
9. Nitpicking: The `LocalizationFunctor` is nowhere used (yet)
Hence, I removed it.
10. Bug / New Feature: Make completion and and fraction field construction
functors commute.
The result of them not commuting is the following coercion bug.
Was:
{{{
sage: R1.<x> = Zp(5)[]
sage: R2 = Qp(5)
sage: R2(1)+x
Traceback (most recent call last):
...
TypeError: unsupported operand parent(s) for '+': '5-adic Field with
capped relative precision 20' and 'Univariate Polynomial Ring in x over
5-adic Ring with capped relative precision 20'
}}}
Now:
{{{
sage: R1.<x> = Zp(5)[]
sage: R2 = Qp(5)
sage: R2(1)+x
(1 + O(5^20))*x + (1 + O(5^20))
}}}
11. New feature: Make the completion functor work on some objects that do
not provide a completion method.
The idea is to use that the completion functor may commute with the
construction of the given argument. That may safe the day.
Was:
{{{
sage: P.<x> = ZZ[]
sage: C = P.completion(x).construction()[0]
sage: R = FractionField(P)
sage: hasattr(R,'completion')
False
sage: C(R)
Traceback (most recent call last):
...
AttributeError: 'FractionField_generic' object has no attribute
'completion'
}}}
Now:
{{{
sage: P.<x> = ZZ[]
sage: C = P.completion(x).construction()[0]
sage: R = FractionField(P)
sage: hasattr(R,'completion')
False
sage: C(R)
Fraction Field of Power Series Ring in x over Integer Ring
}}}
12. Bug / new feature: Coercion for free modules, taking into account a
user-defined inner product
Was:
{{{
sage: P.<t> = ZZ[]
sage: M1 = FreeModule(P,3)
sage: M2 = QQ^3
sage: M2([1,1/2,1/3]) + M1([t,t^2+t,3]) # This is ok
(t + 1, t^2 + t + 1/2, 10/3)
sage: M3 = FreeModule(P,3, inner_product_matrix = Matrix(3,3,range(9)))
sage: M2([1,1/2,1/3]) + M3([t,t^2+t,3]) # This is ok
(t + 1, t^2 + t + 1/2, 10/3)
# The user defined inner product matrix is lost! Bug
sage: parent(M2([1,1/2,1/3]) + M3([t,t^2+t,3])).inner_product_matrix()
[1 0 0]
[0 1 0]
[0 0 1]
}}}
Now:
{{{
sage: parent(M2([1,1/2,1/3]) + M3([t,t^2+t,3])).inner_product_matrix()
[0 1 2]
[3 4 5]
[6 7 8]
}}}
However, the real problem is that modules are not part of the coercion
model. I tried to implement it, but that turned out to be a can of worms.
So, '''I suggest to deal with it on a different ticket'''. Here is one bug
that isn't removed, yet:
{{{
sage: M4 = FreeModule(P,3, inner_product_matrix =
Matrix(3,3,[1,1,1,0,1,1,0,0,1]))
sage: M3([t,1,t^2]) + M4([t,t^2+t,3]) # This should result in an error
(2*t, t^2 + t + 1, t^2 + 3)
}}}
Note that there should be no coercion between `M3` and `M4`, since they
have different user-defined inner product matrices.
13. Bug / new feature: Quotient rings of univariate polynomial rings do
not have a construction method.
Was:
{{{
sage: P.<x> = QQ[]
sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
sage: from sage.categories.pushout import pushout
sage: pushout(Q1,Q2)
Traceback (most recent call last):
...
CoercionException: No common base
}}}
Now:
{{{
sage: P.<x> = QQ[]
sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
sage: from sage.categories.pushout import pushout
sage: pushout(Q1,Q2)
Univariate Quotient Polynomial Ring in xbar over Rational Field with
modulus x^4 + 2*x^2 + 1
}}}
14. Insufficient coercion of quotient rings, if one modulus divides the
other
Was:
{{{
sage: P5.<x> = GF(5)[]
sage: Q = P5.quo([(x^2+1)^2])
sage: P.<x> = ZZ[]
sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
sage: Q.has_coerce_map_from(Q1)
False
}}}
Now: There is a coercion from `Q1` to `Q`.
15. Coercion of `GF(p)` versus `Integers(p)`
I am not sure if this is really a bug.
Was:
{{{
sage: from sage.categories.pushout import pushout
sage: pushout(GF(5), Integers(5))
Ring of integers modulo 5
}}}
Now
{{{
sage: from sage.categories.pushout import pushout
sage: pushout(GF(5), Integers(5))
Finite Field of size 5
}}}
16. Bug / new feature: Construction for QQbar was missing.
Now:
{{{
sage: QQbar.construction()
(AlgebraicClosureFunctor, Rational Field)
}}}
17. Bug / new feature: Construction for number fields is missing.
This became a rather complicated topic, including "coercions for embedded
versus non-embedded number fields and coercion for an order from a
coercion from the ambient field", "pushout for number fields", "comparison
of fractional ideals", "identity of residue fields". See three discussions
on sage-algebra and sage-nt
* [http://groups.google.com/group/sage-
nt/browse_thread/thread/32b65a5173f43267 Bidirectional coercions]
* [http://groups.google.com/group/sage-
nt/browse_thread/thread/5c376dbf7e99ea97 Coercions for number fields]
* [http://groups.google.com/group/sage-
nt/browse_thread/thread/54c1e33872d14334 Comparison of fractional ideals]
__Coercion__
Important for the discussion is: What will we do with embeddings?
Currently, the embedding of two number fields is used to construct a
coercion (compatible with the embedding). Of course, the given embedding
is also used as a coerce map.
It was discussed to additionally have a "forgetful" coercion from an
embedded to a non-embedded number field.
It turned out that with bidirectional and forgetful coercions together,
one can construct examples in which the coercions do not form a
commutative diagram. Hence, we do ''not'' introduce forgetful coercions
here.
However, some improvement of the existing implementation was needed.
Was:
{{{
sage: K.<r4> = NumberField(x^4-2)
sage: L1.<r2_1> = NumberField(x^2-2, embedding = r4**2)
sage: L2.<r2_2> = NumberField(x^2-2, embedding = -r4**2)
sage: r2_1+r2_2 # indirect doctest
ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (1109, 0))
ERROR: An unexpected error occurred while tokenizing input
The following traceback may be corrupted or invalid
The error message is: ('EOF in multi-line statement', (1109, 0))
...
sage: K.has_coerce_map_from(L1.maximal_order())
False # that's the wrong direction.
sage: L1.has_coerce_map_from(K.maximal_order())
True
}}}
Now:
{{{
sage: K.<r4> = NumberField(x^4-2)
sage: L1.<r2_1> = NumberField(x^2-2, embedding = r4**2)
sage: L2.<r2_2> = NumberField(x^2-2, embedding = -r4**2)
sage: r2_1+r2_2 # indirect doctest
0
sage: (r2_1+r2_2).parent() is L1
True
sage: (r2_2+r2_1).parent() is L2
True
sage: K.has_coerce_map_from(L1.maximal_order())
True
sage: L1.has_coerce_map_from(K.maximal_order())
False
}}}
__Pushout__
Was:
{{{
sage: P.<x> = QQ[]
sage: L.<b> = NumberField(x^8-x^4+1, embedding=CDF.0)
sage: M1.<c1> = NumberField(x^2+x+1, embedding=b^4-1)
sage: M2.<c2> = NumberField(x^2+1, embedding=-b^6)
sage: M1.coerce_map_from(M2)
sage: M2.coerce_map_from(M1)
sage: c1+c2; parent(c1+c2) #indirect doctest
Traceback (most recent call last):
...
TypeError: unsupported operand parent(s) for '+': 'Number Field in c1 with
defining polynomial x^2 + x + 1' and 'Number Field in c2 with defining
polynomial x^2 + 1'
sage: from sage.categories.pushout import pushout
sage: pushout(M1['x'],M2['x'])
Traceback (most recent call last):
...
CoercionException: No common base
}}}
Now: Note that we will only have a pushout if the codomains of the
embeddings are number fields. Hence, in the second example, we won't use
`CDF` as a pushout.
{{{
sage: P.<x> = QQ[]
sage: L.<b> = NumberField(x^8-x^4+1, embedding=CDF.0)
sage: M1.<c1> = NumberField(x^2+x+1, embedding=b^4-1)
sage: M2.<c2> = NumberField(x^2+1, embedding=-b^6)
sage: M1.coerce_map_from(M2)
sage: M2.coerce_map_from(M1)
sage: c1+c2; parent(c1+c2) #indirect doctest
-b^6 + b^4 - 1
Number Field in b with defining polynomial x^8 - x^4 + 1
sage: from sage.categories.pushout import pushout
sage: pushout(M1['x'],M2['x'])
Univariate Polynomial Ring in x over Number Field in b with defining
polynomial x^8 - x^4 + 1
sage: K.<a> = NumberField(x^3-2, embedding=CDF(1/2*I*2^(1/3)*sqrt(3) -
1/2*2^(1/3)))
sage: L.<b> = NumberField(x^6-2, embedding=1.1)
sage: L.coerce_map_from(K)
sage: K.coerce_map_from(L)
sage: pushout(K,L)
Traceback (most recent call last):
...
CoercionException: ('Ambiguous Base Extension', Number Field in a with
defining polynomial x^3 - 2, Number Field in b with defining polynomial
x^6 - 2)
}}}
__Comparison of fractional ideals / identity of Residue Fields__
Fractional ideals have a `__cmp__` method that only took into account
the Hermite normal form. Originally, the comparison of fractional ideals
by "==" and by "cmp" yields different results. Since "==" of fractional
ideals is used for caching residue fields, but "cmp" was used for
comparing residue fields, the residue fields did not provide unique
parents.
Was:
{{{
sage: L.<b> = NumberField(x^8-x^4+1)
sage: F_2 = L.fractional_ideal(b^2-1)
sage: F_4 = L.fractional_ideal(b^4-1)
sage: F_2==F_4
True
sage: K.<r4> = NumberField(x^4-2)
sage: L.<r4> = NumberField(x^4-2, embedding=CDF.0)
sage: FK = K.fractional_ideal(K.0)
sage: FL = L.fractional_ideal(L.0)
sage: FK != FL
True
sage: RL = ResidueField(FL)
sage: RK = ResidueField(FK)
sage: RK is RL
False
sage: RK == RL
True
}}}
Now:
{{{
sage: L.<b> = NumberField(x^8-x^4+1)
sage: F_2 = L.fractional_ideal(b^2-1)
sage: F_4 = L.fractional_ideal(b^4-1)
sage: F_2==F_4
True
sage: K.<r4> = NumberField(x^4-2)
sage: L.<r4> = NumberField(x^4-2, embedding=CDF.0)
sage: FK = K.fractional_ideal(K.0)
sage: FL = L.fractional_ideal(L.0)
sage: FK != FL
True
sage: RL = ResidueField(FL)
sage: RK = ResidueField(FK)
sage: RK is RL
False
sage: RK == RL
False
}}}
Since `RL` is defined with the embedded field `L`, not with the
unembedded `K`, there is no coercion from the order of `K` to `RL`.
However, ''conversion'' works (this used to fail!):
{{{
sage: OK = K.maximal_order()
sage: RL.has_coerce_map_from(OK)
False
sage: RL(OK.1)
0
}}}
Note that I also had to change some arithmetic stuff in the `_tate`
method of elliptic curves: The old implementation relied on the assumption
that fractional ideals in an embedded field and in a non-embedded field
can't be equal. This assumption should still hold (since we do not
introduce forgetful coercion), but I think it is OK to keep the change in
_tate.
'''Apply:'''
1. [attachment:8800_functor_pushout_doc_and_fixes.patch]
2. [attachment:referee.patch]
'''Dependencies:''' #10677, #2329.
--
--
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/8800#comment:88>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica,
and MATLAB
--
You received this message because you are subscribed to the Google Groups
"sage-trac" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/sage-trac?hl=en.