Ticket #7643: trac_7643_revised.patch

File trac_7643_revised.patch, 31.9 KB (added by Francis Clarke, 13 years ago)

replaces earlier patch, based on 4.3

  • sage/categories/map.pyx

    # HG changeset patch
    # User Francis Clarke <F.Clarke@Swansea.ac.uk>
    # Date 1263670241 0
    # Node ID 9138763245dd5abeb0204bbe2788f36f3e727eea
    # Parent  21efb0b3fc474972b5c7f617d99173536a3d79d0
    Improved composita
    
    diff -r 21efb0b3fc47 -r 9138763245dd sage/categories/map.pyx
    a b  
    459459                      Ring endomorphism of Univariate Polynomial Ring in x over Integer Ring
    460460                      Defn: x |--> x + 1
    461461
     462            sage: S.<y> = QQ[]
     463            sage: psi = R.hom([y^2])
     464            sage: psi^1
     465            Ring morphism:
     466              From: Univariate Polynomial Ring in x over Integer Ring
     467              To:   Univariate Polynomial Ring in y over Rational Field
     468              Defn: x |--> y^2
     469            sage: psi^2
     470            Traceback (most recent call last):
     471            ...
     472            TypeError: self must be an endomorphism.
     473
     474            sage: K.<a> = NumberField(x^4 - 5*x + 5)
     475            sage: C5.<z> = CyclotomicField(5)
     476            sage: tau = K.hom([z - z^2]); tau
     477            Ring morphism:
     478              From: Number Field in a with defining polynomial x^4 - 5*x + 5
     479              To:   Cyclotomic Field of order 5 and degree 4
     480              Defn: a |--> -z^2 + z
     481            sage: tau^-1
     482            Ring morphism:
     483              From: Cyclotomic Field of order 5 and degree 4
     484              To:   Number Field in a with defining polynomial x^4 - 5*x + 5
     485              Defn: z |--> 3/11*a^3 + 4/11*a^2 + 9/11*a - 14/11
     486
    462487        """
    463         if self._domain is not self._codomain:
     488        if self._domain is not self._codomain and n != 1 and n != -1:
    464489            raise TypeError, "self must be an endomorphism."
    465490        if n == 0:
    466491            from sage.categories.morphism import IdentityMorphism
  • sage/modules/matrix_morphism.py

    diff -r 21efb0b3fc47 -r 9138763245dd sage/modules/matrix_morphism.py
    a b  
    155155            return C(v)
    156156        return C(C.linear_combination_of_basis(v), check=False)
    157157
     158    def _call_(self, x):
     159        """
     160        Alternative for compatibility with sage.categories.map.FormalCompositeMap._call_
     161        """
     162        return self.__call__(x)
     163
    158164    def __invert__(self):
    159165        """
    160166        Invert this matrix morphism.
     
    264270            [ 20  23  26  29]
    265271            [ 56  68  80  92]
    266272            [ 92 113 134 155]
     273
     274        Composite maps can be formed with matrix morphisms::
     275
     276            sage: K.<a> = NumberField(x^2 + 23)
     277            sage: V, VtoK, KtoV = K.vector_space()
     278            sage: f = V.hom([V.0 - V.1, V.0 + V.1])*KtoV; f
     279            Composite map:
     280              From: Number Field in a with defining polynomial x^2 + 23
     281              To:   Vector space of dimension 2 over Rational Field
     282              Defn:   Isomorphism map:
     283                      From: Number Field in a with defining polynomial x^2 + 23
     284                      To:   Vector space of dimension 2 over Rational Field
     285                    then
     286                      Free module morphism defined by the matrix
     287                    [ 1 -1]
     288                    [ 1  1]
     289                    Domain: Vector space of dimension 2 over Rational Field
     290                    Codomain: Vector space of dimension 2 over Rational Field
     291            sage: f(a)
     292            (1, 1)
    267293        """
    268294        if not isinstance(right, MatrixMorphism):
     295            if isinstance(right, (sage.categories.morphism.Morphism, sage.categories.map.Map)):
     296                return sage.categories.map.Map.__mul__(self, right)
    269297            R = self.base_ring()
    270298            return self.parent()(self.matrix() * R(right))
    271299        if self.domain() != right.codomain():
  • sage/rings/number_field/morphism.py

    diff -r 21efb0b3fc47 -r 9138763245dd sage/rings/number_field/morphism.py
    a b  
    129129class NumberFieldHomomorphism_im_gens(RingHomomorphism_im_gens):
    130130    def __invert__(self):
    131131        r"""
     132
     133        Return the inverse of an isomorphism of absolute number fields
     134
    132135        EXAMPLES::
    133136
    134137            sage: K.<a> = NumberField(x^2 + 5)
     
    161164              Defn: z |--> z^3,
    162165             Ring endomorphism of Cyclotomic Field of order 5 and degree 4
    163166              Defn: z |--> z^2)
     167
     168             sage: M.<w> = NumberField(x^4 - 5*x + 5)
     169             sage: phi = M.hom([z - z^2]); phi
     170             Ring morphism:
     171               From: Number Field in w with defining polynomial x^4 - 5*x + 5
     172               To:   Cyclotomic Field of order 5 and degree 4
     173               Defn: w |--> -z^2 + z
     174             sage: phi^-1
     175             Ring morphism:
     176               From: Cyclotomic Field of order 5 and degree 4
     177               To:   Number Field in w with defining polynomial x^4 - 5*x + 5
     178               Defn: z |--> 3/11*w^3 + 4/11*w^2 + 9/11*w - 14/11
     179             
    164180        """
    165         if not self.is_endomorphism():
    166             raise TypeError, "Can only invert endomorphisms"
    167181        K = self.domain()
    168         V, V_into_K, K_into_V = K.relative_vector_space()
     182        L = self.codomain()
     183        if K.degree() != L.degree():
     184            raise TypeError, "Can only invert isomorphisms"
     185        V, V_into_K, _ = K.vector_space()
     186        _, _, L_into_W = L.vector_space()
     187        linear_inverse = ~V.hom(map(L_into_W*self*V_into_K, V.basis()))
     188        return L.hom(map(V_into_K*linear_inverse*L_into_W, [L.gen()]))
    169189
    170         from sage.all import matrix
    171         M = matrix([ K_into_V(self(x)) for x in K.power_basis() ]).transpose()
    172         inv_gen = V_into_K((~M) * K_into_V(K.gen()))
    173         inv = K.hom([inv_gen])
    174 
    175         assert inv(self(K.gen())) == K.gen()
    176         assert self(inv(K.gen())) == K.gen()
    177         return inv
    178190
    179191class RelativeNumberFieldHomset(NumberFieldHomset):
    180192    """
  • sage/rings/number_field/number_field.py

    diff -r 21efb0b3fc47 -r 9138763245dd sage/rings/number_field/number_field.py
    a b  
    166166    from sage.categories.fields import Fields
    167167    return codomain in Fields()
    168168
     169from sage.rings.number_field.morphism import RelativeNumberFieldHomomorphism_from_abs
     170
    169171def proof_flag(t):
    170172    """
    171173    Used for easily determining the correct proof flag to use.
     
    25432545    def composite_fields(self, other, names=None, both_maps=False, preserve_embedding=True):
    25442546        """
    25452547        List of all possible composite number fields formed from self and
    2546         other, as well as possibly embeddings into the compositum.  See the
    2547         documentation for both_maps below.
    2548 
    2549         If preserve_embedding is ``True`` and if self and other *both* have
    2550         embeddings into the same ambient field, only compositums respecting
    2551         both embeddings are returned.  If one (or both) of self or other does
    2552         not have an embedding, or they do not have embeddings into the same
    2553         ambient field, or preserve_embedding is not ``True`` all possible
    2554         composite number fields are returned.
     2548        other, together with (optionally) embeddings into the compositum;
     2549        see the documentation for both_maps below.
     2550
     2551        If preserve_embedding is True and if self and other both have
     2552        embeddings into the same ambient field, or into fields which are
     2553        contained in a common field, only the compositum respecting
     2554        both embeddings is returned.  If one (or both) of self or other
     2555        does not have an embedding or preserve_embedding is False,
     2556        all possible composite number fields are returned.
    25552557
    25562558        INPUT:
    25572559           
     
    25592561
    25602562        - ``names`` - generator name for composite fields
    25612563
    2562         - ``both_maps`` - (default: False)  if True, return quadruples (F,
    2563           self_into_F, other_into_F, k) such that self_into_F maps self into
    2564           F, other_into_F maps other into F, and k is an integer such that
    2565           other_into_F(other.gen()) + k*self_into_F(self.gen()) == F.gen()
    2566 
    2567         - ``preserve_embedding`` - (default: True)  return only compositums with
    2568           compatible ambient embeddings.
     2564        - ``both_maps`` - (default: False)  if True, return quadruples
     2565          (F, self_into_F, other_into_F, k) such that self_into_F is an
     2566          embedding of self in F, other_into_F is an embedding of in F,
     2567          and k is an integer such that F.gen() equals
     2568          other_into_F(other.gen()) + k*self_into_F(self.gen())
     2569          or has the value Infinity in which case F.gen() equals
     2570          self_into_F(self.gen()), or is None (which happens when other is a
     2571          relative number field).
     2572          If both self and other have embeddings into an ambient field, then
     2573          F will have an embedding with respect to which both self_into_F
     2574          and other_into_F will be compatible with the ambient embeddings.
     2575
     2576        - ``preserve_embedding`` - (default: True) if self and other have
     2577          ambient embeddings, then return only the compatible compositum.
    25692578       
    25702579        OUTPUT:
    25712580       
    2572        
    25732581        -  ``list`` - list of the composite fields, possibly with maps.
    25742582       
    25752583       
     
    25772585       
    25782586            sage: K.<a> = NumberField(x^4 - 2)
    25792587            sage: K.composite_fields(K)
    2580             [Number Field in a0 with defining polynomial x^4 - 162,
    2581              Number Field in a1 with defining polynomial x^4 - 2,
    2582              Number Field in a2 with defining polynomial x^8 + 28*x^4 + 2500]
    2583             sage: k.<a> = NumberField(x^3 + 2)
    2584             sage: m.<b> = NumberField(x^3 + 2)
    2585             sage: k.composite_fields(m, 'c')
    2586             [Number Field in c0 with defining polynomial x^3 - 2,
    2587              Number Field in c1 with defining polynomial x^6 - 40*x^3 + 1372]
    2588 
    2589         Let's get the maps as well::
    2590 
    2591             sage: Q1.<a> = NumberField(x^2 + 2, 'b').extension(x^2 + 3, 'c').absolute_field()
    2592             sage: Q2.<b> = NumberField(x^2 + 3, 'a').extension(x^2 + 5, 'c').absolute_field()
    2593             sage: Q1.composite_fields(Q2)
    2594             [Number Field in ab0 with defining polynomial x^8 + 64*x^6 + 904*x^4 + 3840*x^2 + 3600,
    2595              Number Field in ab1 with defining polynomial x^8 + 160*x^6 + 6472*x^4 + 74880*x^2 + 1296]
    2596 
    2597             sage: F, Q1_into_F, Q2_into_F, k = Q1.composite_fields(Q2, both_maps=True)[0]
    2598             sage: F
    2599             Number Field in ab0 with defining polynomial x^8 + 64*x^6 + 904*x^4 + 3840*x^2 + 3600
     2588            [Number Field in a with defining polynomial x^4 - 2,
     2589             Number Field in a0 with defining polynomial x^8 + 28*x^4 + 2500]
     2590       
     2591        A particular compositum is selected, together with compatible maps
     2592        into the compositum, if the fields are endowed with a real or
     2593        complex embedding::
     2594
     2595            sage: K1 = NumberField(x^4 - 2, 'a', embedding=RR(2^(1/4)))
     2596            sage: K2 = NumberField(x^4 - 2, 'a', embedding=RR(-2^(1/4)))
     2597            sage: K1.composite_fields(K2)
     2598            [Number Field in a with defining polynomial x^4 - 2]
     2599            sage: [F, f, g, k], = K1.composite_fields(K2, both_maps=True); F
     2600            Number Field in a with defining polynomial x^4 - 2
     2601            sage: f(K1.0), g(K2.0)
     2602            (a, -a)
     2603
     2604        With preserve_embedding set to False, the embeddings are ignored::
     2605
     2606            sage: K1.composite_fields(K2, preserve_embedding=False)
     2607            [Number Field in a with defining polynomial x^4 - 2,
     2608             Number Field in a0 with defining polynomial x^8 + 28*x^4 + 2500]
     2609
     2610        Changing the embedding selects a different compositum::
     2611
     2612            sage: K3 = NumberField(x^4 - 2, 'a', embedding=CC(2^(1/4)*I))
     2613            sage: [F, f, g, k], = K1.composite_fields(K3, both_maps=True); F
     2614            Number Field in a0 with defining polynomial x^8 + 28*x^4 + 2500
     2615            sage: f(K1.0), g(K3.0)
     2616            (1/240*a0^5 - 41/120*a0, 1/120*a0^5 + 19/60*a0)
     2617
     2618        If no embeddings are specified, the maps into the composite are chosen arbitrarily::
     2619
     2620            sage: Q1.<a> = NumberField(x^4 + 10*x^2 + 1)
     2621            sage: Q2.<b> = NumberField(x^4 + 16*x^2 + 4)
     2622            sage: Q1.composite_fields(Q2, 'c')
     2623            [Number Field in c with defining polynomial x^8 + 64*x^6 + 904*x^4 + 3840*x^2 + 3600]
     2624            sage: F, Q1_into_F, Q2_into_F, k = Q1.composite_fields(Q2, 'c', both_maps=True)[0]
    26002625            sage: Q1_into_F
    26012626            Ring morphism:
    26022627              From: Number Field in a with defining polynomial x^4 + 10*x^2 + 1
    2603               To:   Number Field in ab0 with defining polynomial x^8 + 64*x^6 + 904*x^4 + 3840*x^2 + 3600
    2604               Defn: a |--> 19/14400*ab0^7 + 137/1800*ab0^5 + 2599/3600*ab0^3 + 8/15*ab0
    2605             sage: Q2_into_F
    2606             Ring morphism:
    2607               From: Number Field in b with defining polynomial x^4 + 16*x^2 + 4
    2608               To:   Number Field in ab0 with defining polynomial x^8 + 64*x^6 + 904*x^4 + 3840*x^2 + 3600
    2609               Defn: b |--> 19/7200*ab0^7 + 137/900*ab0^5 + 2599/1800*ab0^3 + 31/15*ab0
    2610 
    2611             sage: Q1_into_F.domain() is Q1
    2612             True
    2613             sage: Q2_into_F(b) + k*Q1_into_F(a) == F.gen()
    2614             True
    2615 
    2616         Let's check something about the "other" composite field::
    2617 
    2618             sage: F, Q1_into_F, Q2_into_F, k = Q1.composite_fields(Q2, both_maps=True)[1]
    2619             sage: Q1_into_F.domain() is Q1
    2620             True
    2621             sage: Q2_into_F(b) + k*Q1_into_F(a) == F.gen()
    2622             True
     2628              To:   Number Field in c with defining polynomial x^8 + 64*x^6 + 904*x^4 + 3840*x^2 + 3600
     2629              Defn: a |--> 19/14400*c^7 + 137/1800*c^5 + 2599/3600*c^3 + 8/15*c
     2630
     2631        This is just one of four embeddings of Q1 into F::
     2632            sage: Hom(Q1, F).order()
     2633            4
    26232634
    26242635        TESTS:
    26252636
     
    26292640            sage: K0.<b> = CyclotomicField(7, 'a').subfields(3)[0][0].change_names()
    26302641            sage: K1.<a1> = K0.extension(x^2 - 2*b^2, 'a1').absolute_field()
    26312642            sage: K2.<a2> = K0.extension(x^2 - 3*b^2, 'a2').absolute_field()
    2632             sage: K1
    2633             Number Field in a1 with defining polynomial x^6 - 10*x^4 + 24*x^2 - 8
    2634             sage: K2
    2635             Number Field in a2 with defining polynomial x^6 - 15*x^4 + 54*x^2 - 27
    2636             sage: K1.is_isomorphic(K2)
    2637             False
    26382643
    26392644        We need embeddings, so we redefine::
    26402645
    26412646            sage: L1.<a1> = NumberField(K1.polynomial(), 'a1', embedding=CC.0)
    2642             sage: CDF(a1)
    2643             -0.629384245426
    26442647            sage: L2.<a2> = NumberField(K2.polynomial(), 'a2', embedding=CC.0)
    2645             sage: CDF(a2)
    2646             -0.77083512672
     2648            sage: [CDF(a1), CDF(a2)]
     2649            [-0.629384245426, -0.77083512672]
     2650
     2651        and we get the same embeddings via the compositum::
    26472652
    26482653            sage: F, L1_into_F, L2_into_F, k = L1.composite_fields(L2, both_maps=True)[0]
    2649             sage: CDF(F.gen())
    2650             -0.141450881294
    2651 
    2652         Both subfield generators have correct embeddings::
    2653 
    2654             sage: CDF(L1_into_F(L1.gen())), CDF(L1.gen())
    2655             (-0.629384245426, -0.629384245426)
    2656             sage: CDF(L2_into_F(L2.gen())), CDF(L2.gen())
    2657             (-0.77083512672, -0.77083512672)
    2658 
    2659         On the other hand, without embeddings, there are more composite fields::
    2660 
    2661             sage: M1.<a1> = NumberField(L1.polynomial(), 'a1')
    2662             sage: M2.<a2> = NumberField(L2.polynomial(), 'a2')
    2663             sage: M1.composite_fields(M2)
    2664             [Number Field in a1a20 with defining polynomial x^12 - 50*x^10 + 613*x^8 - 1270*x^6 + 526*x^4 - 60*x^2 + 1,
    2665              Number Field in a1a21 with defining polynomial x^12 - 50*x^10 + 865*x^8 - 6730*x^6 + 24970*x^4 - 43152*x^2 + 27889,
    2666              Number Field in a1a22 with defining polynomial x^12 - 50*x^10 + 865*x^8 - 6310*x^6 + 18670*x^4 - 14928*x^2 + 1849]
    2667 
    2668         Here's another example::
    2669 
    2670             sage: Q1.<a> = NumberField(x^4 + 10*x^2 + 1, embedding=CC.0); Q1, CDF(a)
    2671             (Number Field in a with defining polynomial x^4 + 10*x^2 + 1, 0.317837245196*I)
    2672             sage: Q2.<b> = NumberField(x^4 + 16*x^2 + 4, embedding=CC.0); Q2, CDF(b)
    2673             (Number Field in b with defining polynomial x^4 + 16*x^2 + 4, 0.504017169931*I)
    2674 
    2675             sage: len(Q1.composite_fields(Q2))
    2676             1
    2677             sage: F, Q1_into_F, Q2_into_F, k2 = Q1.composite_fields(Q2, both_maps=True)[0]
    2678             sage: F, CDF(F.gen())
    2679             (Number Field in ab1 with defining polynomial x^8 + 160*x^6 + 6472*x^4 + 74880*x^2 + 1296,
    2680              -0.131657320461*I)
    2681 
    2682             sage: t = Q2_into_F(Q2.gen()) + k2*Q1_into_F(Q1.gen()); t, CDF(t)
    2683             (ab1, -0.131657320461*I)
    2684             sage: abs(t.minpoly()(CDF(t))) < 1e-8
    2685             True
    2686 
    2687         Let's check that the preserve_embedding flag is respected::
    2688        
    2689             sage: len(Q1.composite_fields(Q2))
    2690             1
    2691             sage: len(Q1.composite_fields(Q2, preserve_embedding=False))
    2692             2
     2654            sage: [CDF(L1_into_F(L1.gen())), CDF(L2_into_F(L2.gen()))]
     2655            [-0.629384245426, -0.77083512672]
    26932656
    26942657        Let's check that if only one field has an embedding, the resulting
    2695         fields do not have an embedding::
    2696        
    2697             sage: Q2.<b> = NumberField(x^4 + 16*x^2 + 4)
    2698             sage: Q2.coerce_embedding() is None
    2699             True
    2700 
    2701             sage: Q1.composite_fields(Q2)
    2702             [Number Field in ab0 with defining polynomial x^8 + 64*x^6 + 904*x^4 + 3840*x^2 + 3600,
    2703              Number Field in ab1 with defining polynomial x^8 + 160*x^6 + 6472*x^4 + 74880*x^2 + 1296]
    2704             sage: Q1.composite_fields(Q2)[0].coerce_embedding() is None
    2705             True
    2706         """
    2707         if names is None:
    2708             sv = self.variable_name(); ov = other.variable_name()
    2709             names = sv + (ov if ov != sv else "")
     2658        fields do not have embeddings::
     2659       
     2660            sage: L1.composite_fields(K2)[0].coerce_embedding() is None
     2661            True
     2662            sage: L2.composite_fields(K1)[0].coerce_embedding() is None
     2663            True
     2664
     2665        We check that other can be a relative number field::
     2666       
     2667            sage: L.<a, b> = NumberField([x^3 - 5, x^2 + 3])       
     2668            sage: CyclotomicField(3, 'w').composite_fields(L, both_maps=True)
     2669            [(Number Field in a with defining polynomial x^3 - 5 over its base field, Ring morphism:
     2670              From: Cyclotomic Field of order 3 and degree 2
     2671              To:   Number Field in a with defining polynomial x^3 - 5 over its base field
     2672              Defn: w |--> -1/2*b - 1/2, Relative number field endomorphism of Number Field in a with defining polynomial x^3 - 5 over its base field
     2673              Defn: a |--> a
     2674                    b |--> b, None)]
     2675        """
    27102676        if not isinstance(other, NumberField_generic):
    27112677            raise TypeError, "other must be a number field."
    2712         f = self.pari_polynomial()
    2713         g = other.pari_polynomial()
    2714 
    2715         R = self.absolute_polynomial().parent()
     2678
     2679        sv = self.variable_name(); ov = other.variable_name()
     2680        if names is None:
     2681            names = sv + (ov if ov != sv else "")
    27162682        name = sage.structure.parent_gens.normalize_names(1, names)[0]
    27172683
    27182684        # should we try to preserve embeddings?
     
    27222688        if other.coerce_embedding() is None:
    27232689            subfields_have_embeddings = False
    27242690        if subfields_have_embeddings:
    2725             if self.coerce_embedding().codomain() is not other.coerce_embedding().codomain():
     2691            try:
     2692                from sage.categories.pushout import pushout
     2693                ambient_field = pushout(self.coerce_embedding().codomain(), other.coerce_embedding().codomain())
     2694            except CoercionException:
     2695                ambient_field = None
     2696            if ambient_field is None:
    27262697                subfields_have_embeddings = False
    27272698
     2699        f = self.pari_polynomial()
     2700        g = other.pari_polynomial()
     2701        R = self.absolute_polynomial().parent()
     2702
     2703        m = self.degree()
     2704        n = other.absolute_degree()
     2705
    27282706        if not both_maps and not subfields_have_embeddings:
    27292707            # short cut!
    2730             C = map(R, f.polcompositum(g))
    2731             return [ NumberField(C[i], name + str(i)) for i in range(len(C)) ]
    2732 
    2733         # If flag = 1, outputs a vector of 4-component vectors [R, a, b,
    2734         # k], where R ranges through the list of all possible compositums
     2708            # eliminate duplicates from the fields given by polcompositum
     2709            # and return the resulting number fields.  There is no need to
     2710            # check that the polynomials are irreducible.
     2711            C = []
     2712            for r in f.polcompositum(g):
     2713                if not any(r.nfisisom(s) for s in C):
     2714                    C.append(r)
     2715            C = map(R, C)
     2716
     2717            q = sum(1 for r in C if r.degree() != max(m, n))
     2718            if q == 1 and name != sv and name != ov:
     2719                names =[name]
     2720            else:
     2721                names = [name + str(i) for i in range(q)]
     2722
     2723            i = 0
     2724            rets = []
     2725            for r in C:
     2726                if r.degree() == m:
     2727                    rets.append(self)
     2728                elif r.degree() == n:
     2729                    rets.append(other)
     2730                else:
     2731                    rets.append(NumberField(r, names[i], check=False))
     2732                    i += 1
     2733            return rets
     2734           
     2735        # If flag = 1, polcompositum outputs a vector of 4-component vectors
     2736        # [R, a, b, k], where R ranges through the list of all possible compositums
    27352737        # as above, and a (resp. b) expresses the root of P (resp. Q) as
    27362738        # an element of Q(X )/(R). Finally, k is a small integer such that
    2737         # b + ka = X modulo R.
    2738         C = f.polcompositum(g, 1)
    2739         rets = []
    2740 
    2741         embedding = None
     2739        # b + ka = X modulo R. 
     2740        # In this case duplicates must only be eliminated if embeddings are going
     2741        # to be preserved.
     2742        C = []
     2743        for v in f.polcompositum(g, 1):
     2744            if subfields_have_embeddings or not any(v[0].nfisisom(u[0]) for u in C):
     2745                C.append(v)
     2746       
    27422747        a = self.gen()
    27432748        b = other.gen()
    2744         for i in range(len(C)):
    2745             r, a_in_F, b_in_F, k = C[i]
     2749
     2750        # If both subfields are provided with embeddings, then we must select
     2751        # the compositum which corresponds to these embeddings.  We do this by
     2752        # evaluating the given polynomials at the corresponding embedded values.
     2753        # For the case we want, the result will be zero, but rounding errors are
     2754        # difficult to predict, so we just take the field which yields the
     2755        # mimumum value.
     2756        if subfields_have_embeddings:
     2757            poly_vals = []
     2758            for r, _, _, k in C:
     2759                r = R(r)
     2760                k = ZZ(k) # essential
     2761                embedding = other.coerce_embedding()(b) + k*self.coerce_embedding()(a)
     2762                poly_vals.append(sage.rings.complex_double.CDF(r(embedding)).abs())
     2763            i = poly_vals.index(min(poly_vals))
     2764            C = [C[i]]
     2765
     2766        q = sum(1 for r, _, _, _ in C if R(r).degree() != max(m, n))
     2767        if q == 1 and name != sv and name != ov:
     2768            names =[name, '']
     2769        else:
     2770            names = [name + str(i) for i in range(q + 1)]
     2771
     2772        if both_maps and not other.is_absolute():
     2773            other_abs = other.absolute_field('z')
     2774            from_other_abs, to_other_abs = other_abs.structure()
     2775
     2776        embedding = None
     2777        i = 0
     2778        rets = []
     2779        for r, a_in_F, b_in_F, k in C:
    27462780            r = R(r)
    2747             k = ZZ(k)
    2748 
    2749             if subfields_have_embeddings:
    2750                 embedding = other.coerce_embedding()(b) + k*self.coerce_embedding()(a)
    2751                 if r(embedding) > 1e-30: # XXX how to do this more generally?
    2752                     continue
    2753 
    2754             F = NumberField(r, name + str(i), embedding=embedding)
    2755             a_in_F = F(a_in_F)
    2756             b_in_F = F(b_in_F)
    2757             into_F1 = self.hom ([a_in_F], check=True)
    2758             into_F2 = other.hom([b_in_F], check=True)
    2759             assert into_F2(b) + k*into_F1(a) == F.gen()
    2760             rets.append( (F, into_F1, into_F2, k) )
    2761 
    2762         if not both_maps:
    2763             return [ F for F, _, _, _ in rets ]
    2764         else:
    2765             return rets
     2781            if r.degree() == m and not both_maps:
     2782                rets.append(self)
     2783            elif r.degree() == n and not both_maps:
     2784                rets.append(other)
     2785            else:
     2786                k = ZZ(k) # essential
     2787           
     2788                if subfields_have_embeddings:
     2789                    embedding = other.coerce_embedding()(b) + k*self.coerce_embedding()(a)
     2790                F = NumberField(r, names[i], check=False, embedding=embedding)
     2791                i += 1
     2792                if both_maps:
     2793                    if other.is_absolute():
     2794                        if r.degree() == m:
     2795                            self_to_F = self.hom([a])
     2796                            other_to_F = other.hom([(~self.hom([F(a_in_F)]))(F(b_in_F))])
     2797                            F = self
     2798                            k = sage.rings.infinity.Infinity
     2799                            i -= 1
     2800                        elif r.degree() == n:
     2801                            other_to_F = other.hom([b])
     2802                            self_to_F = self.hom([(~other.hom([F(b_in_F)]))(F(a_in_F))])
     2803                            F = other
     2804                            k = ZZ(0)
     2805                            i -= 1
     2806                        else:
     2807                            self_to_F = self.hom([F(a_in_F)])
     2808                            other_to_F = other.hom([F(b_in_F)])
     2809                    else:
     2810                        other_abs_to_F = other_abs.hom([F(b_in_F)])
     2811                        other_to_F = RelativeNumberFieldHomomorphism_from_abs(other.Hom(F), other_abs_to_F*to_other_abs)
     2812                        if r.degree() == m:
     2813                            self_to_F = self.hom([a])
     2814                            other_to_F = RelativeNumberFieldHomomorphism_from_abs(other.Hom(self), (~self.hom([F(a_in_F)]))*other_abs_to_F*to_other_abs)
     2815                            F = self
     2816                            k = None
     2817                            i -= 1
     2818                        elif r.degree() == n:
     2819                            other_to_F = RelativeNumberFieldHomomorphism_from_abs(other.Hom(other), from_other_abs)
     2820                            self_to_F = self.hom([from_other_abs((~other_abs_to_F)(F(a_in_F)))])
     2821                            F = other
     2822                            k = None
     2823                            i -= 1
     2824                        else:
     2825                            self_to_F = self.hom([F(a_in_F)])
     2826                            other_to_F = RelativeNumberFieldHomomorphism_from_abs(other.Hom(F), other_abs_to_F*to_other_abs)
     2827                    rets.append( (F, self_to_F, other_to_F, k) )
     2828                else:
     2829                    rets.append(F)
     2830        return rets
    27662831
    27672832    def absolute_degree(self):
    27682833        """
  • sage/rings/number_field/number_field_rel.py

    diff -r 21efb0b3fc47 -r 9138763245dd sage/rings/number_field/number_field_rel.py
    a b  
    511511            sage: K.<a,b> = NumberField([x^4 + 3, x^2 + 2]); K
    512512            Number Field in a with defining polynomial x^4 + 3 over its base field
    513513            sage: K.galois_closure('c')
    514             Number Field in c with defining polynomial x^16 + 144*x^14 + 8988*x^12 + 329616*x^10 + 7824006*x^8 + 113989680*x^6 + 1360354716*x^4 + 3470308272*x^2 + 9407642049
     514            Number Field in c with defining polynomial x^16 + 16*x^14 + 28*x^12 + 784*x^10 + 19846*x^8 - 595280*x^6 + 2744476*x^4 + 3212848*x^2 + 29953729
    515515        """
    516516        return self.absolute_field('a').galois_closure(names=names)
    517517
     518    def composite_fields(self, other, names=None, both_maps=False, preserve_embedding=True):
     519        """
     520        List of all possible composite number fields formed from self and
     521        other, together with (optionally) embeddings into the compositum;
     522        see the documentation for both_maps below.
     523
     524        Since relative fields do not have ambient embeddings,
     525        preserve_embedding has no effect.  In every case all possible
     526        composite number fields are returned.
     527
     528        INPUT:
     529           
     530        - ``other`` - a number field
     531
     532        - ``names`` - generator name for composite fields
     533
     534        - ``both_maps`` - (default: False)  if True, return quadruples
     535          (F, self_into_F, other_into_F, k) such that self_into_F maps self into
     536          F, other_into_F maps other into F.  For relative number fields k is
     537          always None. 
     538        - ``preserve_embedding`` - (default: True) has no effect, but is kept
     539          for compatibility with the absolute version of this function.  In every
     540          case the list of all possible compositums is returned.
     541       
     542        OUTPUT:
     543       
     544        -  ``list`` - list of the composite fields, possibly with maps.
     545       
     546       
     547        EXAMPLES::
     548       
     549            sage: K.<a, b> = NumberField([x^2 + 5, x^2 - 2])
     550            sage: L.<c, d> = NumberField([x^2 + 5, x^2 - 3])
     551            sage: K.composite_fields(L, 'e')
     552            [Number Field in e with defining polynomial x^8 - 24*x^6 + 464*x^4 + 3840*x^2 + 25600]
     553            sage: K.composite_fields(L, 'e', both_maps=True)
     554            [[Number Field in e with defining polynomial x^8 - 24*x^6 + 464*x^4 + 3840*x^2 + 25600,
     555              Relative number field morphism:
     556              From: Number Field in a with defining polynomial x^2 + 5 over its base field
     557             To:   Number Field in e with defining polynomial x^8 - 24*x^6 + 464*x^4 + 3840*x^2 + 25600
     558              Defn: a |--> -9/66560*e^7 + 11/4160*e^5 - 241/4160*e^3 - 101/104*e
     559                    b |--> -21/166400*e^7 + 73/20800*e^5 - 779/10400*e^3 + 7/260*e,
     560              Relative number field morphism:
     561              From: Number Field in c with defining polynomial x^2 + 5 over its base field
     562              To:   Number Field in e with defining polynomial x^8 - 24*x^6 + 464*x^4 + 3840*x^2 + 25600
     563              Defn: c |--> -9/66560*e^7 + 11/4160*e^5 - 241/4160*e^3 - 101/104*e
     564                    d |--> -3/25600*e^7 + 7/1600*e^5 - 147/1600*e^3 + 1/40*e,
     565              None]]
     566        """
     567        if not isinstance(other, NumberField_generic):
     568            raise TypeError, "other must be a number field."
     569        if names is None:
     570            sv = self.variable_name(); ov = other.variable_name()
     571            names = sv + (ov if ov != sv else "")
     572
     573        self_abs = self.absolute_field('w')
     574        abs_composites = self_abs.composite_fields(other, names=names, both_maps=both_maps)
     575
     576        m = self.absolute_degree()
     577
     578        if not both_maps:
     579            rets = []
     580            for F in abs_composites:
     581                if F.absolute_degree() == m:
     582                   F = self
     583                rets.append(F)
     584            return rets
     585
     586        from_self_abs, to_self_abs = self_abs.structure()
     587
     588        rets = []
     589        for F, self_abs_to_F, other_to_F, k in abs_composites:
     590            self_to_F = RelativeNumberFieldHomomorphism_from_abs(self.Hom(F), self_abs_to_F*to_self_abs)
     591            if F.absolute_degree() == m:
     592                if other.is_absolute():
     593                    other_to_F = other.hom([(from_self_abs*(~self_abs_to_F)*other_to_F)(other.gen())])
     594                else:
     595                    other_to_F = RelativeNumberFieldHomomorphism_from_abs(self.Hom(self), from_self_abs*(~self_abs_to_F)*other_to_F)
     596                self_to_F = RelativeNumberFieldHomomorphism_from_abs(self.Hom(self), from_self_abs)
     597                F = self
     598            rets.append([F, self_to_F, other_to_F, None])
     599        return rets
     600
    518601    def absolute_degree(self):
    519602        """
    520603        The degree of this relative number field over the rational field.