Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/fining/lib/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 27.6.2023 mit Größe 164 kB image not shown  

SSL morphisms.gi   Sprache: unbekannt

 
#############################################################################
##
##  morphisms.gi              FinInG package
##                                                              John Bamberg
##                                                              Anton Betten
##                                                              Jan De Beule
##                                                             Philippe Cara
##                                                            Michel Lavrauw
##                                                           Max Neunhoeffer
##
##  Copyright 2018 Colorado State University
##                  Sabancı Üniversitesi
##     Università degli Studi di Padova
##     Universiteit Gent
##     University of St. Andrews
##     University of Western Australia
##                  Vrije Universiteit Brussel
##
##
##  Implementation stuff for incidence geometry morphisms
##
#############################################################################


########################################
# 20/3/14
# cmat notice: in many operations we will compute
# a matrix to initialize a form. Forms can not handle cmats, so
# Unpacking the cmats will sometimes be necessary.
########################################


############################################################
## Generic constructor operations, not intended for the user.
############################################################

## The three methods for GeometryMorphismByFunction are analogous
## with the MappingByFunction function. It behaves in exactly the same
## way except that we return an IsGeometryMorphism.

# CHECKED 27/09/11 jdb
#############################################################################
#O  GeometryMorphismByFunction( <els1>, <els2>, <fun>, <bool>, <prefun> )
##
InstallMethod( GeometryMorphismByFunction, 
 "for two times a collection of elements of any type, a function, a boolean, and a function",
 [ IsAnyElementsOfIncidenceStructure, IsAnyElementsOfIncidenceStructure,
 IsFunction, IsBool, IsFunction ],
 function( els1, els2, fun, bool, prefun )
  local morphism;
  morphism := MappingByFunction(els1, els2, fun, bool, prefun );
  SetFilterObj( morphism, IsGeometryMorphism ); 
  return morphism;
 end );  
  
# CHECKED 27/09/11 jdb
#############################################################################
#O  GeometryMorphismByFunction( <els1>, <els2>, <fun>, <prefun> )
##
InstallMethod( GeometryMorphismByFunction,
 "for two times a collection of elements of any type, and two times a function",
 [ IsAnyElementsOfIncidenceStructure, IsAnyElementsOfIncidenceStructure,
    IsFunction, IsFunction ],
 function( els1, els2, fun, inv )
  local morphism;
  morphism := MappingByFunction(els1, els2, fun, inv );
  SetFilterObj( morphism, IsGeometryMorphism ); 
  return morphism;
 end );  

# CHECKED 27/09/11 jdb
#############################################################################
#O  GeometryMorphismByFunction( <els1>, <els2>, <fun> )
##
InstallMethod( GeometryMorphismByFunction,
 "for two times a collection of elements of any type, and a function",
 [ IsAnyElementsOfIncidenceStructure, IsAnyElementsOfIncidenceStructure,
    IsFunction ],
 function( els1, els2, fun )
  local morphism;
  morphism := MappingByFunction(els1, els2, fun);
  SetFilterObj( morphism, IsGeometryMorphism ); 
  return morphism;
 end );  

#############################################################################
# Display methods
#############################################################################

InstallMethod( ViewObj, 
 "for a geometry morphism",
 [ IsGeometryMorphism ],
 function( f )
  Print("<geometry morphism from "); 
  ViewObj(Source(f));
  Print( " to " );
  ViewObj(Range(f));
  Print(">");
 end );

InstallMethod( PrintObj,
 "for a geometry morphism",
 [ IsGeometryMorphism ],
 function( f )
  Print("Geometry morphism:\n ", f, "\n");
 end );

InstallMethod( Display, 
 "for a geometry morphism",
 [ IsGeometryMorphism ],
 function( f )
  Print("Geometry morphism: ", Source(f), " -> ", Range(f), "\n");
 end );

InstallMethod( ViewObj,
 "for a geometry morphism",
 [ IsGeometryMorphism and IsMappingByFunctionWithInverseRep ],
 function( f )
  Print("<geometry morphism from "); 
  ViewObj(Source(f));
  Print( " to " );
  ViewObj(Range(f));
  Print(">");
 end );

InstallMethod( ViewObj, 
 "for a geometry morphism",
 [ IsGeometryMorphism and IsMappingByFunctionRep ],
 function( f )
  Print("<geometry morphism from "); 
  ViewObj(Source(f));
  Print( " to " );
  ViewObj(Range(f));
  Print(">");
 end );

InstallMethod( PrintObj, 
 "for a geometry morphism",
 [ IsGeometryMorphism and IsMappingByFunctionRep ],
 function( f )
  Print("Geometry morphism:\n ", f, "\n");
 end );

InstallMethod( Display,
 "for a geometry morphism",
 [ IsGeometryMorphism and IsMappingByFunctionRep ],
 function( f )
  Print("Geometry morphism: ", Source(f), " -> ", Range(f), "\n");
 end );

############################################################
## Generic methods for the Image* operations for IsGeometryMorphism. 
############################################################

# CHECKED 27/09/11 jdb
#############################################################################
#O  ImageElm( <em>, <x> )
##
InstallOtherMethod( ImageElm, 
 "for a geometry morphism and an element of an incidence structure",
 [IsGeometryMorphism, IsElementOfIncidenceStructure],
 function(em, x)
  return em!.fun(x); 
 end );

# CHECKED 27/09/11 jdb
#############################################################################
#O  \^( <x>, <em> )
##
InstallOtherMethod( \^, 
 "for an element of an incidence structure and a geometry morphism",
 [IsElementOfIncidenceStructure, IsGeometryMorphism],
 function(x, em)
  return ImageElm(em,x);
 end );


# CHECKED 27/09/11 jdb
#############################################################################
#O  ImagesSet( <em>, <x> )
##
InstallOtherMethod( ImagesSet,
 "for a geometry morphism and a collection of elements of an incidence structure",
 [IsGeometryMorphism, IsElementOfIncidenceStructureCollection],
 function(em, x)
  return List(x, t -> em!.fun(t));
 end );


# CHECKED 27/09/11 jdb
#############################################################################
#O  PreImageElm( <em>, <x> )
##
InstallOtherMethod( PreImageElm,
 "for a geometry morphism and an element of an incidence structure",
 [IsGeometryMorphism, IsElementOfIncidenceStructure],
 function(em, x)
  if IsInjective(em) then
   return em!.prefun(x); 
  else
   Error("Map is not injective");
  fi;
 end );
  
# CHECKED 27/09/11 jdb
#############################################################################
#O  PreImagesSet( <em>, <x> )
##
InstallOtherMethod( PreImagesSet,
 "for a geometry morphism and an element of an incidence structure",
 [IsGeometryMorphism, IsElementOfIncidenceStructureCollection],
 function(em, x)
  return List(x, t -> em!.prefun(t)); 
 end );

##########################################################
## User methods for the "natural geometry morphisms"
##########################################################

## The specialised operations...

# CHECKED 14/12/11 jdb + ml (and added a check that basefields of <ps1> and <ps2> are equal.)
# cmat changed 21/3/14.
#############################################################################
#O  NaturalEmbeddingBySubspace( <ps1>, <ps2>, <v> ) returns a geometry morphism
# from the projective space <ps1> into <v>, an element of the projective space
# <ps2>. <v> must be of the right type, i.e. same projective dimension as <ps1>
# and <ps1> and <ps2> must be projective spaces over the same field.
##
InstallMethod( NaturalEmbeddingBySubspace, 
 "for a projective space into another, via a specified subspace",  
 [ IsProjectiveSpace, IsProjectiveSpace, IsSubspaceOfProjectiveSpace ],
 function( geom1, geom2, v ) 
  local d1, d2, rk, f, invbasis, basis, func, pre, map, morphism, bs;
  rk := v!.type;
  basis := Unpack(v!.obj); #cmat change
  d1 := geom1!.dimension + 1;
  d2 := geom2!.dimension + 1;
  f := geom2!.basefield;
  if not v in geom2 then
   Error("Subspace is not an element of ", geom2);
  fi;
  if d2 < d1 or d1 <> rk then
   Error("Dimensions are incompatible");
  fi;
  if not f = geom1!.basefield then
   Error("Basefields must be the same");
  fi;
    ##    To find the preimage, we first find an invertible matrix C such that
    ##    [a1,..,ad]B = [a1,...,ad,0,...,0]C (where B is our d x e matrix "basis") 
    ##    is our embedding. We simply extend B to a basis for geom2.
  bs := BaseSteinitzVectors(Basis(geom2!.vectorspace), basis);
  invbasis := Inverse(Concatenation(bs!.subspace, bs!.factorspace));
  #ConvertToMatrixRep(invbasis, f); #useless now.
  func := x -> VectorSpaceToElement(geom2 , Unpack(x!.obj) * basis); #VectorSpaceToElement makes sure the 2nd arg becomes cmat.
  pre := function(y)
   local newy;
   if not y in v then
    Error("Applying preimage to an element which is not in the range");
   fi;
   newy:= Unpack(y!.obj) * invbasis; #cmat change.
   if not IsMatrix(newy) then 
    newy := newy{[1..d1]};
                #ConvertToVectorRepNC(newy, f); #useless now.
   else
    newy := newy{[1..Size(newy)]}{[1..d1]};
    #ConvertToMatrixRepNC(newy, f); #useless now.
   fi;
   return VectorSpaceToElement(geom1, newy); #VectorSpaceToElement etc.
  end;   
  morphism := GeometryMorphismByFunction(ElementsOfIncidenceStructure(geom1), 
                                           ElementsOfIncidenceStructure(geom2), 
                                           func, false, pre );
  SetIsInjective( morphism, true );  
  return morphism;
 end );

# CHECKED 27/09/11 jdb + ml
# cmat changed 21/3/14.
#############################################################################
#O  NaturalEmbeddingBySubspaceNC( <ps1>, <ps2>, <v> ) 
## This operation is just like its namesake except that it 
## has no checks
##
InstallMethod( NaturalEmbeddingBySubspaceNC, 
 "for a projective space into another, via a specified subspace",  
 [ IsProjectiveSpace, IsProjectiveSpace, IsSubspaceOfProjectiveSpace ],
 function( geom1, geom2, v ) 
  local d1, f, invbasis, basis, func, pre, map, morphism, bs;
  basis := Unpack(v!.obj); #cmat change
  d1 := geom1!.dimension + 1;
  f := geom2!.basefield;
  bs := BaseSteinitzVectors(Basis(geom2!.vectorspace), basis);
  invbasis := Inverse(Concatenation(bs!.subspace, bs!.factorspace));
  # ConvertToMatrixRep(invbasis, f); #useless now.
  func := x -> VectorSpaceToElement(geom2 , x!.obj * basis); #VectorSpaceToElement etc.
  pre := function(y)
   local newy;
   newy:= Unpack(y!.obj) * invbasis; #cmat change
   if not IsMatrix(newy) then 
    newy := newy{[1..d1]}; 
    #ConvertToVectorRepNC(newy, f); useless now.
   else
    newy := newy{[1..Size(newy)]}{[1..d1]};
    #ConvertToMatrixRepNC(newy, f); useless now.
   fi;
   return VectorSpaceToElement(geom1, newy); #cfr. supra.
  end;   
  morphism := GeometryMorphismByFunction(ElementsOfIncidenceStructure(geom1), 
                                           ElementsOfIncidenceStructure(geom2), 
                                           func, false, pre );
  SetIsInjective( morphism, true );  
  return morphism;
 end );

# CHECKED 27/09/11 jdb + ml
# cmat version 20/3/14.
#############################################################################
#O  NaturalEmbeddingBySubspace( <ps1>, <ps2>, <v> ) returns a geometry morphism
# from the polar space <ps1> into <ps2>, a polar space induced as a section of
# <ps1> and <v>, the latter a subspace of the ambient projective space of <ps1>
##
InstallMethod( NaturalEmbeddingBySubspace, 
 "for a polar space into another, via a specified subspace",  
 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsSubspaceOfProjectiveSpace ], 
 function( geom1, geom2, v ) 
 local map, d1, d2, rk, f, i, basis, invbasis, bs, c1, c2, orth, tyv, quad1, quad2,
  change, perp2, formonv, ses1, ses2, ty1, ty2, newmat, func, pre, invchange;
  rk := v!.type;
  ty1 := PolarSpaceType(geom1); 
  ty2 := PolarSpaceType(geom2); 
  f := geom1!.basefield; 
  d1 := geom1!.dimension;
  d2 := geom2!.dimension;
  tyv := TypeOfSubspace( geom2, v );
    ## Check that fields are the same and v is non-degenerate 
  if geom2!.basefield <> f then
   Error("fields of both spaces must be the same");
  fi;
    #27/9/2011. jdb was wondering on the next 7 lines. Is this not just tyv = "degenerate"?
 #if not (ty2 = "parabolic" and IsEvenInt(Size(f))) then
    #  perp2 := Polarity(geom2);
    #  if perp2(v) in geom2 then
    #     Error("subspace is degenerate"); 
    #     return;
    #  fi;
    #fi;
  if rk > d2 or d1 > d2 then
   Error("dimensions are incompatible"); 
  fi; 
  if tyv = "degenerate" then
   Error("subspace is degenerate");
  elif
   tyv <> ty1 then 
   Error("non-degenerate section is not the same type as ", geom1);
  fi;  
  orth := ["parabolic", "elliptic", "hyperbolic"];
  if ty1 = ty2 or (ty1 in orth and ty2 in orth) then 
          
          ## Let B be basis of v. Then the form BM(B^T)^sigma (n.b. sigma 
          ## is a field automorphism), where M is the form for geom2,
          ## will be equivalent to the form for geom1.

   basis := Unpack(v!.obj); #cmat unpack

          ## As usual we must consider two cases, the quadratic form case
          ## and the sesquilinear form case. We then obtain a base change matrix c1.

   if HasQuadraticForm( geom2 ) and HasQuadraticForm( geom1 ) then
    quad1 := QuadraticForm(geom1); #cmat notice: these are never cmats. So keep untouched.
    quad2 := QuadraticForm(geom2);
    newmat := basis * quad2!.matrix * TransposedMat(basis);
    formonv := QuadraticFormByMatrix(newmat, f);
    c1 := BaseChangeToCanonical( quad1 );
   else
    ses1 := SesquilinearForm(geom1);
    ses2 := SesquilinearForm(geom2);
    if ty1 = "hermitian" then 
     newmat := basis * ses2!.matrix * (TransposedMat(basis))^CompanionAutomorphism(geom1);
     formonv := HermitianFormByMatrix(newmat, f);
    else
     newmat := basis * ses2!.matrix * TransposedMat(basis);
     formonv := BilinearFormByMatrix(newmat, f);
    fi;
    c1 := BaseChangeToCanonical( ses1 );
   fi;

       ## Finding isometry from geom1 to polar space defined by formofv:

   c2 := BaseChangeToCanonical( formonv );
   change := c1^-1 * c2;        
   # ConvertToMatrixRep(change, f); #became useless.
   invchange := change^-1;
   bs := BaseSteinitzVectors(Basis(geom2!.vectorspace), basis);
   invbasis := Inverse(Concatenation(bs!.subspace, bs!.factorspace));
   # ConvertToMatrixRep(invbasis, f); #same here.
   func := x -> VectorSpaceToElement( geom2, Unpack(x!.obj) * change * basis);
   pre := function(y)
    local newy;
    if not y in v then
     Error("Applying preimage to an element which is not in the range");
    fi;
    newy:= Unpack(y!.obj) * invbasis;
    if IsMatrix(newy) then 
     newy := newy{[1..Size(newy)]}{[1..rk]};
     ConvertToMatrixRepNC(newy, f);
    else
     newy := newy{[1..rk]}; 
     ConvertToVectorRepNC(newy, f);
    fi;
    return VectorSpaceToElement(geom1, newy * invchange);
   end; 
   map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(geom1), 
                                         ElementsOfIncidenceStructure(geom2), 
                                         func, false, pre );
   SetIsInjective( map, true );
  else 
   Error("Polar spaces are not compatible"); 
  fi;
  return map;
 end );
  
# CHECKED 28/09/11 jdb
# cmat comments: see NaturalEmbeddingBySubspace (above).
#############################################################################
#O  NaturalEmbeddingBySubspaceNC( <ps1>, <ps2>, <v> ) 
## This operation is just like its namesake except that it 
## has no checks
##  
InstallMethod( NaturalEmbeddingBySubspaceNC, 
 "for a geometry into another, via a specified subspace, no-check version",  
 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsSubspaceOfProjectiveSpace ],
 function( geom1, geom2, v ) 
  local map, rk, f, i, basis, invbasis, bs, c1, c2, orth, tyv, quad1, quad2,
          change, perp2, formonv, ses1, ses2, ty1, ty2, newmat, func, pre, invchange;
  rk := v!.type;
  ty1 := PolarSpaceType(geom1); 
  ty2 := PolarSpaceType(geom2); 
  f := geom1!.basefield;
  tyv := TypeOfSubspace( geom2, v );
  orth := ["parabolic", "elliptic", "hyperbolic"];
  if ty1 = ty2 or (ty1 in orth and ty2 in orth) then 
          
          ## Let B be basis of v. Then the form BM(B^T)^sigma (n.b. sigma 
          ## is a field automorphism), where M is the form for geom2,
          ## will be equivalent to the form for geom1.

  basis := Unpack(v!.obj);

          ## As usual we must consider two cases, the quadratic form case
          ## and the sesquilinear form case. We then obtain a base change matrix c1.

  if HasQuadraticForm( geom2 ) and HasQuadraticForm( geom1 ) then
   quad1 := QuadraticForm(geom1);
   quad2 := QuadraticForm(geom2);
   newmat := basis * quad2!.matrix * TransposedMat(basis);
   formonv := QuadraticFormByMatrix(newmat, f);
   c1 := BaseChangeToCanonical( quad1 );
  else
   ses1 := SesquilinearForm(geom1);
   ses2 := SesquilinearForm(geom2);
   if ty1 = "hermitian" then 
    newmat := basis * ses2!.matrix * (TransposedMat(basis))^CompanionAutomorphism(geom1);
    formonv := HermitianFormByMatrix(newmat, f);
   else
    newmat := basis * ses2!.matrix * TransposedMat(basis);
    formonv := BilinearFormByMatrix(newmat, f);
   fi;
   c1 := BaseChangeToCanonical( ses1 );
  fi;

       ## Finding isometry from geom1 to polar space defined by formofv:

  c2 := BaseChangeToCanonical( formonv );
  change := c1^-1 * c2;        
  #ConvertToMatrixRepNC(change, f);
  invchange := change^-1;
  bs := BaseSteinitzVectors(Basis(geom2!.vectorspace), basis);
  invbasis := Inverse(Concatenation(bs!.subspace, bs!.factorspace));
  #ConvertToMatrixRepNC(invbasis, f);

  func := x -> VectorSpaceToElement( geom2, Unpack(x!.obj) * change * basis);
  pre := function(y)
   local newy;
   newy:= Unpack(y!.obj) * invbasis;
    if IsMatrix(newy) then 
     newy := newy{[1..Size(newy)]}{[1..rk]};
     ConvertToMatrixRepNC(newy, f);
    else
     newy := newy{[1..rk]}; 
     ConvertToVectorRepNC(newy, f);
    fi;
    return VectorSpaceToElement(geom1, newy * invchange);
   end; 
   map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(geom1), 
                                         ElementsOfIncidenceStructure(geom2), 
                                         func, false, pre );
  SetIsInjective( map, true );
  else 
   Error("Polar spaces are not compatible"); 
  fi;
  return map;
 end );
  
# CHECKED 28/09/11 jdb (and added a check that basefields of <ps1> and <ps2> are equal.)
#cmat changed 20/3/14.
#############################################################################
#O  IsomorphismPolarSpaces( <ps1>, <ps2>, <bool> ) 
# returns the coordinate transformation from <ps1> to <ps2> (which must be
# polar spaces of the same type of course). If <bool> is true, then an intertwiner 
# is computed.
##
InstallMethod( IsomorphismPolarSpaces, 
    "for two similar polar spaces and a boolean",  
    [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsBool ],
 function( ps1, ps2, computeintertwiner )
  local form1, form2, f, map, c1, c2, change, invchange, hom, ty1, ty2,
          coll1, coll2, gens1, gens2, x, y, func, inv, twinerfunc, twinerprefun, r1, r2;
  ty1 := PolarSpaceType( ps1 );
  ty2 := PolarSpaceType( ps2 );
  if ty1 = "parabolic" and ty2 = "symplectic" then
            return IsomorphismPolarSpacesProjectionFromNucleus(ps1, ps2, computeintertwiner);
        fi;
        r1 := Rank(ps1);
        r2 := Rank(ps2);
        if ty1 <> ty2 or r1 <> r2 then
   Error("Polar spaces are of different type or of different rank");
  fi;
  f := ps1!.basefield;
  if f <> ps2!.basefield then
   Error( "<ps1> and <ps2> must be polar spaces over the same field");
  fi;
  if IsEvenInt(Size(f)) and ty1 in ["parabolic", "elliptic", "hyperbolic"] then
   form1 := QuadraticForm( ps1 );
   form2 := QuadraticForm( ps2 );
  else
   form1 := SesquilinearForm( ps1 );
   form2 := SesquilinearForm( ps2 );
  fi;
  c1 := BaseChangeToCanonical( form1 );
  c2 := BaseChangeToCanonical( form2 );
  change := NewMatrix(IsCMatRep, ps1!.basefield, ps1!.dimension+1,c1^-1 * c2);  #cmat change here
  #ConvertToMatrixRep(change, f);
  invchange := Inverse(change); 
  if ty1 = "hermitian" then
   change := change^CompanionAutomorphism(ps2);
   invchange := invchange^CompanionAutomorphism(ps1);
  fi; 
  func := function(x)
   return VectorSpaceToElement(ps2, ShallowCopy(x!.obj) * change);
  end;
  inv := function(x)
   return VectorSpaceToElement(ps1, ShallowCopy(x!.obj) * invchange);
  end;
  map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps1), 
                                      ElementsOfIncidenceStructure(ps2),
                                      func, inv);               

    # map from gens2 to gens1
  twinerprefun := function( x )
   local y;
    y := MutableCopyMat( x!.mat );
                #return ProjElWithFrob( change * y * invchange^x!.frob, x!.frob, f );  
                return ProjElWithFrob( change * y * invchange^(x!.frob^-1), x!.frob, f );  

   end;  
       
    # map from gens1 to gens2
  twinerfunc := function( y )
   local x;
    x := MutableCopyMat( y!.mat );
                #return ProjElWithFrob( invchange * x * change^y!.frob, y!.frob, f );
                return ProjElWithFrob( invchange * x * change^(y!.frob^-1), y!.frob, f ); 
   end;
  if computeintertwiner then
   if (HasIsCanonicalPolarSpace( ps1 ) and IsCanonicalPolarSpace( ps1 )) or
    HasCollineationGroup( ps1 ) then
    coll1 := CollineationGroup(ps1);
    gens1 := GeneratorsOfGroup( coll1 );  
    gens2 := List(gens1, twinerfunc);  
    coll2 := GroupWithGenerators(gens2);  
    hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 
    SetIntertwiner( map, hom );
    UseIsomorphismRelation(coll1,coll2);                
   elif (HasIsCanonicalPolarSpace( ps2 ) and IsCanonicalPolarSpace( ps2 )) or
    HasCollineationGroup( ps2 ) then                                 
    coll2 := CollineationGroup( ps2 );  
    gens2 := GeneratorsOfGroup( coll2 );          
    gens1 := List(gens2, twinerprefun ); 
    coll1 := GroupWithGenerators(gens1);      
    hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 
    SetIntertwiner( map, hom );      
    UseIsomorphismRelation(coll1,coll2);         
   else 
    Info(InfoFinInG, 1, "No intertwiner computed. One of the polar spaces must have a collineation group computed");
   fi;
  fi;
  return map;
 end );

# CHECKED 28/09/11 jdb
#############################################################################
#O  IsomorphismPolarSpaces( <ps1>, <ps2> ) 
# returns IsomorphismPolarSpaces( <ps1>, <ps2>, true );
##
InstallMethod( IsomorphismPolarSpaces, 
 "for two similar polar spaces",  
 [ IsClassicalPolarSpace, IsClassicalPolarSpace ],
 function( ps1, ps2 )
  return IsomorphismPolarSpaces( ps1, ps2, true );
 end );

# CHECKED 28/09/11 jdb
#############################################################################
#O  IsomorphismPolarSpacesNC( <ps1>, <ps2>, <bool> ) 
# This operation is just like its namesake except that it 
## has no checks
InstallMethod( IsomorphismPolarSpacesNC, 
    "for two similar polar spaces and a boolean",  
 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsBool ],
 function( ps1, ps2, computeintertwiner )
  local form1, form2, f, map, c1, c2, change, invchange, hom, ty1, ty2, n,
          coll1, coll2, gens1, gens2, func, inv, mono, twinerprefun, twinerfunc;
  ty1 := PolarSpaceType( ps1 );
  ty2 := PolarSpaceType( ps2 );
  f := ps1!.basefield;
  if IsEvenInt(Size(f)) and ty1 in ["parabolic", "elliptic", "hyperbolic"] then
   form1 := QuadraticForm( ps1 );
   form2 := QuadraticForm( ps2 );
  else
   form1 := SesquilinearForm( ps1 );
   form2 := SesquilinearForm( ps2 );
  fi;
  c1 := BaseChangeToCanonical( form1 );
  c2 := BaseChangeToCanonical( form2 );
  change := NewMatrix(IsCMatRep, ps1!.basefield, ps1!.dimension+1,c1^-1 * c2);  #cmat change here
  #ConvertToMatrixRepNC(change, f);
  invchange := Inverse(change);     
  func := function(x)
   return VectorSpaceToElement(ps2, ShallowCopy(x!.obj) * change);
  end;
  inv := function(x)
   return VectorSpaceToElement(ps1, ShallowCopy(x!.obj) * invchange);
  end;
  map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps1), 
                                      ElementsOfIncidenceStructure(ps2),
                                      func, inv);               
    ## Now creating intertwiner...
 # map from gens2 to gens1
  twinerprefun := function( x )
   local y;
   y := MutableCopyMat( x!.mat );
                #return ProjElWithFrob( invchange * x * change^y!.frob, y!.frob, f );
                #return ProjElWithFrob( invchange * x * change^(y!.frob^-1), y!.frob, f );
                return ProjElWithFrob( change * y * invchange^(x!.frob^-1), x!.frob, f );
  end;  
       
    # map from gens1 to gens2
  twinerfunc := function( y )
   local x;
            x := MutableCopyMat( y!.mat );
                #return ProjElWithFrob( invchange * x * change^y!.frob, y!.frob, f );
                return ProjElWithFrob( invchange * x * change^(y!.frob^-1), y!.frob, f ); 
  end;
  if computeintertwiner then
   if (HasIsCanonicalPolarSpace( ps1 ) and IsCanonicalPolarSpace( ps1 )) or
    HasCollineationGroup( ps1 ) then
    coll1 := CollineationGroup(ps1);
    gens1 := GeneratorsOfGroup( coll1 );  
    gens2 := List(gens1, twinerfunc);  
    coll2 := GroupWithGenerators(gens2);  
    hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 
    SetIntertwiner( map, hom );
    UseIsomorphismRelation(coll1,coll2);                
   elif (HasIsCanonicalPolarSpace( ps2 ) and IsCanonicalPolarSpace( ps2 )) or
    HasCollineationGroup( ps2 ) then                                 
    coll2 := CollineationGroup( ps2 );  
    gens2 := GeneratorsOfGroup( coll2 );          
    gens1 := List(gens2, twinerprefun ); 
    coll1 := GroupWithGenerators(gens1);      
    hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 
    SetIntertwiner( map, hom );      
    UseIsomorphismRelation(coll1,coll2);         
   else 
    Info(InfoFinInG, 1, "No intertwiner computed. One of the polar spaces must have a collineation group computed");
   fi;
  fi;
  return map;
 end );
  
# CHECKED 28/09/11 jdb
#############################################################################
#O  IsomorphismPolarSpacesNC( <ps1>, <ps2> ) 
# returns IsomorphismPolarSpacesNC( <ps1>, <ps2>, true );
##
InstallMethod( IsomorphismPolarSpacesNC, 
 "for two similar polar spaces",  
 [ IsClassicalPolarSpace, IsClassicalPolarSpace ],
 function( ps1, ps2 )  
  return IsomorphismPolarSpacesNC( ps1, ps2, true );
 end );

###########################################################
## Field reduction / subfield morphisms. Helper operations.
############################################################

# CHECKED 28/09/11 jdb
#############################################################################
#O  ShrinkMat( <B>, <mat> ) 
# returns the preimage of BlownUpMat
##
InstallMethod( ShrinkMat, 
 "for a basis and a matrix",
 [ IsBasis, IsMatrix ],
 function( B, mat )
  
  ## THERE WAS A BUG IN THIS FUNCTION: (lavrauw 15/10/2010)
  ## NOT ALL MATRICES ARE BLOWNUP MATRICES! Here is a new version.
  ## Here is the explanation that could go into the documentation:
  ## The result of BlownUpMat(B,A) is the matrix, where each entry a=A_ij is replaced by
  ## the dxd matrix M_a, representing the linear map x |-> ax with respect to the basis B of
  ## the field K seen as d-dimensional vectorspace over F. This means that if 
  ## B={b_1,b_2,...,b_d}, then the first row are the coefficients of ab_1 w.r.t. B, and so on.
  ## Concersely, if we want to implement ShrinkMat, we need to check if the input is 
  ## a blown up matrix. 
  ## This can be done as follows. Let A be a dm x dn matrix. For each dxd block, say M, 
  ## we need to check that the set {b_i^(-1)\sum_{j=1}^{d}m_ij b_j: i\in \{1,..,d\}} has size one
  ## (Since this would be the a \in K represented as a linear map by M w.r.t. the basis B)
  
  ## This function is basically the inverse map of
  ## BlownUpMat.
  
 local d,vecs,m,n,bmat,blocks,bl,submat,i,j,ij,checklist,row,newmat;
 d:=Size(B);
 vecs:=BasisVectors(B);
 m:=NrRows(mat);
 n:=NrCols(mat);
  # First we check if m and n are multiples of d
 if not (IsInt(m/d) and IsInt(n/d)) then 
  Error("The matrix does not have the right dimensions");
 fi;
 blocks:=List(Cartesian([0..m/d-1],[0..n/d-1]),ij->mat{[ij[1]*d+1 .. ij[1]*d+d]}{[ij[2]*d+1 ..ij[2]*d+d]});
 newmat:=[];
 for i in [1..m/d] do
  row:=[]; 
  for j in [1..n/d] do
   #submat:=blocks[(i-1)*d+j]; #wrong line?
      submat:=blocks[(i-1)*(m/d)+j];
   checklist:=List([1..d],i->(vecs[i]^(-1)*(Sum([1..d],j->submat[i][j]*vecs[j]))));
   if Size(AsSet(checklist))<>1 then 
    Error("The matrix is not a blown up matrix");
   fi;
   Add(row,checklist[1]);
  od;
  Add(newmat,row);
 od;
 return newmat;
  # DO WE NEED TO USE ConvertToMatrixRepNC ?
 end); 
  
  
InstallMethod( ShrinkMat, 
 "for a field, a subfield and a matrix",
 [ IsField,IsField, IsMatrix ],
 function( f1,f2,mat )
 # This gives the same result as ShrinkMat(B,mat) with B the natural basis returned by
 # Basis(AsVectorSpace(f2,f1));
 local B;
 B:=Basis(AsVectorSpace(f2,f1));
 return ShrinkMat(B,mat);
end );
 

#############################################################################
#O  ShrinkVec( <f1>, <f2>, <v> ) 
# f2 is a subfield of f1 and v is vector in a vectorspace V2 over f2
# return the vector of length d2/t, where d2=dim(V2), and t=[f1:f2]
# using the natural basis Basis(AsVectorSpace(f2,f1))
##
InstallMethod( ShrinkVec, 
 "for a field, a subfield and a vector",
 [ IsField, IsField, IsVector ],
 function( f1,f2,v )
 local t,d1,d2,basvecs;
 t:=Dimension(AsVectorSpace(f2,f1));
 d1:=Length(v)/t;
 if IsInt(d1) then
  basvecs:=BasisVectors(Basis(AsVectorSpace(f2,f1)));
  return List([1..d1],i->v{[(i-1)*t+1..i*t]}*basvecs);
 else
  Error("The length of v is not divisible by the degree of the field extension");
 fi;
end );

#############################################################################
#O ShrinkVec( <f1>, <f2>, <v>, basis )
# The same operation as above, but now with a basis as extra argument
InstallMethod( ShrinkVec, 
 "for a field, a subfield and a vector",
 [ IsField, IsField, IsVector, IsBasis ],
 function( f1,f2,v,basis )
 local t,d1,d2,basvecs;
 t:=Dimension(AsVectorSpace(f2,f1));
 d1:=Length(v)/t;
 if IsInt(d1) then
  basvecs:=BasisVectors(basis);
  return List([1..d1],i->v{[(i-1)*t+1..i*t]}*basvecs);
 else
  Error("The length of v is not divisible by the degree of the field extension");
 fi;
end );



#############################################################################
#O  BlownUpSubspaceOfProjectiveSpace( <B>, <pg1> ) 
#   blows up a projective space by field reduction.
##
InstallMethod( BlownUpProjectiveSpace,
 "for a basis and a projective space",
 [ IsBasis, IsProjectiveSpace ],
 function(basis,pg1)
 local q,t,r,pg2,mat1,mat2;
  q:=basis!.q;
  t:=basis!.d;
  if not pg1!.basefield=GF(q^t) then 
   Error("The basis and the projective space are not compatible!");
  fi;
  r:=Dimension(pg1)+1;
  pg2:=PG(r*t-1,q);
  return pg2;
 end );

#############################################################################
#O  BlownUpProjectiveSpaceBySubfield( <subfield>, <pg> ) 
#   blows up a projective space by field reduction w.r.t. the canonical basis
##
InstallMethod( BlownUpProjectiveSpaceBySubfield,
 "for a field and a projective space",
 [ IsField, IsProjectiveSpace ],
 function(subfield,pg)
 local t,r,field;
        field:=LeftActingDomain(UnderlyingVectorSpace(pg));
        if not subfield in Subfields(field) then
         Error("The first argument is not a subfield of the basefield of the projective space!");
        fi;
        t:=Dimension(AsVectorSpace(subfield,field));
        r:=Dimension(pg)+1;
        return PG(r*t-1,Size(subfield));
  end );

# CHECKED 1/10/11 jdb
#############################################################################
#O  BlownUpSubspaceOfProjectiveSpace( <B>, <subspace> ) 
#  blows up a subspace of a projective space by field reduction
##
InstallMethod( BlownUpSubspaceOfProjectiveSpace,
 "for a basis and a subspace of a projective space",
 [ IsBasis, IsSubspaceOfProjectiveSpace ],
 function(basis,subspace)
 local pg1,q,t,r,pg2,mat1,mat2;
  pg1:=AmbientGeometry(subspace);
  q:=basis!.q;
  t:=basis!.d;
  if not pg1!.basefield=GF(q^t) then 
   Error("The basis and the subspace are not compatible!");
  fi;
  r:=Dimension(pg1)+1;
  pg2:=PG(r*t-1,q);
  mat1:=subspace!.obj;
  if subspace!.type = 1 then 
   mat1 := [subspace!.obj]; 
  fi; 
  mat2:=BlownUpMat(basis,mat1);
  return VectorSpaceToElement(pg2,mat2);
 end );

#############################################################################
#O  BlownUpSubspaceOfProjectiveSpaceBySubfield( <subfield>, <subspace> )
# blows up a subspace of projective space by field reduction
# This is w.r.t. to the canonical basis of the field over the subfield.
##
InstallMethod( BlownUpSubspaceOfProjectiveSpaceBySubfield,
 "for a field and a subspace of a projective space",
 [ IsField, IsSubspaceOfProjectiveSpace],
 function(subfield,subspace)
 local pg,field,basis;
  pg:=AmbientGeometry(subspace);
  field:=LeftActingDomain(UnderlyingVectorSpace(pg));
  if not subfield in Subfields(field) then
   Error("The first argument is not a subfield of the basefield of the projective space!");
  fi;
  basis:=Basis(AsVectorSpace(subfield,field));
  return BlownUpSubspaceOfProjectiveSpace(basis,subspace);
 end );

#############################################################################
#O  IsDesarguesianSpreadElement( <B>, <subspace> ) 
# checks if a subspace is a blown up point using field reduction, w.r.t. a basis
##
InstallMethod( IsDesarguesianSpreadElement, 
 "for a basis and a subspace of a projective space",
 [ IsBasis, IsSubspaceOfProjectiveSpace ],
 function(basis,subspace)
  local flag,q,t,pg1,pg2,em,basvecs,v,v1,i,mat,rt,r;
  flag:=true;
  q:=basis!.q;
  t:=basis!.d;
  pg2:=AmbientGeometry(subspace);
  rt:=Dimension(pg2)+1;
  if not (Dimension(subspace)+1 = t and IsInt(rt/t)) then 
   flag:=false;
  else
   r:=rt/t;
   pg1:=PG(r-1,q^t);
   basvecs:=BasisVectors(basis);
   v:=Coordinates(Random(Points(subspace)));
   v1:=List([1..r],i->v{[(i-1)*t+1..i*t]}*basvecs);
   mat:=BlownUpMat(basis,[v1]);
   flag:=subspace = VectorSpaceToElement(pg2,mat);
  fi;
  return flag;
 end );
   
# To be done: check this method! (but it is currently never used).   
#############################################################################
#O  IsBlownUpSubspaceOfProjectiveSpace( <B>, <mat> ) 
# checks if a subspace is blown up using field reduction, w.r.t. a basis
##
InstallMethod( IsBlownUpSubspaceOfProjectiveSpace, 
 "for a basis and a subspace of a projective space",
 [ IsBasis, IsSubspaceOfProjectiveSpace ],
 # It's important that we include a basis in the arguments, 
 # since for every subspace, provided it has the right dimension, there exists some
 # basis with respect to which the subspace is blown up.
 function(basis,subspace)
  local flag,q,F,t,K,pg2,rt,r,pg1,basvecs,mat1,span,x,v,v1,i;
  flag:=true;
  q:=basis!.q;
  F:=GF(q);
  t:=basis!.d;
  K:=GF(q^t);
  pg2:=AmbientGeometry(subspace);
  rt:=Dimension(pg2)+1;
  if not (IsInt(rt/t) and IsInt((Dimension(subspace)+1)/t)) then 
   flag:=false;
  else
   r:=rt/t;
   pg1:=PG(r-1,q^t);
   basvecs:=BasisVectors(basis);
   mat1:=[];
   span:=[];
   repeat
    repeat x:=Random(Points(subspace)); until not x in span;
     v:=Coordinates(x);
     v1:=List([1..r],i->v{[(i-1)*t+1..i*t]}*basvecs);
     Add(mat1,v1);
     span:=VectorSpaceToElement(pg2,BlownUpMat(basis,mat1));
    until Dimension(span)=Dimension(subspace);
    if not span = subspace then 
     flag:= false;
    fi;
  fi;
  return flag;
 end );   
  
#############################################################################
#  PROJECTIVE SPACES
#############################################################################

#
#  A recent reference on field reduction of projective spaces is
#  "Field reduction and linear sets in finite geomety" by Lavrauw
#   and Van de Voorde (preprint 2013)

#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <field>, <basis> ) 
# <geom2> is a projective space over a field K, <geom1> is a projective space
# over a field extension L, and considering L as a vector space over K, yields 
# that <geom1> and <geom2> have the same ambient vectorspace over K, then this
# operation returns the natural embedding, i.e. with relation to the given basis
# of L over K.
# Important note: the intertwiner has as source the *Projectivity group* only, not
# the full collineation group!
##
InstallMethod( NaturalEmbeddingByFieldReduction, 
 "for two projective spaces and a basis",
     [ IsProjectiveSpace, IsField, IsBasis ],
 function( pg1, f2, basis )
  
  ## This morphism contains a func and prefunc with built-in check.
 
  local map, f1, d1, d2, t, fun, prefun, g1, gens, newgens, g2, twiner, hom, hominv, q1, q2, pg2;
  f1 := pg1!.basefield; 
  q1 := Size(f1);
  q2 := Size(f2);
  t := LogInt(q1,q2);
  if not q2^t = q1 then
   Error( "<f2> is not a subfield of the base field of <pg1> ");
  fi;
  d1 := pg1!.dimension + 1;
  d2 := d1*t;
  pg2 := ProjectiveSpace(d2-1,q2);
  if not (IsBasis(basis) and f1=GF((basis!.q)^basis!.d) and f2=GF(basis!.q) and d1*(basis!.d)=d2) then
   Error("The basis is not a basis or is not compatible with the basefields of the geometries");
  fi;

  fun := function( subspace ) # This map blows up a subspace of geom1 to a subspace of geom2
            local mat1,mat2;
            #return BlownUpSubspaceOfProjectiveSpace(basis,x);
            mat1 := subspace!.obj;
            if subspace!.type = 1 then
                mat1 := [subspace!.obj];
            fi;
            mat2 := BlownUpMat(basis,mat1);
            return VectorSpaceToElement(pg2,mat2);
  end;

        prefun := function( subspace ) # This map is the inverse of func and returns an error, or a subspace of geom1
            local flag,basvecs,mat1,span,x,v,v1,i,vecs;
            flag:=true;
            if not subspace in pg2 then
                Error("The input is not in the range of the field reduction map!");
            fi;
            if not IsInt((Dimension(subspace)+1)/t) then
                flag := false;
            else
                basvecs:=BasisVectors(basis);
                vecs := Unpack(UnderlyingObject(subspace));
                mat1 := List(vecs,x->List([1..d1],i->x{[(i-1)*t+1..i*t]}*basvecs));
                span := VectorSpaceToElement(pg2,BlownUpMat(basis,mat1));
                if not span = subspace then
                    flag := false;
                fi;
            fi;
            if flag= false then
                Error("The input is not in the range of the field reduction map!");
            fi;
            return VectorSpaceToElement(pg1,mat1);
        end;

     #prefun := function( x ) #I want to find out why this is not correct...
  # return VectorSpaceToElement(pg1,ShrinkMat(basis,x!.obj));
  #end;
       
  map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(pg1),
                                         ElementsOfIncidenceStructure(pg2),
                                            fun, false, prefun);
  
  SetIsInjective( map, true );
        
        ## Now creating intertwiner
        ## 12/6/14: added check to see whether m is really a projectivity
  
        hom := function( m )
   local image;
            if not IsOne(m!.frob) then
                return fail;
                Info(InfoFinInG, 1, "<el> is not a projectivity");
            fi;
   image := BlownUpMat(basis, m!.mat); 
   #ConvertToMatrixRepNC( image, f2 );
   return CollineationOfProjectiveSpace(image, f2);
  end;

  hominv := function( m )
   local preimage;
            if not IsOne(m!.frob) then
                return fail;
                Info(InfoFinInG, 1, "<el> is not a projectivity");
            fi;
   preimage := ShrinkMat(basis, Unpack(m!.mat));
   #ConvertToMatrixRepNC( preimage, f1 );       
   return CollineationOfProjectiveSpace(preimage, f1);
  end;

  g1 := ProjectivityGroup( pg1 );
  gens := GeneratorsOfGroup( g1 );
  newgens := List(gens, hom);
  g2 := Group( newgens );
  SetSize(g2, Size(g1));
  twiner := GroupHomomorphismByFunction(g1, g2, hom, hominv);
  SetIntertwiner( map, twiner);
  return map;
 end );

#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <field> ) 
# <field> is a field K, <geom1> is a projective space
# over a field extension L, and considering L as a vector space over K, yields 
# that a projective space geom2 that has the same ambient vectorspace over K. This
# operation returns the natural embedding, i.e. with relation to the standard basis of
# L over K.
##
InstallMethod( NaturalEmbeddingByFieldReduction, 
 "for a projective space and a field",
 [ IsProjectiveSpace, IsField ],
 function( pg1, f2 )
  local basis;
  basis:=Basis(AsVectorSpace(f2,pg1!.basefield));
  return NaturalEmbeddingByFieldReduction(pg1,f2,basis);
 end );

#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <geom2>, <basis> ) 
# <geom2> is a projective space over a field K, <geom1> is a projective space
# over a field extension L, and considering L as a vector space over K, yields 
# that <geom1> and <geom2> have the same ambient vectorspace over K, then this
# operation returns the natural embedding, i.e. with relation to the given basis
# of L over K.
##
InstallMethod( NaturalEmbeddingByFieldReduction, 
 "for two projective spaces and a basis",
 [ IsProjectiveSpace, IsProjectiveSpace, IsBasis ],
 function( pg1, pg2, basis )
  return NaturalEmbeddingByFieldReduction(pg1,pg2!.basefield,basis);
 end);

# CHECKED 28/09/11 jdb
#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <geom2> ) 
# <geom2> is a projective space over a field K, <geom1> is a projective space
# over a field extension L, and considering L as a vector space over K, yields 
# that <geom1> and <geom2> have the same ambient vectorspace over K, then this
# operation returns the natural embedding, i.e. with relation to the standard basis of
# L over K.
##
InstallMethod( NaturalEmbeddingByFieldReduction, 
 "for two projective spaces",
 [ IsProjectiveSpace, IsProjectiveSpace ],
 function( pg1, pg2 )
  local basis;
  basis:=Basis(AsVectorSpace(pg2!.basefield,pg1!.basefield));
  return NaturalEmbeddingByFieldReduction(pg1,pg2!.basefield,basis);
 end );










############################################################################
#  POLAR SPACES
##############################################################################


#
#  Two references on field reduction of polar spaces:
#  [1] from group theoretic point of view: "Polar spaces and embeddings of classical groups" by Nick Gill
#  (New Zealand J. Math)
#  [2] from finite geometry point of view: "Field reduction and linear sets in finite geomety" by Lavrauw
#   and Van de Voorde (preprint 2013)
#
#  We will use [2] as it is more convenient to us.


####
#
# First the field reduction of bilinear forms, quadratic forms and hermitian forms
#
#####


#############################################################################
#O  BilinearFormFieldReduction( <bil1>, <f2>, <alpha>, <basis> ) 
# <bil1> is a bilinear form over <f1>, <f2> is a subfield of <f1>. 
# <alpha> determines the linear map from f1 to <f2>, f1 the field extension of <f2>
# with basis <basis>. This operation then
# returns the bilinear form L(bil1(.,.)), L(x) = T(alpha*x), T the trace from <f1> to <f2>.
##
# REMARK (ml 31/10/13) The fourth argument is a basis of L over K. This just gives
# extra functionality to the user, and it is used as third argument in the 
# operation ShrinkVec. 
# There is also a version of this field reduction operation, which does not use
# a basis as fourth argument. In this case the standard GAP basis is used.

InstallMethod( BilinearFormFieldReduction,
 "for a bilinear form and a field",
 [ IsBilinearForm, IsField, IsFFE, IsBasis ],
 function(bil1,f2,alpha,basis)
 # f2 is a subfield of the basefield of the bilinear form bil1
 local mat1,f1,d1,t,d2,V2,V1,phi,b2,b2vecs,mat2,i,row,j,bil2;
 mat1:=bil1!.matrix;
 f1:=bil1!.basefield;
 d1:=NrRows(mat1);
 t:=Dimension(AsVectorSpace(f2,f1));
 d2:=d1*t;
 V2:=f2^d2;
 V1:=f1^d1;
 b2:=Basis(V2);
 b2vecs:=BasisVectors(b2);
 mat2:=[];
 for i in [1..d2] do 
  row:=[];
  for j in [1..d2] do
   Add(row,Trace(f1,f2,alpha*(ShrinkVec(f1,f2,b2vecs[i],basis)*mat1*ShrinkVec(f1,f2,b2vecs[j],basis))));
  od;
  Add(mat2,row);
 od;
 bil2:=BilinearFormByMatrix(mat2,f2);
 return bil2;
end );

#############################################################################
#O  BilinearFormFieldReduction( <bil1>, <f2>, <alpha> ) 
# The same as above, but without specified basis. In this case the canonical basis is used.
##
InstallMethod( BilinearFormFieldReduction,
 "for a bilinear form and a field",
 [ IsBilinearForm, IsField, IsFFE ],
 function(bil1,f2,alpha)
 # f2 is a subfield of the basefield of the bilinear form bil1
 return BilinearFormFieldReduction(bil1,f2,alpha,Basis(AsVectorSpace(f2,bil1!.basefield)));
end );

#############################################################################
#O  QuadraticFormFieldReduction( <qf1>, <f2>, <alpha>, <basis> ) 
# <bil1> is a bilinear form over <f1>, <f2> is a subfield of <f1>. This operation
# returns the bilinear form T(alpha*qf1(.,.)), T the trace from <f1> to <f2>.
##
# REMARK (ml 31/10/13) The fourth argument is a basis of L over K. This just gives
# extra functionality to the user, and it is used as third argument in the 
# operation ShrinkVec. 
# There is also a version of this field reduction operation, which does not use
# a basis as fourth argument. In this case the standard GAP basis is used.

InstallMethod( QuadraticFormFieldReduction,
 "for a quadratic form and a field",
 [ IsQuadraticForm, IsField, IsFFE, IsBasis ],
 function(qf1,f2,alpha,basis)
 # f2 is a subfield of the basefield for the quadratic form q1
 local f1,basvecs,d1,t,d2,V2,V1,phi,b2,b2vecs,mat2,i,bil1,j,qf2;
 f1:=qf1!.basefield;
 basvecs:=BasisVectors(basis);
 d1:=NrRows(qf1!.matrix);
 t:=Dimension(AsVectorSpace(f2,f1));
 d2:=d1*t;
 V2:=f2^d2;
 V1:=f1^d1;
 b2:=Basis(V2);
 b2vecs:=BasisVectors(b2);
 mat2:=IdentityMat(d2,f2);
 for i in [1..d2] do
  mat2[i,i]:=Trace(f1,f2,alpha*((ShrinkVec(f1,f2,b2vecs[i],basis))^qf1));
 od;
 for i in [1..d2-1] do
  for j in [i+1..d2] do
   mat2[i,j]:=Trace(f1,f2,alpha*((ShrinkVec(f1,f2,b2vecs[i]+b2vecs[j],basis))^qf1
       -(ShrinkVec(f1,f2,b2vecs[i],basis))^qf1-(ShrinkVec(f1,f2,b2vecs[j],basis))^qf1));
   #mat2[j,i]:=mat2[i,j]; THESE entries need to be zero
  od;
 od;
 qf2:=QuadraticFormByMatrix(mat2,f2);
 return qf2;
end );

#############################################################################
# #O  QuadraticFormFieldReduction( <qf1>, <f2>, <alpha> )
# The same as above, but without specified basis. In this case the canonical basis is used.
##
InstallMethod( QuadraticFormFieldReduction,
 "for a quadratic form and a field",
 [ IsQuadraticForm, IsField, IsFFE ],
 function(qf1,f2,alpha)
 local basis,qf2;
 # f2 is a subfield of the basefield for the quadratic form q1
 basis:=Basis(AsVectorSpace(f2,qf1!.basefield));
 qf2:=QuadraticFormFieldReduction(qf1,f2,alpha,basis);
 return qf2;
end );


#############################################################################
#O  HermitianFormFieldReduction( <hf1>, <f2>, <alpha>, <basis> ) 
# <hf1> is a hermitian form over <f1>, <f2> is a subfield of <f1>. This operation
# returns the form T(alpha*bil1(.,.)). Depending on the degree of the field extension, this
# yields either a bilinear form or a hermitian form.
##
# REMARK (ml 31/10/13) The fourth argument is a basis of L over K. This just gives
# extra functionality to the user, and it is used as third argument in the 
# operation ShrinkVec. 
# There is also a version of this field reduction operation, which does not use
# a basis as fourth argument. In this case the standard GAP basis is used.

InstallMethod( HermitianFormFieldReduction,
 "for a hermitian form and a field",
 [ IsHermitianForm, IsField, IsFFE, IsBasis ],
 function(hf1,f2,alpha,basis)
 # f2 is a subfield of the basefield for the hermitian form hf1
 # here the basefield is always a square
 local f1,d1,t,d2,V2,V1,phi,b2,b2vecs,mat2,i,row,j,hf2;
 f1:=hf1!.basefield;
 d1:=NrRows(hf1!.matrix);
 t:=Dimension(AsVectorSpace(f2,f1));
 d2:=d1*t;
 V2:=f2^d2;
 V1:=f1^d1;
 b2:=Basis(V2);
 b2vecs:=BasisVectors(b2);
 mat2:=[];
 for i in [1..d2] do 
  row:=[];
  for j in [1..d2] do
   Add(row,Trace(f1,f2,alpha*([ShrinkVec(f1,f2,b2vecs[i],basis),ShrinkVec(f1,f2,b2vecs[j],basis)]^hf1)));
  od;
  Add(mat2,row);
 od;
 # checking parity of t is indeed sufficient to decide, see table in manual.
 if IsOddInt(t) then 
  hf2:=HermitianFormByMatrix(mat2,f2);
 else
  hf2:=BilinearFormByMatrix(mat2,f2);
 fi;
 return hf2;
end );


#############################################################################
# #O  HermitianFormFieldReduction( <hf11>, <f2>, <alpha> ) 
# The same as above, but without specified basis. In this case the canonical basis is used.
##

InstallMethod( HermitianFormFieldReduction,
 "for a hermitian form and a field",
 [ IsHermitianForm, IsField, IsFFE ],
 function(hf1,f2,alpha)
 # f2 is a subfield of the basefield for the hermitian form hf1
 # here the basefield is always a square
 local basis;
 
 basis:=Basis(AsVectorSpace(f2,hf1!.basefield));
 return HermitianFormFieldReduction(hf1,f2,alpha,basis);
end );



##########################################################
#
# Next the embeddings for polar spaces based on the field reduction of the forms above

#############################################################################

# master version for the user: handle all parameters.
#############################################################################
#O  NaturalEmbeddingByFieldReduction( <ps1>, <f2>, <alpha>, <basis>, <bool> ) 
# returns a geometry morphism, described below. If <bool> is true, then an intertwiner
# is computed.
##
InstallMethod (NaturalEmbeddingByFieldReduction,
 "for a polar space, a field, a basis, a finite field element, and a boolean",
 [IsClassicalPolarSpace, IsField, IsFFE, IsBasis, IsBool],
 function(ps1,f2,alpha,basis,computeintertwiner)
 # f2 must be a subfield of the basefield of the classical polar space
 local type1,qf1,qf2,ps2,bil1,bil2,hf1,hf2,fun,prefun,f1,
     map,hom,hominv,g1,gens,newgens,g2,twiner, t,q1,q2,d1,d2;
 type1 := PolarSpaceType(ps1);
 f1:=ps1!.basefield;
 q1 := Size(f1);
 q2 := Size(f2);
 t := LogInt(q1,q2);
 d1 := ps1!.dimension + 1;
 d2 := d1*t;

 # 1. the polar space is of orthogonal type
 if type1 in ["hyperbolic","elliptic","parabolic"] then
  qf1:=QuadraticForm(ps1);
  qf2:=QuadraticFormFieldReduction(qf1,f2,alpha,basis);
  
  if IsSingularForm(qf2) then 
   Error("The field reduction does not yield a natural embedding");
  else ps2:=PolarSpace(qf2);
  fi;
 # 2. the polar space is of symplectic type
 elif type1 in ["symplectic"] then
  bil1:=SesquilinearForm(ps1);
  bil2:=BilinearFormFieldReduction(bil1,f2,alpha,basis);
  ps2:=PolarSpace(bil2);
 # 3. the polar space is of hermitian type 
 elif type1 in ["hermitian"] then
  hf1:=SesquilinearForm(ps1);
  hf2:=HermitianFormFieldReduction(hf1,f2,alpha,basis);
  ps2:=PolarSpace(hf2);
 fi;
 
 #em:=NaturalEmbeddingByFieldReduction(AmbientSpace(ps1),AmbientSpace(ps2));
 # this is the field reduction for projective spaces PG(r-1,q^t) -> PG(rt-1,q)
 
 #fun:=function(x)
 # local projfun;
 # projfun:=em!.fun;
 # return VectorSpaceToElement(ps2,projfun(x)!.obj);
 #end;
 
    fun := function( subspace )
        local mat1,mat2;
        mat1 := subspace!.obj;
        if subspace!.type = 1 then
            mat1 := [subspace!.obj];
        fi;
        mat2 := BlownUpMat(basis,mat1);
        return VectorSpaceToElement(ps2,mat2);
    end;


 #prefun:=function(x)
 # local projprefun;
 # projprefun:=em!.prefun;
 # return VectorSpaceToElement(ps1,projprefun(x)!.obj);
 #end;

    # new version (2/7/14, much faster since we avoid the repeat loops and Random calls.
    prefun := function( subspace ) # This map is the inverse of func and returns an error, or a subspace of geom1
        local flag,basvecs,mat1,span,x,v,v1,i,vecs;
        flag:=true;
        if not subspace in ps2 then
            Error("The input is not in the range of the field reduction map!");
        fi;
        if not IsInt((Dimension(subspace)+1)/t) then
            flag:=false;
        else
            basvecs:=BasisVectors(basis);
            mat1:=[];
            span:=[];
            vecs := Unpack(UnderlyingObject(subspace));
            mat1 := List(vecs,x->List([1..d1],i->x{[(i-1)*t+1..i*t]}*basvecs));
            span := VectorSpaceToElement(AmbientSpace(ps2),BlownUpMat(basis,mat1)); 
                #the ambientspace makes sure that there is no error message here yet.
            if not span=subspace then
                flag := false;
            fi;
        fi;
        if flag= false then
            Error("The input is not in the range of the field reduction map!");
        fi;
        return VectorSpaceToElement(ps1,mat1);
    end;

 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps1), ElementsOfIncidenceStructure(ps2),
                                           fun, false, prefun);
  
 SetIsInjective( map, true );
 
 ## Now creating intertwiner
 
 if computeintertwiner then
        if (HasIsCanonicalPolarSpace( ps1 ) and IsCanonicalPolarSpace( ps1 )) or
            HasCollineationGroup( ps1 ) then
            
            hom := function( m )
                local image;
                image := BlownUpMat(basis, m!.mat);
                ConvertToMatrixRepNC( image, f2 );
                return CollineationOfProjectiveSpace(image, f2);
            end;
            hominv := function( m )
                local preimage;
                preimage := ShrinkMat(basis, Unpack(m!.mat));
                ConvertToMatrixRepNC( preimage, f1 );
                return CollineationOfProjectiveSpace(preimage, f1);
            end;
            g1 := IsometryGroup( ps1 );
            gens := GeneratorsOfGroup( g1 );
            newgens := List(gens, hom);
            g2 := Group( newgens );
            SetSize(g2, Size(g1));
            twiner := GroupHomomorphismByFunction(g1, g2, hom, hominv);
            SetIntertwiner( map, twiner);
        fi;
 fi;

    return map;
 end );


#first version: user wants a particular alpha, and basis, agrees with bool=true.
#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis> )
# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis>, true )
#
InstallMethod (NaturalEmbeddingByFieldReduction,
 "for a polar space and a field, a finite field element, and a basis",
 [IsClassicalPolarSpace, IsField, IsFFE, IsBasis],
 function(ps1,f2,alpha,basis)
  return NaturalEmbeddingByFieldReduction(ps1,f2,alpha,basis,true);
 end );

#second particular version: user wants a particular alpha, and bool, agrees with basis.
#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <bool> )
# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis>, true )
# where <basis> is the canonical basis of BaseField(geom1) over <f2>.
#
InstallMethod (NaturalEmbeddingByFieldReduction,
 "for a polar space and a field, a finite field element, and a boolean",
 [IsClassicalPolarSpace, IsField, IsFFE, IsBool],
 function(ps1,f2,alpha,bool)
  return NaturalEmbeddingByFieldReduction(ps1,f2,alpha,Basis(AsVectorSpace(f2,BaseField(ps1))),bool);
 end );

#third particular version: user wants a particular alpha, agrees with basis and bool.
#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha> )
# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis>, true )
# where <basis> is the canonical basis of BaseField(geom1) over <f2>.
#
InstallMethod (NaturalEmbeddingByFieldReduction,
 "for a polar space, a field, and a finite field element",
 [IsClassicalPolarSpace, IsField, IsFFE],
 function(ps1,f2,alpha)
  return NaturalEmbeddingByFieldReduction(ps1,f2,alpha,Basis(AsVectorSpace(f2,BaseField(ps1))),true);
 end );

# fourth particular version: user agrees but wants to control intertwiner
#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <bool> ) 
# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <one>, <basis>, <bool> )
# where <basis> is the canonical basis of BaseField(geom1) over <f2>, and
# <one> is One(BaseField(geom1)). This might be usefull if you agree with the defaults, but doesn't 
# want the intertwiner.
#
InstallMethod (NaturalEmbeddingByFieldReduction,
 "for a polar space, a field, and a boolean",
 [IsClassicalPolarSpace, IsField, IsBool],
 function(ps1,f2,bool)
  return NaturalEmbeddingByFieldReduction(ps1,f2,One(f2),Basis(AsVectorSpace(f2,BaseField(ps1))),bool);
 end );

# fifth particular version: user agrees with everything
#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <f2> ) 
# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis>, true )
# where <basis> is the canonical basis of BaseField(geom1) over <f2>, and
# <alpha> is One(f2).
InstallMethod (NaturalEmbeddingByFieldReduction,
 "for a polar space and a field",
 [IsClassicalPolarSpace, IsField],
 function(ps1,f2)
  return NaturalEmbeddingByFieldReduction(ps1,f2,One(f2),Basis(AsVectorSpace(f2,BaseField(ps1))),true);
 end );

# CHECKED 28/09/11 jdb
#############################################################################
#O  NaturalEmbeddingByFieldReduction( <geom1>, <geom2> ) 
# returns NaturalEmbeddingByFieldReduction(<geom1>, <geom2> )
##
InstallMethod( NaturalEmbeddingByFieldReduction, 
 "for two polar spaces",
 [ IsClassicalPolarSpace, IsClassicalPolarSpace ],
 function( geom1, geom2 )
  return NaturalEmbeddingByFieldReduction( geom1, geom2, true );
 end );

# ml 31/10/2013
# changed with new prefun 2/7/14 jdb
#####################################################################################
# O  NaturalEmbeddingByFieldReduction
# This NaturalEmbeddingByFieldReduction takes two polar spaces and returns an embedding
# obtained by field reduction if such an embedding exists, otherwise it returns an error.
##
InstallMethod (NaturalEmbeddingByFieldReduction,
 "for two classical polar spaces",
 [IsClassicalPolarSpace, IsClassicalPolarSpace, IsBool],
 function(ps1, ps2, computeintertwiner)

 #####################################################################
 #  List of possible embeddings of ps1 in PG(r-1, q^t) -> ps2 in PG(rt-1, q)
 #
 #  Hermitian
 #   H -> H (t odd)
 #   H -> W (t even)
 #   H -> Q+ (t even, r even)
 #   H -> Q- (t even, r odd)
 #
 #  Symplectic
 #   W -> W (always)
 #
 #  Orthogonal 
 # r even   
 # Q+ -> Q+ (always)
 # Q- -> Q- (always)
 # r odd
 # Q -> Q (t odd, q odd)
 #   Q -> Q+ (t even, q odd)
 #   Q -> Q- (t even, q odd)
 #####################################################################

    local t, r, type1, type2, form1, f1, f2, newps, sigma, map,
    gamma, bil1, q, n, alpha, basis, is_square, iso, form2, fun, prefun,
          c1, c2, cps2, cps2inv, d1, hom, hominv, g1, g2, gens, newgens, twiner;
     
 type1 := PolarSpaceType(ps1);
 type2 := PolarSpaceType(ps2);
 if type1 in ["hyperbolic", "parabolic", "elliptic"] then 
    form1 := QuadraticForm(ps1);
 else
    form1 := SesquilinearForm(ps1);
 fi;
    d1 := ps1!.dimension+1;
 f1 := ps1!.basefield;
 f2 := ps2!.basefield;
 q := Size(f2);
 
 if not IsSubset(f1,f2) then
    Error("Fields are incompatible");
 fi;
 t := Log(Size(f1),q);
 r := ProjectiveDimension(ps1)+1;
 basis := CanonicalBasis(AsVectorSpace(f2,f1));
    is_square := function(x, f) return IsZero(x) or IsEvenInt(LogFFE(x,PrimitiveRoot(f))); end;
      
 if IsSesquilinearForm(form1) then
        if type1 = "hermitian" and type2 = "hermitian" and IsOddInt(t) then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction");
    # need alpha to be fixed by sigma:
   alpha := One(f1);
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
   form2 := HermitianFormFieldReduction(form1,f2,alpha,basis);
     elif type1 = "hermitian" and type2 = "symplectic" and IsEvenInt(t) then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction");
   if IsEvenInt(q) then
    # need alpha to be fixed by sigma:
    alpha := One(f1);    
   else
    # need sigma(alpha)=-alpha
    sigma := Sqrt(Size(f1));
    alpha := First(f1, x->not IsZero(x) and x^sigma = -x);    
   fi; 
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
            form2 := HermitianFormFieldReduction(form1,f2,alpha,basis);
  
     elif type1 = "hermitian" and type2 = "hyperbolic" and IsEvenInt(t) and IsEvenInt(r) 
    and IsOddInt(q) then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction");
   alpha := One(f1); 
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
            form2 := HermitianFormFieldReduction(form1,f2,alpha,basis);
      
     elif type1 = "hermitian" and type2 = "elliptic" and IsEvenInt(t) and IsOddInt(r) 
    and IsOddInt(q) then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction");
   alpha := One(f1); 
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
            form2 := HermitianFormFieldReduction(form1,f2,alpha,basis);
   
     elif type1 = "symplectic" and type2 = "symplectic" then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction");
   alpha := One(f1);    
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
            form2 := BilinearFormFieldReduction(form1,f2,alpha,basis);
        else
    Error("These polar spaces are not suitable for field reduction.");
  fi;
 else  # form1 is a quadratic form
  if type1 = "hyperbolic" and type2 = "hyperbolic" then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction");
   alpha := One(f1);
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
            form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis);
  elif type1 = "elliptic" and type2 = "elliptic" then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction");
   alpha := One(f1);
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
            form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis);
  elif type1 = "parabolic" and type2 = "parabolic" and IsOddInt(t) and IsOddInt(q) then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction");
   alpha := One(f1);
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
            form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis);

  # If ps1 is parabolic and ps2 is hyperbolic or elliptic, 
  # then the choice of alpha that we will use for the embedding
  # depends on the isometry type of ps1. The isometry type is determined by
  # gamma being a square or not, where gamma is
  # (-1)^(r-1)/2 times the determinant of the bilinear form, divided by two.
  # The conditions we use here are taken from [Lavrauw - Van de Voorde: "Field
  # reduction and linear sets in finite geometry", preprint], they are
  # somewhat easier to work with than the conditions in Gill's paper.

  elif type1 = "parabolic" and type2 = "hyperbolic" and IsEvenInt(t) and IsOddInt(q) then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction: Parabolic -> Hyperbolic");
   bil1:=AssociatedBilinearForm(form1); # important to use this instead of 
   # BilinearFormByQuadraticForm (see documentation of the Forms package).
   gamma:=((-1)^((r-1)/2)) * Determinant(bil1!.matrix)/2;
   if q^(t/2) mod 4 = 1 then # the product of alpha and gamma must be a nonsquare
     alpha := First(f1, a -> not IsZero(a) and not is_square( a*gamma, f1 ) );
   else # the product of alpha and gamma must be a square
    alpha := First(f1, a -> not IsZero(a) and is_square( a*gamma, f1 ) );
   fi;
   #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis );
            form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis);

  elif type1 = "parabolic" and type2 = "elliptic" and IsEvenInt(t) and IsOddInt(q) then
   Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction: Parabolic -> Elliptic");
   bil1:=AssociatedBilinearForm(form1); # important to use this instead of 
   # BilinearFormByQuadraticForm (see documentation of the Forms package).
--> --------------------

--> maximum size reached

--> --------------------

[ Verzeichnis aufwärts0.77unsichere Verbindung  Übersetzung europäischer Sprachen durch Browser  ]