Changes between Version 1 and Version 35 of Ticket #8800


Ignore:
Timestamp:
12/06/10 10:48:31 (10 years ago)
Author:
SimonKing
Comment:

Legend:

Unmodified
Added
Removed
Modified
  • Ticket #8800

    • Property Status changed from new to needs_work
    • Property Summary changed from Doctest coverage of categories to Doctest coverage of categories - numerous coercion fixes
  • Ticket #8800 – Description

    v1 v35  
    88pushout.py: 24% (19 of 77)
    99}}}
    10 Trying to add doc tests, I actually found a bug:
    11 
    12 {{{
     10
     11The 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%'''.
     12
     13
     14However, 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.
     15
     16This 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.
     17
     181. Bug: Creating `ForgetfulFunctor` fails.
     19
     20  Was:
     21  {{{
    1322sage: abgrps = CommutativeAdditiveGroups()
    1423sage: ForgetfulFunctor(abgrps, abgrps)
     
    2130
    2231TypeError: IdentityFunctor() takes exactly one argument (2 given)
     32  }}}
     33
     34  Now:
     35  {{{
     36sage: abgrps = CommutativeAdditiveGroups()
     37sage: ForgetfulFunctor(abgrps, abgrps)
     38The identity functor on Category of commutative additive groups
     39  }}}
     40
     412. Bug: Applying `ForgetfulFunctor` returns `None`.
     42
     43  Was:
     44  {{{
     45sage: fields = Fields()
     46sage: rings = Rings()
     47sage: F = ForgetfulFunctor(fields,rings)
     48sage: F(QQ)
     49  }}}
     50
     51  Now:
     52  {{{
     53sage: fields = Fields()
     54sage: rings = Rings()
     55sage: F = ForgetfulFunctor(fields,rings)
     56sage: F(QQ)
     57Rational Field
     58  }}}
     59
     603. Bug: Applying a functor does not complain if the argument is not contained in the domain.
     61
     62  Was:
     63  {{{
     64sage: fields = Fields()
     65sage: rings = Rings()
     66sage: F = ForgetfulFunctor(fields,rings)
     67# Yields None, see previous bug
     68sage: F(ZZ['x','y'])
     69  }}}
     70
     71  Now:
     72  {{{
     73sage: fields = Fields()
     74sage: rings = Rings()
     75sage: F = ForgetfulFunctor(fields,rings)
     76sage: F(ZZ['x','y'])
     77Traceback (most recent call last):
     78...
     79TypeError: x (=Multivariate Polynomial Ring in x, y over Integer Ring) is not in Category of fields
     80  }}}
     81
     824. Bug: Comparing identity functor with any functor only checks domain and codomain
     83
     84  Was:
     85  {{{
     86sage: F = QQ['x'].construction()[0]
     87sage: F
     88Poly[x]
     89sage: F == IdentityFunctor(Rings())
     90False
     91sage: IdentityFunctor(Rings()) == F
     92True
     93  }}}
     94
     95  Now:
     96  {{{
     97sage: F = QQ['x'].construction()[0]
     98sage: F
     99Poly[x]
     100sage: F == IdentityFunctor(Rings())
     101False
     102sage: IdentityFunctor(Rings()) == F
     103False
     104  }}}
     105
     1065. Bug: Comparing identity functor with anything that is not a functor produces an error
     107
     108  Was:
     109  {{{
     110sage: IdentityFunctor(Rings()) == QQ
     111Traceback (most recent call last):
     112...
     113AttributeError: 'RationalField_with_category' object has no attribute 'domain'
     114  }}}
     115
     116  Now:
     117  {{{
     118sage: IdentityFunctor(Rings()) == QQ
     119False
     120  }}}
     121
     1226. Bug: The matrix functor is ill defined; moreover, ill-definedness does not result in an error.
     123
     124  Was:
     125  {{{
     126sage: F = MatrixSpace(ZZ,2,3).construction()[0]
     127sage: F(RR) in F.codomain()
     128False
     129# The codomain is wrong for non-square matrices!
     130sage: F.codomain()
     131Category of rings
     132  }}}
     133
     134  Now:
     135  {{{
     136sage: F = MatrixSpace(ZZ,2,3).construction()[0]
     137sage: F.codomain()
     138Category of commutative additive groups
     139sage: F(RR) in F.codomain()
     140True
     141sage: F = MatrixSpace(ZZ,2,2).construction()[0]
     142sage: F.codomain()
     143Category of rings
     144sage: F(RR) in F.codomain()
     145True
     146  }}}
     147
     1487. Bug: Wrong domain for `VectorFunctor`; and again, functors don't test if the domain is appropriate
     149
     150  Was:
     151  {{{
     152sage: F = FreeModule(ZZ,3).construction()[0]
     153sage: F
     154VectorFunctor
     155sage: F.domain()
     156Category of objects
     157sage: F.codomain()
     158Category of objects
     159sage: Set([1,2,3]) in F.domain()
     160True
     161sage: F(Set([1,2,3]))
     162Traceback (most recent call last):
     163...
     164AttributeError: 'Set_object_enumerated' object has no attribute 'is_commutative'
     165  }}}
     166
     167  Now:
     168  {{{
     169sage: F = FreeModule(ZZ,3).construction()[0]
     170sage: F
     171VectorFunctor
     172sage: F.domain()
     173Category of commutative rings
     174sage: Set([1,2,3]) in F.domain()
     175False
     176sage: F(Set([1,2,3]))
     177Traceback (most recent call last):
     178...
     179TypeError: x (={1, 2, 3}) is not in Category of commutative rings
     180  }}}
     181
     1828. Bug: `BlackBoxConstructionFunctor` is completely unusable
     183
     184`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`.
     185
     186  The following did not work at all:
     187  {{{
     188sage: from sage.categories.pushout import BlackBoxConstructionFunctor
     189sage: FG = BlackBoxConstructionFunctor(gap)
     190sage: FS = BlackBoxConstructionFunctor(singular)
     191sage: FG
     192BlackBoxConstructionFunctor
     193sage: FG(ZZ)
     194Integers
     195sage: FG(ZZ).parent()
     196Gap
     197sage: FS(QQ['t'])
     198//   characteristic : 0
     199//   number of vars : 1
     200//        block   1 : ordering lp
     201//                  : names    t
     202//        block   2 : ordering C
     203sage: FG == FS
     204False
     205sage: FG == loads(dumps(FG))
     206True
    23207}}}
    24 The forgetful functor should coincide with the identity functor, but inside ``ForgetfulFunctor``, the latter is called in the wrong way.
     208
     2099. Nitpicking: The `LocalizationFunctor` is nowhere used (yet)
     210
     211Hence, I removed it.
     212
     21310. Bug / New Feature: Make completion and and fraction field construction functors commute.
     214
     215The result of them not commuting is the following coercion bug.
     216
     217  Was:
     218  {{{
     219sage: R1.<x> = Zp(5)[]
     220sage: R2 = Qp(5)
     221sage: R2(1)+x
     222Traceback (most recent call last):
     223...
     224TypeError: 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'
     225  }}}
     226
     227  Now:
     228  {{{
     229sage: R1.<x> = Zp(5)[]
     230sage: R2 = Qp(5)
     231sage: R2(1)+x
     232(1 + O(5^20))*x + (1 + O(5^20))
     233  }}}
     234
     23511. New feature: Make the completion functor work on some objects that do not provide a completion method.
     236
     237The idea is to use that the completion functor may commute with the construction of the given argument. That may safe the day.
     238
     239  Was:
     240  {{{
     241sage: P.<x> = ZZ[]
     242sage: C = P.completion(x).construction()[0]
     243sage: R = FractionField(P)
     244sage: hasattr(R,'completion')
     245False
     246sage: C(R)
     247Traceback (most recent  call last):
     248...
     249AttributeError: 'FractionField_generic' object has no attribute 'completion'
     250  }}}
     251
     252  Now:
     253  {{{
     254sage: P.<x> = ZZ[]
     255sage: C = P.completion(x).construction()[0]
     256sage: R = FractionField(P)
     257sage: hasattr(R,'completion')
     258False
     259sage: C(R)
     260Fraction Field of Power Series Ring in x over Integer Ring
     261  }}}
     262
     26312. Bug / new feature: Coercion for free modules, taking into account a user-defined inner product
     264
     265  Was:
     266  {{{
     267sage: P.<t> = ZZ[]
     268sage: M1 = FreeModule(P,3)
     269sage: M2 = QQ^3
     270sage: M2([1,1/2,1/3]) + M1([t,t^2+t,3])     # This is ok
     271(t + 1, t^2 + t + 1/2, 10/3)
     272sage: M3 = FreeModule(P,3, inner_product_matrix = Matrix(3,3,range(9)))
     273sage: M2([1,1/2,1/3]) + M3([t,t^2+t,3])     # This is ok
     274(t + 1, t^2 + t + 1/2, 10/3)
     275# The user defined inner product matrix is lost! Bug
     276sage: parent(M2([1,1/2,1/3]) + M3([t,t^2+t,3])).inner_product_matrix()
     277[1 0 0]
     278[0 1 0]
     279[0 0 1]
     280  }}}
     281
     282  Now:
     283  {{{
     284sage: parent(M2([1,1/2,1/3]) + M3([t,t^2+t,3])).inner_product_matrix()
     285[0 1 2]
     286[3 4 5]
     287[6 7 8]
     288  }}}
     289
     290  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:
     291  {{{
     292sage: M4 = FreeModule(P,3, inner_product_matrix = Matrix(3,3,[1,1,1,0,1,1,0,0,1]))
     293sage: M3([t,1,t^2]) + M4([t,t^2+t,3])     # This should result in an error
     294(2*t, t^2 + t + 1, t^2 + 3)
     295  }}}
     296  Note that there should be no coercion between `M3` and `M4`, since they have different user-defined inner product matrices.
     297
     298
     29913. Bug / new feature: Quotient rings of univariate polynomial rings do not have a construction method.
     300
     301  Was:
     302  {{{
     303sage: P.<x> = QQ[]
     304sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
     305sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
     306sage: from sage.categories.pushout import pushout
     307sage: pushout(Q1,Q2)
     308Traceback (most recent call last):
     309...
     310CoercionException: No common base
     311  }}}
     312
     313  Now:
     314  {{{
     315sage: P.<x> = QQ[]
     316sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
     317sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
     318sage: from sage.categories.pushout import pushout
     319sage: pushout(Q1,Q2)
     320Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^4 + 2*x^2 + 1
     321  }}}
     322
     32314. Insufficient coercion of quotient rings, if one modulus divides the other
     324
     325  Was:
     326  {{{
     327sage: P5.<x> = GF(5)[]
     328sage: Q = P5.quo([(x^2+1)^2])
     329sage: P.<x> = ZZ[]
     330sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
     331sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
     332sage: Q.has_coerce_map_from(Q1)
     333False
     334  }}}
     335
     336  Now: There is a coercion from `Q1` to `Q`.
     337
     33815. Coercion of `GF(p)` versus `Integers(p)`
     339
     340I am not sure if this is really a bug.
     341
     342  Was:
     343  {{{
     344sage: from sage.categories.pushout import pushout
     345sage: pushout(GF(5), Integers(5))
     346Ring of integers modulo 5
     347  }}}
     348
     349  Now
     350  {{{
     351sage: from sage.categories.pushout import pushout
     352sage: pushout(GF(5), Integers(5))
     353Finite Field of size 5
     354  }}}
     355
     35616. Bug / new feature: Construction for QQbar was missing.
     357
     358  Now:
     359  {{{
     360sage: QQbar.construction()
     361(AlgebraicClosureFunctor, Rational Field)
     362  }}}
     363
     36417. Bug / new feature: Construction for number fields is missing.
     365
     366This 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
     367  * [http://groups.google.com/group/sage-nt/browse_thread/thread/32b65a5173f43267 Bidirectional coercions]
     368  * [http://groups.google.com/group/sage-nt/browse_thread/thread/5c376dbf7e99ea97 Coercions for number fields]
     369  * [http://groups.google.com/group/sage-nt/browse_thread/thread/54c1e33872d14334 Comparison of fractional ideals]
     370
     371__Coercion__
     372
     373  Was:
     374  {{{
     375sage: K.<r4> = NumberField(x^4-2)
     376sage: L1.<r2_1> = NumberField(x^2-2, embedding = r4**2)
     377sage: L2.<r2_2> = NumberField(x^2-2, embedding = -r4**2)
     378sage: r2_1+r2_2    # indirect doctest
     379ERROR: An unexpected error occurred while tokenizing input
     380The following traceback may be corrupted or invalid
     381The error message is: ('EOF in multi-line statement', (1109, 0))
     382
     383ERROR: An unexpected error occurred while tokenizing input
     384The following traceback may be corrupted or invalid
     385The error message is: ('EOF in multi-line statement', (1109, 0))
     386
     387...
     388sage: K.has_coerce_map_from(L1.maximal_order())
     389False   # that's the wrong direction.
     390sage: L1.has_coerce_map_from(K.maximal_order())
     391True
     392  }}}
     393
     394  Now:
     395  {{{
     396sage: K.<r4> = NumberField(x^4-2)
     397sage: L1.<r2_1> = NumberField(x^2-2, embedding = r4**2)
     398sage: L2.<r2_2> = NumberField(x^2-2, embedding = -r4**2)
     399sage: r2_1+r2_2    # indirect doctest
     4000
     401sage: (r2_1+r2_2).parent() is L1
     402True
     403sage: (r2_2+r2_1).parent() is L2
     404True
     405sage: K.has_coerce_map_from(L1.maximal_order())
     406True
     407sage: L1.has_coerce_map_from(K.maximal_order())
     408False
     409  }}}
     410
     411__Pushout__
     412
     413  Was:
     414  {{{
     415sage: P.<x> = QQ[]
     416sage: L.<b> = NumberField(x^8-x^4+1, embedding=CDF.0)
     417sage: M1.<c1> = NumberField(x^2+x+1, embedding=b^4-1)
     418sage: M2.<c2> = NumberField(x^2+1, embedding=-b^6)
     419sage: M1.coerce_map_from(M2)
     420sage: M2.coerce_map_from(M1)
     421sage: c1+c2; parent(c1+c2)    #indirect doctest
     422Traceback (most recent call last):
     423...
     424TypeError: 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'
     425sage: from sage.categories.pushout import pushout
     426sage: pushout(M1['x'],M2['x'])
     427Traceback (most recent call last):
     428...
     429CoercionException: No common base
     430  }}}
     431
     432  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.
     433  {{{
     434sage: P.<x> = QQ[]
     435sage: L.<b> = NumberField(x^8-x^4+1, embedding=CDF.0)
     436sage: M1.<c1> = NumberField(x^2+x+1, embedding=b^4-1)
     437sage: M2.<c2> = NumberField(x^2+1, embedding=-b^6)
     438sage: M1.coerce_map_from(M2)
     439sage: M2.coerce_map_from(M1)
     440sage: c1+c2; parent(c1+c2)    #indirect doctest
     441-b^6 + b^4 - 1
     442Number Field in b with defining polynomial x^8 - x^4 + 1
     443sage: from sage.categories.pushout import pushout
     444sage: pushout(M1['x'],M2['x'])
     445Univariate Polynomial Ring in x over Number Field in b with defining polynomial x^8 - x^4 + 1
     446sage: K.<a> = NumberField(x^3-2, embedding=CDF(1/2*I*2^(1/3)*sqrt(3) - 1/2*2^(1/3)))
     447sage: L.<b> = NumberField(x^6-2, embedding=1.1)
     448sage: L.coerce_map_from(K)
     449sage: K.coerce_map_from(L)
     450sage: pushout(K,L)
     451Traceback (most recent call last):
     452...
     453CoercionException: ('Ambiguous Base Extension', Number Field in a with defining polynomial x^3 - 2, Number Field in b with defining polynomial x^6 - 2)
     454}}}
     455
     456__Comparison of fractional ideals / identity of Residue Fields__
     457
     458  Fractional ideals have a `__cmp__` method that only took into account the Hermite normal form. In addition with coercion, we obtain:
     459  {{{
     460sage: L.<b> = NumberField(x^8-x^4+1)
     461sage: F_2 = L.fractional_ideal(b^2-1)
     462sage: F_4 = L.fractional_ideal(b^4-1)
     463sage: F_2==F_4
     464True
     465sage: K.<r4> = NumberField(x^4-2)
     466sage: L.<r4> = NumberField(x^4-2, embedding=CDF.0)
     467sage: FK = K.fractional_ideal(K.0)
     468sage: FL = L.fractional_ideal(L.0)
     469sage: FK == FL
     470True
     471  }}}
     472
     473  Since the residue fields of two equal fractional fields are the same (caching), we obtain:
     474  {{{
     475sage: RL = ResidueField(FL)
     476sage: RK = ResidueField(FK)
     477sage: RK is RL
     478True
     479  }}}
     480
     481  Thus, `RK` is in fact defined with the embedded field `L`, not with the unembedded `K`. Hence, there is no coercion from the order of `K` to `RK`. However, ''conversion'' works (this used to fail!):
     482
     483  {{{
     484sage: OK = K.maximal_order()
     485sage: RK.has_coerce_map_from(OK)
     486False
     487sage: RK(OK.1)
     4880
     489  }}}
     490
     491  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.