1############################################################################# 2## 3## morphisms.gi FinInG package 4## John Bamberg 5## Anton Betten 6## Jan De Beule 7## Philippe Cara 8## Michel Lavrauw 9## Max Neunhoeffer 10## 11## Copyright 2018 Colorado State University 12## Sabancı Üniversitesi 13## Università degli Studi di Padova 14## Universiteit Gent 15## University of St. Andrews 16## University of Western Australia 17## Vrije Universiteit Brussel 18## 19## 20## Implementation stuff for incidence geometry morphisms 21## 22############################################################################# 23 24 25######################################## 26# 20/3/14 27# cmat notice: in many operations we will compute 28# a matrix to initialize a form. Forms can not handle cmats, so 29# Unpacking the cmats will sometimes be necessary. 30######################################## 31 32 33############################################################ 34## Generic constructor operations, not intended for the user. 35############################################################ 36 37## The three methods for GeometryMorphismByFunction are analogous 38## with the MappingByFunction function. It behaves in exactly the same 39## way except that we return an IsGeometryMorphism. 40 41# CHECKED 27/09/11 jdb 42############################################################################# 43#O GeometryMorphismByFunction( <els1>, <els2>, <fun>, <bool>, <prefun> ) 44## 45InstallMethod( GeometryMorphismByFunction, 46 "for two times a collection of elements of any type, a function, a boolean, and a function", 47 [ IsAnyElementsOfIncidenceStructure, IsAnyElementsOfIncidenceStructure, 48 IsFunction, IsBool, IsFunction ], 49 function( els1, els2, fun, bool, prefun ) 50 local morphism; 51 morphism := MappingByFunction(els1, els2, fun, bool, prefun ); 52 SetFilterObj( morphism, IsGeometryMorphism ); 53 return morphism; 54 end ); 55 56# CHECKED 27/09/11 jdb 57############################################################################# 58#O GeometryMorphismByFunction( <els1>, <els2>, <fun>, <prefun> ) 59## 60InstallMethod( GeometryMorphismByFunction, 61 "for two times a collection of elements of any type, and two times a function", 62 [ IsAnyElementsOfIncidenceStructure, IsAnyElementsOfIncidenceStructure, 63 IsFunction, IsFunction ], 64 function( els1, els2, fun, inv ) 65 local morphism; 66 morphism := MappingByFunction(els1, els2, fun, inv ); 67 SetFilterObj( morphism, IsGeometryMorphism ); 68 return morphism; 69 end ); 70 71# CHECKED 27/09/11 jdb 72############################################################################# 73#O GeometryMorphismByFunction( <els1>, <els2>, <fun> ) 74## 75InstallMethod( GeometryMorphismByFunction, 76 "for two times a collection of elements of any type, and a function", 77 [ IsAnyElementsOfIncidenceStructure, IsAnyElementsOfIncidenceStructure, 78 IsFunction ], 79 function( els1, els2, fun ) 80 local morphism; 81 morphism := MappingByFunction(els1, els2, fun); 82 SetFilterObj( morphism, IsGeometryMorphism ); 83 return morphism; 84 end ); 85 86############################################################################# 87# Display methods 88############################################################################# 89 90InstallMethod( ViewObj, 91 "for a geometry morphism", 92 [ IsGeometryMorphism ], 93 function( f ) 94 Print("<geometry morphism from "); 95 ViewObj(Source(f)); 96 Print( " to " ); 97 ViewObj(Range(f)); 98 Print(">"); 99 end ); 100 101InstallMethod( PrintObj, 102 "for a geometry morphism", 103 [ IsGeometryMorphism ], 104 function( f ) 105 Print("Geometry morphism:\n ", f, "\n"); 106 end ); 107 108InstallMethod( Display, 109 "for a geometry morphism", 110 [ IsGeometryMorphism ], 111 function( f ) 112 Print("Geometry morphism: ", Source(f), " -> ", Range(f), "\n"); 113 end ); 114 115InstallMethod( ViewObj, 116 "for a geometry morphism", 117 [ IsGeometryMorphism and IsMappingByFunctionWithInverseRep ], 118 function( f ) 119 Print("<geometry morphism from "); 120 ViewObj(Source(f)); 121 Print( " to " ); 122 ViewObj(Range(f)); 123 Print(">"); 124 end ); 125 126InstallMethod( ViewObj, 127 "for a geometry morphism", 128 [ IsGeometryMorphism and IsMappingByFunctionRep ], 129 function( f ) 130 Print("<geometry morphism from "); 131 ViewObj(Source(f)); 132 Print( " to " ); 133 ViewObj(Range(f)); 134 Print(">"); 135 end ); 136 137InstallMethod( PrintObj, 138 "for a geometry morphism", 139 [ IsGeometryMorphism and IsMappingByFunctionRep ], 140 function( f ) 141 Print("Geometry morphism:\n ", f, "\n"); 142 end ); 143 144InstallMethod( Display, 145 "for a geometry morphism", 146 [ IsGeometryMorphism and IsMappingByFunctionRep ], 147 function( f ) 148 Print("Geometry morphism: ", Source(f), " -> ", Range(f), "\n"); 149 end ); 150 151############################################################ 152## Generic methods for the Image* operations for IsGeometryMorphism. 153############################################################ 154 155# CHECKED 27/09/11 jdb 156############################################################################# 157#O ImageElm( <em>, <x> ) 158## 159InstallOtherMethod( ImageElm, 160 "for a geometry morphism and an element of an incidence structure", 161 [IsGeometryMorphism, IsElementOfIncidenceStructure], 162 function(em, x) 163 return em!.fun(x); 164 end ); 165 166# CHECKED 27/09/11 jdb 167############################################################################# 168#O \^( <x>, <em> ) 169## 170InstallOtherMethod( \^, 171 "for an element of an incidence structure and a geometry morphism", 172 [IsElementOfIncidenceStructure, IsGeometryMorphism], 173 function(x, em) 174 return ImageElm(em,x); 175 end ); 176 177 178# CHECKED 27/09/11 jdb 179############################################################################# 180#O ImagesSet( <em>, <x> ) 181## 182InstallOtherMethod( ImagesSet, 183 "for a geometry morphism and a collection of elements of an incidence structure", 184 [IsGeometryMorphism, IsElementOfIncidenceStructureCollection], 185 function(em, x) 186 return List(x, t -> em!.fun(t)); 187 end ); 188 189 190# CHECKED 27/09/11 jdb 191############################################################################# 192#O PreImageElm( <em>, <x> ) 193## 194InstallOtherMethod( PreImageElm, 195 "for a geometry morphism and an element of an incidence structure", 196 [IsGeometryMorphism, IsElementOfIncidenceStructure], 197 function(em, x) 198 if IsInjective(em) then 199 return em!.prefun(x); 200 else 201 Error("Map is not injective"); 202 fi; 203 end ); 204 205# CHECKED 27/09/11 jdb 206############################################################################# 207#O PreImagesSet( <em>, <x> ) 208## 209InstallOtherMethod( PreImagesSet, 210 "for a geometry morphism and an element of an incidence structure", 211 [IsGeometryMorphism, IsElementOfIncidenceStructureCollection], 212 function(em, x) 213 return List(x, t -> em!.prefun(t)); 214 end ); 215 216########################################################## 217## User methods for the "natural geometry morphisms" 218########################################################## 219 220## The specialised operations... 221 222# CHECKED 14/12/11 jdb + ml (and added a check that basefields of <ps1> and <ps2> are equal.) 223# cmat changed 21/3/14. 224############################################################################# 225#O NaturalEmbeddingBySubspace( <ps1>, <ps2>, <v> ) returns a geometry morphism 226# from the projective space <ps1> into <v>, an element of the projective space 227# <ps2>. <v> must be of the right type, i.e. same projective dimension as <ps1> 228# and <ps1> and <ps2> must be projective spaces over the same field. 229## 230InstallMethod( NaturalEmbeddingBySubspace, 231 "for a projective space into another, via a specified subspace", 232 [ IsProjectiveSpace, IsProjectiveSpace, IsSubspaceOfProjectiveSpace ], 233 function( geom1, geom2, v ) 234 local d1, d2, rk, f, invbasis, basis, func, pre, map, morphism, bs; 235 rk := v!.type; 236 basis := Unpack(v!.obj); #cmat change 237 d1 := geom1!.dimension + 1; 238 d2 := geom2!.dimension + 1; 239 f := geom2!.basefield; 240 if not v in geom2 then 241 Error("Subspace is not an element of ", geom2); 242 fi; 243 if d2 < d1 or d1 <> rk then 244 Error("Dimensions are incompatible"); 245 fi; 246 if not f = geom1!.basefield then 247 Error("Basefields must be the same"); 248 fi; 249 ## To find the preimage, we first find an invertible matrix C such that 250 ## [a1,..,ad]B = [a1,...,ad,0,...,0]C (where B is our d x e matrix "basis") 251 ## is our embedding. We simply extend B to a basis for geom2. 252 bs := BaseSteinitzVectors(Basis(geom2!.vectorspace), basis); 253 invbasis := Inverse(Concatenation(bs!.subspace, bs!.factorspace)); 254 #ConvertToMatrixRep(invbasis, f); #useless now. 255 func := x -> VectorSpaceToElement(geom2 , Unpack(x!.obj) * basis); #VectorSpaceToElement makes sure the 2nd arg becomes cmat. 256 pre := function(y) 257 local newy; 258 if not y in v then 259 Error("Applying preimage to an element which is not in the range"); 260 fi; 261 newy:= Unpack(y!.obj) * invbasis; #cmat change. 262 if not IsMatrix(newy) then 263 newy := newy{[1..d1]}; 264 #ConvertToVectorRepNC(newy, f); #useless now. 265 else 266 newy := newy{[1..Size(newy)]}{[1..d1]}; 267 #ConvertToMatrixRepNC(newy, f); #useless now. 268 fi; 269 return VectorSpaceToElement(geom1, newy); #VectorSpaceToElement etc. 270 end; 271 morphism := GeometryMorphismByFunction(ElementsOfIncidenceStructure(geom1), 272 ElementsOfIncidenceStructure(geom2), 273 func, false, pre ); 274 SetIsInjective( morphism, true ); 275 return morphism; 276 end ); 277 278# CHECKED 27/09/11 jdb + ml 279# cmat changed 21/3/14. 280############################################################################# 281#O NaturalEmbeddingBySubspaceNC( <ps1>, <ps2>, <v> ) 282## This operation is just like its namesake except that it 283## has no checks 284## 285InstallMethod( NaturalEmbeddingBySubspaceNC, 286 "for a projective space into another, via a specified subspace", 287 [ IsProjectiveSpace, IsProjectiveSpace, IsSubspaceOfProjectiveSpace ], 288 function( geom1, geom2, v ) 289 local d1, f, invbasis, basis, func, pre, map, morphism, bs; 290 basis := Unpack(v!.obj); #cmat change 291 d1 := geom1!.dimension + 1; 292 f := geom2!.basefield; 293 bs := BaseSteinitzVectors(Basis(geom2!.vectorspace), basis); 294 invbasis := Inverse(Concatenation(bs!.subspace, bs!.factorspace)); 295 # ConvertToMatrixRep(invbasis, f); #useless now. 296 func := x -> VectorSpaceToElement(geom2 , x!.obj * basis); #VectorSpaceToElement etc. 297 pre := function(y) 298 local newy; 299 newy:= Unpack(y!.obj) * invbasis; #cmat change 300 if not IsMatrix(newy) then 301 newy := newy{[1..d1]}; 302 #ConvertToVectorRepNC(newy, f); useless now. 303 else 304 newy := newy{[1..Size(newy)]}{[1..d1]}; 305 #ConvertToMatrixRepNC(newy, f); useless now. 306 fi; 307 return VectorSpaceToElement(geom1, newy); #cfr. supra. 308 end; 309 morphism := GeometryMorphismByFunction(ElementsOfIncidenceStructure(geom1), 310 ElementsOfIncidenceStructure(geom2), 311 func, false, pre ); 312 SetIsInjective( morphism, true ); 313 return morphism; 314 end ); 315 316# CHECKED 27/09/11 jdb + ml 317# cmat version 20/3/14. 318############################################################################# 319#O NaturalEmbeddingBySubspace( <ps1>, <ps2>, <v> ) returns a geometry morphism 320# from the polar space <ps1> into <ps2>, a polar space induced as a section of 321# <ps1> and <v>, the latter a subspace of the ambient projective space of <ps1> 322## 323InstallMethod( NaturalEmbeddingBySubspace, 324 "for a polar space into another, via a specified subspace", 325 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsSubspaceOfProjectiveSpace ], 326 function( geom1, geom2, v ) 327 local map, d1, d2, rk, f, i, basis, invbasis, bs, c1, c2, orth, tyv, quad1, quad2, 328 change, perp2, formonv, ses1, ses2, ty1, ty2, newmat, func, pre, invchange; 329 rk := v!.type; 330 ty1 := PolarSpaceType(geom1); 331 ty2 := PolarSpaceType(geom2); 332 f := geom1!.basefield; 333 d1 := geom1!.dimension; 334 d2 := geom2!.dimension; 335 tyv := TypeOfSubspace( geom2, v ); 336 ## Check that fields are the same and v is non-degenerate 337 if geom2!.basefield <> f then 338 Error("fields of both spaces must be the same"); 339 fi; 340 #27/9/2011. jdb was wondering on the next 7 lines. Is this not just tyv = "degenerate"? 341 #if not (ty2 = "parabolic" and IsEvenInt(Size(f))) then 342 # perp2 := Polarity(geom2); 343 # if perp2(v) in geom2 then 344 # Error("subspace is degenerate"); 345 # return; 346 # fi; 347 #fi; 348 if rk > d2 or d1 > d2 then 349 Error("dimensions are incompatible"); 350 fi; 351 if tyv = "degenerate" then 352 Error("subspace is degenerate"); 353 elif 354 tyv <> ty1 then 355 Error("non-degenerate section is not the same type as ", geom1); 356 fi; 357 orth := ["parabolic", "elliptic", "hyperbolic"]; 358 if ty1 = ty2 or (ty1 in orth and ty2 in orth) then 359 360 ## Let B be basis of v. Then the form BM(B^T)^sigma (n.b. sigma 361 ## is a field automorphism), where M is the form for geom2, 362 ## will be equivalent to the form for geom1. 363 364 basis := Unpack(v!.obj); #cmat unpack 365 366 ## As usual we must consider two cases, the quadratic form case 367 ## and the sesquilinear form case. We then obtain a base change matrix c1. 368 369 if HasQuadraticForm( geom2 ) and HasQuadraticForm( geom1 ) then 370 quad1 := QuadraticForm(geom1); #cmat notice: these are never cmats. So keep untouched. 371 quad2 := QuadraticForm(geom2); 372 newmat := basis * quad2!.matrix * TransposedMat(basis); 373 formonv := QuadraticFormByMatrix(newmat, f); 374 c1 := BaseChangeToCanonical( quad1 ); 375 else 376 ses1 := SesquilinearForm(geom1); 377 ses2 := SesquilinearForm(geom2); 378 if ty1 = "hermitian" then 379 newmat := basis * ses2!.matrix * (TransposedMat(basis))^CompanionAutomorphism(geom1); 380 formonv := HermitianFormByMatrix(newmat, f); 381 else 382 newmat := basis * ses2!.matrix * TransposedMat(basis); 383 formonv := BilinearFormByMatrix(newmat, f); 384 fi; 385 c1 := BaseChangeToCanonical( ses1 ); 386 fi; 387 388 ## Finding isometry from geom1 to polar space defined by formofv: 389 390 c2 := BaseChangeToCanonical( formonv ); 391 change := c1^-1 * c2; 392 # ConvertToMatrixRep(change, f); #became useless. 393 invchange := change^-1; 394 bs := BaseSteinitzVectors(Basis(geom2!.vectorspace), basis); 395 invbasis := Inverse(Concatenation(bs!.subspace, bs!.factorspace)); 396 # ConvertToMatrixRep(invbasis, f); #same here. 397 func := x -> VectorSpaceToElement( geom2, Unpack(x!.obj) * change * basis); 398 pre := function(y) 399 local newy; 400 if not y in v then 401 Error("Applying preimage to an element which is not in the range"); 402 fi; 403 newy:= Unpack(y!.obj) * invbasis; 404 if IsMatrix(newy) then 405 newy := newy{[1..Size(newy)]}{[1..rk]}; 406 ConvertToMatrixRepNC(newy, f); 407 else 408 newy := newy{[1..rk]}; 409 ConvertToVectorRepNC(newy, f); 410 fi; 411 return VectorSpaceToElement(geom1, newy * invchange); 412 end; 413 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(geom1), 414 ElementsOfIncidenceStructure(geom2), 415 func, false, pre ); 416 SetIsInjective( map, true ); 417 else 418 Error("Polar spaces are not compatible"); 419 fi; 420 return map; 421 end ); 422 423# CHECKED 28/09/11 jdb 424# cmat comments: see NaturalEmbeddingBySubspace (above). 425############################################################################# 426#O NaturalEmbeddingBySubspaceNC( <ps1>, <ps2>, <v> ) 427## This operation is just like its namesake except that it 428## has no checks 429## 430InstallMethod( NaturalEmbeddingBySubspaceNC, 431 "for a geometry into another, via a specified subspace, no-check version", 432 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsSubspaceOfProjectiveSpace ], 433 function( geom1, geom2, v ) 434 local map, rk, f, i, basis, invbasis, bs, c1, c2, orth, tyv, quad1, quad2, 435 change, perp2, formonv, ses1, ses2, ty1, ty2, newmat, func, pre, invchange; 436 rk := v!.type; 437 ty1 := PolarSpaceType(geom1); 438 ty2 := PolarSpaceType(geom2); 439 f := geom1!.basefield; 440 tyv := TypeOfSubspace( geom2, v ); 441 orth := ["parabolic", "elliptic", "hyperbolic"]; 442 if ty1 = ty2 or (ty1 in orth and ty2 in orth) then 443 444 ## Let B be basis of v. Then the form BM(B^T)^sigma (n.b. sigma 445 ## is a field automorphism), where M is the form for geom2, 446 ## will be equivalent to the form for geom1. 447 448 basis := Unpack(v!.obj); 449 450 ## As usual we must consider two cases, the quadratic form case 451 ## and the sesquilinear form case. We then obtain a base change matrix c1. 452 453 if HasQuadraticForm( geom2 ) and HasQuadraticForm( geom1 ) then 454 quad1 := QuadraticForm(geom1); 455 quad2 := QuadraticForm(geom2); 456 newmat := basis * quad2!.matrix * TransposedMat(basis); 457 formonv := QuadraticFormByMatrix(newmat, f); 458 c1 := BaseChangeToCanonical( quad1 ); 459 else 460 ses1 := SesquilinearForm(geom1); 461 ses2 := SesquilinearForm(geom2); 462 if ty1 = "hermitian" then 463 newmat := basis * ses2!.matrix * (TransposedMat(basis))^CompanionAutomorphism(geom1); 464 formonv := HermitianFormByMatrix(newmat, f); 465 else 466 newmat := basis * ses2!.matrix * TransposedMat(basis); 467 formonv := BilinearFormByMatrix(newmat, f); 468 fi; 469 c1 := BaseChangeToCanonical( ses1 ); 470 fi; 471 472 ## Finding isometry from geom1 to polar space defined by formofv: 473 474 c2 := BaseChangeToCanonical( formonv ); 475 change := c1^-1 * c2; 476 #ConvertToMatrixRepNC(change, f); 477 invchange := change^-1; 478 bs := BaseSteinitzVectors(Basis(geom2!.vectorspace), basis); 479 invbasis := Inverse(Concatenation(bs!.subspace, bs!.factorspace)); 480 #ConvertToMatrixRepNC(invbasis, f); 481 482 func := x -> VectorSpaceToElement( geom2, Unpack(x!.obj) * change * basis); 483 pre := function(y) 484 local newy; 485 newy:= Unpack(y!.obj) * invbasis; 486 if IsMatrix(newy) then 487 newy := newy{[1..Size(newy)]}{[1..rk]}; 488 ConvertToMatrixRepNC(newy, f); 489 else 490 newy := newy{[1..rk]}; 491 ConvertToVectorRepNC(newy, f); 492 fi; 493 return VectorSpaceToElement(geom1, newy * invchange); 494 end; 495 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(geom1), 496 ElementsOfIncidenceStructure(geom2), 497 func, false, pre ); 498 SetIsInjective( map, true ); 499 else 500 Error("Polar spaces are not compatible"); 501 fi; 502 return map; 503 end ); 504 505# CHECKED 28/09/11 jdb (and added a check that basefields of <ps1> and <ps2> are equal.) 506#cmat changed 20/3/14. 507############################################################################# 508#O IsomorphismPolarSpaces( <ps1>, <ps2>, <bool> ) 509# returns the coordinate transformation from <ps1> to <ps2> (which must be 510# polar spaces of the same type of course). If <bool> is true, then an intertwiner 511# is computed. 512## 513InstallMethod( IsomorphismPolarSpaces, 514 "for two similar polar spaces and a boolean", 515 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsBool ], 516 function( ps1, ps2, computeintertwiner ) 517 local form1, form2, f, map, c1, c2, change, invchange, hom, ty1, ty2, 518 coll1, coll2, gens1, gens2, x, y, func, inv, twinerfunc, twinerprefun, r1, r2; 519 ty1 := PolarSpaceType( ps1 ); 520 ty2 := PolarSpaceType( ps2 ); 521 if ty1 = "parabolic" and ty2 = "symplectic" then 522 return IsomorphismPolarSpacesProjectionFromNucleus(ps1, ps2, computeintertwiner); 523 fi; 524 r1 := Rank(ps1); 525 r2 := Rank(ps2); 526 if ty1 <> ty2 or r1 <> r2 then 527 Error("Polar spaces are of different type or of different rank"); 528 fi; 529 f := ps1!.basefield; 530 if f <> ps2!.basefield then 531 Error( "<ps1> and <ps2> must be polar spaces over the same field"); 532 fi; 533 if IsEvenInt(Size(f)) and ty1 in ["parabolic", "elliptic", "hyperbolic"] then 534 form1 := QuadraticForm( ps1 ); 535 form2 := QuadraticForm( ps2 ); 536 else 537 form1 := SesquilinearForm( ps1 ); 538 form2 := SesquilinearForm( ps2 ); 539 fi; 540 c1 := BaseChangeToCanonical( form1 ); 541 c2 := BaseChangeToCanonical( form2 ); 542 change := NewMatrix(IsCMatRep, ps1!.basefield, ps1!.dimension+1,c1^-1 * c2); #cmat change here 543 #ConvertToMatrixRep(change, f); 544 invchange := Inverse(change); 545 if ty1 = "hermitian" then 546 change := change^CompanionAutomorphism(ps2); 547 invchange := invchange^CompanionAutomorphism(ps1); 548 fi; 549 func := function(x) 550 return VectorSpaceToElement(ps2, ShallowCopy(x!.obj) * change); 551 end; 552 inv := function(x) 553 return VectorSpaceToElement(ps1, ShallowCopy(x!.obj) * invchange); 554 end; 555 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps1), 556 ElementsOfIncidenceStructure(ps2), 557 func, inv); 558 559 # map from gens2 to gens1 560 twinerprefun := function( x ) 561 local y; 562 y := MutableCopyMat( x!.mat ); 563 #return ProjElWithFrob( change * y * invchange^x!.frob, x!.frob, f ); 564 return ProjElWithFrob( change * y * invchange^(x!.frob^-1), x!.frob, f ); 565 566 end; 567 568 # map from gens1 to gens2 569 twinerfunc := function( y ) 570 local x; 571 x := MutableCopyMat( y!.mat ); 572 #return ProjElWithFrob( invchange * x * change^y!.frob, y!.frob, f ); 573 return ProjElWithFrob( invchange * x * change^(y!.frob^-1), y!.frob, f ); 574 end; 575 if computeintertwiner then 576 if (HasIsCanonicalPolarSpace( ps1 ) and IsCanonicalPolarSpace( ps1 )) or 577 HasCollineationGroup( ps1 ) then 578 coll1 := CollineationGroup(ps1); 579 gens1 := GeneratorsOfGroup( coll1 ); 580 gens2 := List(gens1, twinerfunc); 581 coll2 := GroupWithGenerators(gens2); 582 hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 583 SetIntertwiner( map, hom ); 584 UseIsomorphismRelation(coll1,coll2); 585 elif (HasIsCanonicalPolarSpace( ps2 ) and IsCanonicalPolarSpace( ps2 )) or 586 HasCollineationGroup( ps2 ) then 587 coll2 := CollineationGroup( ps2 ); 588 gens2 := GeneratorsOfGroup( coll2 ); 589 gens1 := List(gens2, twinerprefun ); 590 coll1 := GroupWithGenerators(gens1); 591 hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 592 SetIntertwiner( map, hom ); 593 UseIsomorphismRelation(coll1,coll2); 594 else 595 Info(InfoFinInG, 1, "No intertwiner computed. One of the polar spaces must have a collineation group computed"); 596 fi; 597 fi; 598 return map; 599 end ); 600 601# CHECKED 28/09/11 jdb 602############################################################################# 603#O IsomorphismPolarSpaces( <ps1>, <ps2> ) 604# returns IsomorphismPolarSpaces( <ps1>, <ps2>, true ); 605## 606InstallMethod( IsomorphismPolarSpaces, 607 "for two similar polar spaces", 608 [ IsClassicalPolarSpace, IsClassicalPolarSpace ], 609 function( ps1, ps2 ) 610 return IsomorphismPolarSpaces( ps1, ps2, true ); 611 end ); 612 613# CHECKED 28/09/11 jdb 614############################################################################# 615#O IsomorphismPolarSpacesNC( <ps1>, <ps2>, <bool> ) 616# This operation is just like its namesake except that it 617## has no checks 618InstallMethod( IsomorphismPolarSpacesNC, 619 "for two similar polar spaces and a boolean", 620 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsBool ], 621 function( ps1, ps2, computeintertwiner ) 622 local form1, form2, f, map, c1, c2, change, invchange, hom, ty1, ty2, n, 623 coll1, coll2, gens1, gens2, func, inv, mono, twinerprefun, twinerfunc; 624 ty1 := PolarSpaceType( ps1 ); 625 ty2 := PolarSpaceType( ps2 ); 626 f := ps1!.basefield; 627 if IsEvenInt(Size(f)) and ty1 in ["parabolic", "elliptic", "hyperbolic"] then 628 form1 := QuadraticForm( ps1 ); 629 form2 := QuadraticForm( ps2 ); 630 else 631 form1 := SesquilinearForm( ps1 ); 632 form2 := SesquilinearForm( ps2 ); 633 fi; 634 c1 := BaseChangeToCanonical( form1 ); 635 c2 := BaseChangeToCanonical( form2 ); 636 change := NewMatrix(IsCMatRep, ps1!.basefield, ps1!.dimension+1,c1^-1 * c2); #cmat change here 637 #ConvertToMatrixRepNC(change, f); 638 invchange := Inverse(change); 639 func := function(x) 640 return VectorSpaceToElement(ps2, ShallowCopy(x!.obj) * change); 641 end; 642 inv := function(x) 643 return VectorSpaceToElement(ps1, ShallowCopy(x!.obj) * invchange); 644 end; 645 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps1), 646 ElementsOfIncidenceStructure(ps2), 647 func, inv); 648 ## Now creating intertwiner... 649 # map from gens2 to gens1 650 twinerprefun := function( x ) 651 local y; 652 y := MutableCopyMat( x!.mat ); 653 #return ProjElWithFrob( invchange * x * change^y!.frob, y!.frob, f ); 654 #return ProjElWithFrob( invchange * x * change^(y!.frob^-1), y!.frob, f ); 655 return ProjElWithFrob( change * y * invchange^(x!.frob^-1), x!.frob, f ); 656 end; 657 658 # map from gens1 to gens2 659 twinerfunc := function( y ) 660 local x; 661 x := MutableCopyMat( y!.mat ); 662 #return ProjElWithFrob( invchange * x * change^y!.frob, y!.frob, f ); 663 return ProjElWithFrob( invchange * x * change^(y!.frob^-1), y!.frob, f ); 664 end; 665 if computeintertwiner then 666 if (HasIsCanonicalPolarSpace( ps1 ) and IsCanonicalPolarSpace( ps1 )) or 667 HasCollineationGroup( ps1 ) then 668 coll1 := CollineationGroup(ps1); 669 gens1 := GeneratorsOfGroup( coll1 ); 670 gens2 := List(gens1, twinerfunc); 671 coll2 := GroupWithGenerators(gens2); 672 hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 673 SetIntertwiner( map, hom ); 674 UseIsomorphismRelation(coll1,coll2); 675 elif (HasIsCanonicalPolarSpace( ps2 ) and IsCanonicalPolarSpace( ps2 )) or 676 HasCollineationGroup( ps2 ) then 677 coll2 := CollineationGroup( ps2 ); 678 gens2 := GeneratorsOfGroup( coll2 ); 679 gens1 := List(gens2, twinerprefun ); 680 coll1 := GroupWithGenerators(gens1); 681 hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 682 SetIntertwiner( map, hom ); 683 UseIsomorphismRelation(coll1,coll2); 684 else 685 Info(InfoFinInG, 1, "No intertwiner computed. One of the polar spaces must have a collineation group computed"); 686 fi; 687 fi; 688 return map; 689 end ); 690 691# CHECKED 28/09/11 jdb 692############################################################################# 693#O IsomorphismPolarSpacesNC( <ps1>, <ps2> ) 694# returns IsomorphismPolarSpacesNC( <ps1>, <ps2>, true ); 695## 696InstallMethod( IsomorphismPolarSpacesNC, 697 "for two similar polar spaces", 698 [ IsClassicalPolarSpace, IsClassicalPolarSpace ], 699 function( ps1, ps2 ) 700 return IsomorphismPolarSpacesNC( ps1, ps2, true ); 701 end ); 702 703########################################################### 704## Field reduction / subfield morphisms. Helper operations. 705############################################################ 706 707# CHECKED 28/09/11 jdb 708############################################################################# 709#O ShrinkMat( <B>, <mat> ) 710# returns the preimage of BlownUpMat 711## 712InstallMethod( ShrinkMat, 713 "for a basis and a matrix", 714 [ IsBasis, IsMatrix ], 715 function( B, mat ) 716 717 ## THERE WAS A BUG IN THIS FUNCTION: (lavrauw 15/10/2010) 718 ## NOT ALL MATRICES ARE BLOWNUP MATRICES! Here is a new version. 719 ## Here is the explanation that could go into the documentation: 720 ## The result of BlownUpMat(B,A) is the matrix, where each entry a=A_ij is replaced by 721 ## the dxd matrix M_a, representing the linear map x |-> ax with respect to the basis B of 722 ## the field K seen as d-dimensional vectorspace over F. This means that if 723 ## B={b_1,b_2,...,b_d}, then the first row are the coefficients of ab_1 w.r.t. B, and so on. 724 ## Concersely, if we want to implement ShrinkMat, we need to check if the input is 725 ## a blown up matrix. 726 ## This can be done as follows. Let A be a dm x dn matrix. For each dxd block, say M, 727 ## 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 728 ## (Since this would be the a \in K represented as a linear map by M w.r.t. the basis B) 729 730 ## This function is basically the inverse map of 731 ## BlownUpMat. 732 733 local d,vecs,m,n,bmat,blocks,bl,submat,i,j,ij,checklist,row,newmat; 734 d:=Size(B); 735 vecs:=BasisVectors(B); 736 m:=DimensionsMat(mat)[1]; 737 n:=DimensionsMat(mat)[2]; 738 # First we check if m and n are multiples of d 739 if not (IsInt(m/d) and IsInt(n/d)) then 740 Error("The matrix does not have the right dimensions"); 741 fi; 742 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]}); 743 newmat:=[]; 744 for i in [1..m/d] do 745 row:=[]; 746 for j in [1..n/d] do 747 #submat:=blocks[(i-1)*d+j]; #wrong line? 748 submat:=blocks[(i-1)*(m/d)+j]; 749 checklist:=List([1..d],i->(vecs[i]^(-1)*(Sum([1..d],j->submat[i][j]*vecs[j])))); 750 if Size(AsSet(checklist))<>1 then 751 Error("The matrix is not a blown up matrix"); 752 fi; 753 Add(row,checklist[1]); 754 od; 755 Add(newmat,row); 756 od; 757 return newmat; 758 # DO WE NEED TO USE ConvertToMatrixRepNC ? 759 end); 760 761 762## THE OLD ShrinkMat function: 763# InstallGlobalFunction( ShrinkMat, "preimage of BlownUpMat", 764# function( B, mat ) 765# local result, vectors, row, i, j, resrow, b; 766# vectors := BasisVectors( B ); 767# result := []; 768# b := Size(B); 769# for i in [1..Size(mat)/b] do 770# resrow := []; 771# for j in [1..Size(mat[1])/b] do 772# row := mat[(i-1) * b + 1]{[(j-1) * b + 1 .. j * b]}; 773# Add(resrow, row * vectors); 774# od; 775# Add(result, resrow); 776# od; 777# ConvertToMatrixRepNC( result ); 778# return result; 779# end ); 780 781InstallMethod( ShrinkMat, 782 "for a field, a subfield and a matrix", 783 [ IsField,IsField, IsMatrix ], 784 function( f1,f2,mat ) 785 # This gives the same result as ShrinkMat(B,mat) with B the natural basis returned by 786 # Basis(AsVectorSpace(f2,f1)); 787 local B; 788 B:=Basis(AsVectorSpace(f2,f1)); 789 return ShrinkMat(B,mat); 790end ); 791 792 793############################################################################# 794#O ShrinkVec( <f1>, <f2>, <v> ) 795# f2 is a subfield of f1 and v is vector in a vectorspace V2 over f2 796# return the vector of length d2/t, where d2=dim(V2), and t=[f1:f2] 797# using the natural basis Basis(AsVectorSpace(f2,f1)) 798## 799InstallMethod( ShrinkVec, 800 "for a field, a subfield and a vector", 801 [ IsField, IsField, IsVector ], 802 function( f1,f2,v ) 803 local t,d1,d2,basvecs; 804 t:=Dimension(AsVectorSpace(f2,f1)); 805 d1:=Length(v)/t; 806 if IsInt(d1) then 807 basvecs:=BasisVectors(Basis(AsVectorSpace(f2,f1))); 808 return List([1..d1],i->v{[(i-1)*t+1..i*t]}*basvecs); 809 else 810 Error("The length of v is not divisible by the degree of the field extension"); 811 fi; 812end ); 813 814############################################################################# 815#O ShrinkVec( <f1>, <f2>, <v>, basis ) 816# The same operation as above, but now with a basis as extra argument 817InstallMethod( ShrinkVec, 818 "for a field, a subfield and a vector", 819 [ IsField, IsField, IsVector, IsBasis ], 820 function( f1,f2,v,basis ) 821 local t,d1,d2,basvecs; 822 t:=Dimension(AsVectorSpace(f2,f1)); 823 d1:=Length(v)/t; 824 if IsInt(d1) then 825 basvecs:=BasisVectors(basis); 826 return List([1..d1],i->v{[(i-1)*t+1..i*t]}*basvecs); 827 else 828 Error("The length of v is not divisible by the degree of the field extension"); 829 fi; 830end ); 831 832 833 834############################################################################# 835#O BlownUpSubspaceOfProjectiveSpace( <B>, <pg1> ) 836# blows up a projective space by field reduction. 837## 838InstallMethod( BlownUpProjectiveSpace, 839 "for a basis and a projective space", 840 [ IsBasis, IsProjectiveSpace ], 841 function(basis,pg1) 842 local q,t,r,pg2,mat1,mat2; 843 q:=basis!.q; 844 t:=basis!.d; 845 if not pg1!.basefield=GF(q^t) then 846 Error("The basis and the projective space are not compatible!"); 847 fi; 848 r:=Dimension(pg1)+1; 849 pg2:=PG(r*t-1,q); 850 return pg2; 851 end ); 852 853############################################################################# 854#O BlownUpProjectiveSpaceBySubfield( <subfield>, <pg> ) 855# blows up a projective space by field reduction w.r.t. the canonical basis 856## 857InstallMethod( BlownUpProjectiveSpaceBySubfield, 858 "for a field and a projective space", 859 [ IsField, IsProjectiveSpace ], 860 function(subfield,pg) 861 local t,r,field; 862 field:=LeftActingDomain(UnderlyingVectorSpace(pg)); 863 if not subfield in Subfields(field) then 864 Error("The first argument is not a subfield of the basefield of the projective space!"); 865 fi; 866 t:=Dimension(AsVectorSpace(subfield,field)); 867 r:=Dimension(pg)+1; 868 return PG(r*t-1,Size(subfield)); 869 end ); 870 871# CHECKED 1/10/11 jdb 872############################################################################# 873#O BlownUpSubspaceOfProjectiveSpace( <B>, <subspace> ) 874# blows up a subspace of a projective space by field reduction 875## 876InstallMethod( BlownUpSubspaceOfProjectiveSpace, 877 "for a basis and a subspace of a projective space", 878 [ IsBasis, IsSubspaceOfProjectiveSpace ], 879 function(basis,subspace) 880 local pg1,q,t,r,pg2,mat1,mat2; 881 pg1:=AmbientGeometry(subspace); 882 q:=basis!.q; 883 t:=basis!.d; 884 if not pg1!.basefield=GF(q^t) then 885 Error("The basis and the subspace are not compatible!"); 886 fi; 887 r:=Dimension(pg1)+1; 888 pg2:=PG(r*t-1,q); 889 mat1:=subspace!.obj; 890 if subspace!.type = 1 then 891 mat1 := [subspace!.obj]; 892 fi; 893 mat2:=BlownUpMat(basis,mat1); 894 return VectorSpaceToElement(pg2,mat2); 895 end ); 896 897############################################################################# 898#O BlownUpSubspaceOfProjectiveSpaceBySubfield( <subfield>, <subspace> ) 899# blows up a subspace of projective space by field reduction 900# This is w.r.t. to the canonical basis of the field over the subfield. 901## 902InstallMethod( BlownUpSubspaceOfProjectiveSpaceBySubfield, 903 "for a field and a subspace of a projective space", 904 [ IsField, IsSubspaceOfProjectiveSpace], 905 function(subfield,subspace) 906 local pg,field,basis; 907 pg:=AmbientGeometry(subspace); 908 field:=LeftActingDomain(UnderlyingVectorSpace(pg)); 909 if not subfield in Subfields(field) then 910 Error("The first argument is not a subfield of the basefield of the projective space!"); 911 fi; 912 basis:=Basis(AsVectorSpace(subfield,field)); 913 return BlownUpSubspaceOfProjectiveSpace(basis,subspace); 914 end ); 915 916############################################################################# 917#O IsDesarguesianSpreadElement( <B>, <subspace> ) 918# checks if a subspace is a blown up point using field reduction, w.r.t. a basis 919## 920InstallMethod( IsDesarguesianSpreadElement, 921 "for a basis and a subspace of a projective space", 922 [ IsBasis, IsSubspaceOfProjectiveSpace ], 923 function(basis,subspace) 924 local flag,q,t,pg1,pg2,em,basvecs,v,v1,i,mat,rt,r; 925 flag:=true; 926 q:=basis!.q; 927 t:=basis!.d; 928 pg2:=AmbientGeometry(subspace); 929 rt:=Dimension(pg2)+1; 930 if not (Dimension(subspace)+1 = t and IsInt(rt/t)) then 931 flag:=false; 932 else 933 r:=rt/t; 934 pg1:=PG(r-1,q^t); 935 basvecs:=BasisVectors(basis); 936 v:=Coordinates(Random(Points(subspace))); 937 v1:=List([1..r],i->v{[(i-1)*t+1..i*t]}*basvecs); 938 mat:=BlownUpMat(basis,[v1]); 939 flag:=subspace = VectorSpaceToElement(pg2,mat); 940 fi; 941 return flag; 942 end ); 943 944# To be done: check this method! (but it is currently never used). 945############################################################################# 946#O IsBlownUpSubspaceOfProjectiveSpace( <B>, <mat> ) 947# checks if a subspace is blown up using field reduction, w.r.t. a basis 948## 949InstallMethod( IsBlownUpSubspaceOfProjectiveSpace, 950 "for a basis and a subspace of a projective space", 951 [ IsBasis, IsSubspaceOfProjectiveSpace ], 952 # It's important that we include a basis in the arguments, 953 # since for every subspace, provided it has the right dimension, there exists some 954 # basis with respect to which the subspace is blown up. 955 function(basis,subspace) 956 local flag,q,F,t,K,pg2,rt,r,pg1,basvecs,mat1,span,x,v,v1,i; 957 flag:=true; 958 q:=basis!.q; 959 F:=GF(q); 960 t:=basis!.d; 961 K:=GF(q^t); 962 pg2:=AmbientGeometry(subspace); 963 rt:=Dimension(pg2)+1; 964 if not (IsInt(rt/t) and IsInt((Dimension(subspace)+1)/t)) then 965 flag:=false; 966 else 967 r:=rt/t; 968 pg1:=PG(r-1,q^t); 969 basvecs:=BasisVectors(basis); 970 mat1:=[]; 971 span:=[]; 972 repeat 973 repeat x:=Random(Points(subspace)); until not x in span; 974 v:=Coordinates(x); 975 v1:=List([1..r],i->v{[(i-1)*t+1..i*t]}*basvecs); 976 Add(mat1,v1); 977 span:=VectorSpaceToElement(pg2,BlownUpMat(basis,mat1)); 978 until Dimension(span)=Dimension(subspace); 979 if not span = subspace then 980 flag:= false; 981 fi; 982 fi; 983 return flag; 984 end ); 985 986############################################################################# 987# PROJECTIVE SPACES 988############################################################################# 989 990# 991# A recent reference on field reduction of projective spaces is 992# "Field reduction and linear sets in finite geomety" by Lavrauw 993# and Van de Voorde (preprint 2013) 994 995############################################################################# 996#O NaturalEmbeddingByFieldReduction( <geom1>, <field>, <basis> ) 997# <geom2> is a projective space over a field K, <geom1> is a projective space 998# over a field extension L, and considering L as a vector space over K, yields 999# that <geom1> and <geom2> have the same ambient vectorspace over K, then this 1000# operation returns the natural embedding, i.e. with relation to the given basis 1001# of L over K. 1002# Important note: the intertwiner has as source the *Projectivity group* only, not 1003# the full collineation group! 1004## 1005InstallMethod( NaturalEmbeddingByFieldReduction, 1006 "for two projective spaces and a basis", 1007 [ IsProjectiveSpace, IsField, IsBasis ], 1008 function( pg1, f2, basis ) 1009 1010 ## This morphism contains a func and prefunc with built-in check. 1011 1012 local map, f1, d1, d2, t, fun, prefun, g1, gens, newgens, g2, twiner, hom, hominv, q1, q2, pg2; 1013 f1 := pg1!.basefield; 1014 q1 := Size(f1); 1015 q2 := Size(f2); 1016 t := LogInt(q1,q2); 1017 if not q2^t = q1 then 1018 Error( "<f2> is not a subfield of the base field of <pg1> "); 1019 fi; 1020 d1 := pg1!.dimension + 1; 1021 d2 := d1*t; 1022 pg2 := ProjectiveSpace(d2-1,q2); 1023 if not (IsBasis(basis) and f1=GF((basis!.q)^basis!.d) and f2=GF(basis!.q) and d1*(basis!.d)=d2) then 1024 Error("The basis is not a basis or is not compatible with the basefields of the geometries"); 1025 fi; 1026 1027 fun := function( subspace ) # This map blows up a subspace of geom1 to a subspace of geom2 1028 local mat1,mat2; 1029 #return BlownUpSubspaceOfProjectiveSpace(basis,x); 1030 mat1 := subspace!.obj; 1031 if subspace!.type = 1 then 1032 mat1 := [subspace!.obj]; 1033 fi; 1034 mat2 := BlownUpMat(basis,mat1); 1035 return VectorSpaceToElement(pg2,mat2); 1036 end; 1037 1038 prefun := function( subspace ) # This map is the inverse of func and returns an error, or a subspace of geom1 1039 local flag,basvecs,mat1,span,x,v,v1,i,vecs; 1040 flag:=true; 1041 if not subspace in pg2 then 1042 Error("The input is not in the range of the field reduction map!"); 1043 fi; 1044 if not IsInt((Dimension(subspace)+1)/t) then 1045 flag := false; 1046 else 1047 basvecs:=BasisVectors(basis); 1048 vecs := Unpack(UnderlyingObject(subspace)); 1049 mat1 := List(vecs,x->List([1..d1],i->x{[(i-1)*t+1..i*t]}*basvecs)); 1050 span := VectorSpaceToElement(pg2,BlownUpMat(basis,mat1)); 1051 if not span = subspace then 1052 flag := false; 1053 fi; 1054 fi; 1055 if flag= false then 1056 Error("The input is not in the range of the field reduction map!"); 1057 fi; 1058 return VectorSpaceToElement(pg1,mat1); 1059 end; 1060 1061 #prefun := function( x ) #I want to find out why this is not correct... 1062 # return VectorSpaceToElement(pg1,ShrinkMat(basis,x!.obj)); 1063 #end; 1064 1065 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(pg1), 1066 ElementsOfIncidenceStructure(pg2), 1067 fun, false, prefun); 1068 1069 SetIsInjective( map, true ); 1070 1071 ## Now creating intertwiner 1072 ## 12/6/14: added check to see whether m is really a projectivity 1073 1074 hom := function( m ) 1075 local image; 1076 if not IsOne(m!.frob) then 1077 return fail; 1078 Info(InfoFinInG, 1, "<el> is not a projectivity"); 1079 fi; 1080 image := BlownUpMat(basis, m!.mat); 1081 ConvertToMatrixRepNC( image, f2 ); 1082 return CollineationOfProjectiveSpace(image, f2); 1083 end; 1084 1085 hominv := function( m ) 1086 local preimage; 1087 if not IsOne(m!.frob) then 1088 return fail; 1089 Info(InfoFinInG, 1, "<el> is not a projectivity"); 1090 fi; 1091 preimage := ShrinkMat(basis, Unpack(m!.mat)); 1092 ConvertToMatrixRepNC( preimage, f1 ); 1093 return CollineationOfProjectiveSpace(preimage, f1); 1094 end; 1095 1096 g1 := ProjectivityGroup( pg1 ); 1097 gens := GeneratorsOfGroup( g1 ); 1098 newgens := List(gens, hom); 1099 g2 := Group( newgens ); 1100 SetSize(g2, Size(g1)); 1101 twiner := GroupHomomorphismByFunction(g1, g2, hom, hominv); 1102 SetIntertwiner( map, twiner); 1103 return map; 1104 end ); 1105 1106############################################################################# 1107#O NaturalEmbeddingByFieldReduction( <geom1>, <field> ) 1108# <field> is a field K, <geom1> is a projective space 1109# over a field extension L, and considering L as a vector space over K, yields 1110# that a projective space geom2 that has the same ambient vectorspace over K. This 1111# operation returns the natural embedding, i.e. with relation to the standard basis of 1112# L over K. 1113## 1114InstallMethod( NaturalEmbeddingByFieldReduction, 1115 "for a projective space and a field", 1116 [ IsProjectiveSpace, IsField ], 1117 function( pg1, f2 ) 1118 local basis; 1119 basis:=Basis(AsVectorSpace(f2,pg1!.basefield)); 1120 return NaturalEmbeddingByFieldReduction(pg1,f2,basis); 1121 end ); 1122 1123############################################################################# 1124#O NaturalEmbeddingByFieldReduction( <geom1>, <geom2>, <basis> ) 1125# <geom2> is a projective space over a field K, <geom1> is a projective space 1126# over a field extension L, and considering L as a vector space over K, yields 1127# that <geom1> and <geom2> have the same ambient vectorspace over K, then this 1128# operation returns the natural embedding, i.e. with relation to the given basis 1129# of L over K. 1130## 1131InstallMethod( NaturalEmbeddingByFieldReduction, 1132 "for two projective spaces and a basis", 1133 [ IsProjectiveSpace, IsProjectiveSpace, IsBasis ], 1134 function( pg1, pg2, basis ) 1135 return NaturalEmbeddingByFieldReduction(pg1,pg2!.basefield,basis); 1136 end); 1137 1138# CHECKED 28/09/11 jdb 1139############################################################################# 1140#O NaturalEmbeddingByFieldReduction( <geom1>, <geom2> ) 1141# <geom2> is a projective space over a field K, <geom1> is a projective space 1142# over a field extension L, and considering L as a vector space over K, yields 1143# that <geom1> and <geom2> have the same ambient vectorspace over K, then this 1144# operation returns the natural embedding, i.e. with relation to the standard basis of 1145# L over K. 1146## 1147InstallMethod( NaturalEmbeddingByFieldReduction, 1148 "for two projective spaces", 1149 [ IsProjectiveSpace, IsProjectiveSpace ], 1150 function( pg1, pg2 ) 1151 local basis; 1152 basis:=Basis(AsVectorSpace(pg2!.basefield,pg1!.basefield)); 1153 return NaturalEmbeddingByFieldReduction(pg1,pg2!.basefield,basis); 1154 end ); 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165############################################################################ 1166# POLAR SPACES 1167############################################################################## 1168 1169 1170# 1171# Two references on field reduction of polar spaces: 1172# [1] from group theoretic point of view: "Polar spaces and embeddings of classical groups" by Nick Gill 1173# (New Zealand J. Math) 1174# [2] from finite geometry point of view: "Field reduction and linear sets in finite geomety" by Lavrauw 1175# and Van de Voorde (preprint 2013) 1176# 1177# We will use [2] as it is more convenient to us. 1178 1179 1180#### 1181# 1182# First the field reduction of bilinear forms, quadratic forms and hermitian forms 1183# 1184##### 1185 1186 1187############################################################################# 1188#O BilinearFormFieldReduction( <bil1>, <f2>, <alpha>, <basis> ) 1189# <bil1> is a bilinear form over <f1>, <f2> is a subfield of <f1>. 1190# <alpha> determines the linear map from f1 to <f2>, f1 the field extension of <f2> 1191# with basis <basis>. This operation then 1192# returns the bilinear form L(bil1(.,.)), L(x) = T(alpha*x), T the trace from <f1> to <f2>. 1193## 1194# REMARK (ml 31/10/13) The fourth argument is a basis of L over K. This just gives 1195# extra functionality to the user, and it is used as third argument in the 1196# operation ShrinkVec. 1197# There is also a version of this field reduction operation, which does not use 1198# a basis as fourth argument. In this case the standard GAP basis is used. 1199 1200InstallMethod( BilinearFormFieldReduction, 1201 "for a bilinear form and a field", 1202 [ IsBilinearForm, IsField, IsFFE, IsBasis ], 1203 function(bil1,f2,alpha,basis) 1204 # f2 is a subfield of the basefield of the bilinear form bil1 1205 local mat1,f1,d1,t,d2,V2,V1,phi,b2,b2vecs,mat2,i,row,j,bil2; 1206 mat1:=bil1!.matrix; 1207 f1:=bil1!.basefield; 1208 d1:=Size(mat1); 1209 t:=Dimension(AsVectorSpace(f2,f1)); 1210 d2:=d1*t; 1211 V2:=f2^d2; 1212 V1:=f1^d1; 1213 b2:=Basis(V2); 1214 b2vecs:=BasisVectors(b2); 1215 mat2:=[]; 1216 for i in [1..d2] do 1217 row:=[]; 1218 for j in [1..d2] do 1219 Add(row,Trace(f1,f2,alpha*(ShrinkVec(f1,f2,b2vecs[i],basis)*mat1*ShrinkVec(f1,f2,b2vecs[j],basis)))); 1220 od; 1221 Add(mat2,row); 1222 od; 1223 bil2:=BilinearFormByMatrix(mat2,f2); 1224 return bil2; 1225end ); 1226 1227############################################################################# 1228#O BilinearFormFieldReduction( <bil1>, <f2>, <alpha> ) 1229# The same as above, but without specified basis. In this case the canonical basis is used. 1230## 1231InstallMethod( BilinearFormFieldReduction, 1232 "for a bilinear form and a field", 1233 [ IsBilinearForm, IsField, IsFFE ], 1234 function(bil1,f2,alpha) 1235 # f2 is a subfield of the basefield of the bilinear form bil1 1236 return BilinearFormFieldReduction(bil1,f2,alpha,Basis(AsVectorSpace(f2,bil1!.basefield))); 1237end ); 1238 1239############################################################################# 1240#O QuadraticFormFieldReduction( <qf1>, <f2>, <alpha>, <basis> ) 1241# <bil1> is a bilinear form over <f1>, <f2> is a subfield of <f1>. This operation 1242# returns the bilinear form T(alpha*qf1(.,.)), T the trace from <f1> to <f2>. 1243## 1244# REMARK (ml 31/10/13) The fourth argument is a basis of L over K. This just gives 1245# extra functionality to the user, and it is used as third argument in the 1246# operation ShrinkVec. 1247# There is also a version of this field reduction operation, which does not use 1248# a basis as fourth argument. In this case the standard GAP basis is used. 1249 1250InstallMethod( QuadraticFormFieldReduction, 1251 "for a quadratic form and a field", 1252 [ IsQuadraticForm, IsField, IsFFE, IsBasis ], 1253 function(qf1,f2,alpha,basis) 1254 # f2 is a subfield of the basefield for the quadratic form q1 1255 local f1,basvecs,d1,t,d2,V2,V1,phi,b2,b2vecs,mat2,i,bil1,j,qf2; 1256 f1:=qf1!.basefield; 1257 basvecs:=BasisVectors(basis); 1258 d1:=Size(qf1!.matrix); 1259 t:=Dimension(AsVectorSpace(f2,f1)); 1260 d2:=d1*t; 1261 V2:=f2^d2; 1262 V1:=f1^d1; 1263 b2:=Basis(V2); 1264 b2vecs:=BasisVectors(b2); 1265 mat2:=IdentityMat(d2,f2); 1266 for i in [1..d2] do 1267 mat2[i][i]:=Trace(f1,f2,alpha*((ShrinkVec(f1,f2,b2vecs[i],basis))^qf1)); 1268 od; 1269 for i in [1..d2-1] do 1270 for j in [i+1..d2] do 1271 mat2[i][j]:=Trace(f1,f2,alpha*((ShrinkVec(f1,f2,b2vecs[i]+b2vecs[j],basis))^qf1 1272 -(ShrinkVec(f1,f2,b2vecs[i],basis))^qf1-(ShrinkVec(f1,f2,b2vecs[j],basis))^qf1)); 1273 #mat2[j][i]:=mat2[i][j]; THESE entries need to be zero 1274 od; 1275 od; 1276 qf2:=QuadraticFormByMatrix(mat2,f2); 1277 return qf2; 1278end ); 1279 1280############################################################################# 1281# #O QuadraticFormFieldReduction( <qf1>, <f2>, <alpha> ) 1282# The same as above, but without specified basis. In this case the canonical basis is used. 1283## 1284InstallMethod( QuadraticFormFieldReduction, 1285 "for a quadratic form and a field", 1286 [ IsQuadraticForm, IsField, IsFFE ], 1287 function(qf1,f2,alpha) 1288 local basis,qf2; 1289 # f2 is a subfield of the basefield for the quadratic form q1 1290 basis:=Basis(AsVectorSpace(f2,qf1!.basefield)); 1291 qf2:=QuadraticFormFieldReduction(qf1,f2,alpha,basis); 1292 return qf2; 1293end ); 1294 1295 1296############################################################################# 1297#O HermitianFormFieldReduction( <hf1>, <f2>, <alpha>, <basis> ) 1298# <hf1> is a hermitian form over <f1>, <f2> is a subfield of <f1>. This operation 1299# returns the form T(alpha*bil1(.,.)). Depending on the degree of the field extension, this 1300# yields either a bilinear form or a hermitian form. 1301## 1302# REMARK (ml 31/10/13) The fourth argument is a basis of L over K. This just gives 1303# extra functionality to the user, and it is used as third argument in the 1304# operation ShrinkVec. 1305# There is also a version of this field reduction operation, which does not use 1306# a basis as fourth argument. In this case the standard GAP basis is used. 1307 1308InstallMethod( HermitianFormFieldReduction, 1309 "for a hermitian form and a field", 1310 [ IsHermitianForm, IsField, IsFFE, IsBasis ], 1311 function(hf1,f2,alpha,basis) 1312 # f2 is a subfield of the basefield for the hermitian form hf1 1313 # here the basefield is always a square 1314 local f1,d1,t,d2,V2,V1,phi,b2,b2vecs,mat2,i,row,j,hf2; 1315 f1:=hf1!.basefield; 1316 d1:=Size(hf1!.matrix); 1317 t:=Dimension(AsVectorSpace(f2,f1)); 1318 d2:=d1*t; 1319 V2:=f2^d2; 1320 V1:=f1^d1; 1321 b2:=Basis(V2); 1322 b2vecs:=BasisVectors(b2); 1323 mat2:=[]; 1324 for i in [1..d2] do 1325 row:=[]; 1326 for j in [1..d2] do 1327 Add(row,Trace(f1,f2,alpha*([ShrinkVec(f1,f2,b2vecs[i],basis),ShrinkVec(f1,f2,b2vecs[j],basis)]^hf1))); 1328 od; 1329 Add(mat2,row); 1330 od; 1331 # checking parity of t is indeed sufficient to decide, see table in manual. 1332 if IsOddInt(t) then 1333 hf2:=HermitianFormByMatrix(mat2,f2); 1334 else 1335 hf2:=BilinearFormByMatrix(mat2,f2); 1336 fi; 1337 return hf2; 1338end ); 1339 1340 1341############################################################################# 1342# #O HermitianFormFieldReduction( <hf11>, <f2>, <alpha> ) 1343# The same as above, but without specified basis. In this case the canonical basis is used. 1344## 1345 1346InstallMethod( HermitianFormFieldReduction, 1347 "for a hermitian form and a field", 1348 [ IsHermitianForm, IsField, IsFFE ], 1349 function(hf1,f2,alpha) 1350 # f2 is a subfield of the basefield for the hermitian form hf1 1351 # here the basefield is always a square 1352 local basis; 1353 1354 basis:=Basis(AsVectorSpace(f2,hf1!.basefield)); 1355 return HermitianFormFieldReduction(hf1,f2,alpha,basis); 1356end ); 1357 1358 1359 1360########################################################## 1361# 1362# Next the embeddings for polar spaces based on the field reduction of the forms above 1363# 1364############################################################################# 1365 1366# master version for the user: handle all parameters. 1367############################################################################# 1368#O NaturalEmbeddingByFieldReduction( <ps1>, <f2>, <alpha>, <basis>, <bool> ) 1369# returns a geometry morphism, described below. If <bool> is true, then an intertwiner 1370# is computed. 1371## 1372InstallMethod (NaturalEmbeddingByFieldReduction, 1373 "for a polar space, a field, a basis, a finite field element, and a boolean", 1374 [IsClassicalPolarSpace, IsField, IsFFE, IsBasis, IsBool], 1375 function(ps1,f2,alpha,basis,computeintertwiner) 1376 # f2 must be a subfield of the basefield of the classical polar space 1377 local type1,qf1,qf2,ps2,bil1,bil2,hf1,hf2,fun,prefun,f1, 1378 map,hom,hominv,g1,gens,newgens,g2,twiner, t,q1,q2,d1,d2; 1379 type1 := PolarSpaceType(ps1); 1380 f1:=ps1!.basefield; 1381 q1 := Size(f1); 1382 q2 := Size(f2); 1383 t := LogInt(q1,q2); 1384 d1 := ps1!.dimension + 1; 1385 d2 := d1*t; 1386 1387 # 1. the polar space is of orthogonal type 1388 if type1 in ["hyperbolic","elliptic","parabolic"] then 1389 qf1:=QuadraticForm(ps1); 1390 qf2:=QuadraticFormFieldReduction(qf1,f2,alpha,basis); 1391 1392 if IsSingularForm(qf2) then 1393 Error("The field reduction does not yield a natural embedding"); 1394 else ps2:=PolarSpace(qf2); 1395 fi; 1396 # 2. the polar space is of symplectic type 1397 elif type1 in ["symplectic"] then 1398 bil1:=SesquilinearForm(ps1); 1399 bil2:=BilinearFormFieldReduction(bil1,f2,alpha,basis); 1400 ps2:=PolarSpace(bil2); 1401 # 3. the polar space is of hermitian type 1402 elif type1 in ["hermitian"] then 1403 hf1:=SesquilinearForm(ps1); 1404 hf2:=HermitianFormFieldReduction(hf1,f2,alpha,basis); 1405 ps2:=PolarSpace(hf2); 1406 fi; 1407 1408 #em:=NaturalEmbeddingByFieldReduction(AmbientSpace(ps1),AmbientSpace(ps2)); 1409 # this is the field reduction for projective spaces PG(r-1,q^t) -> PG(rt-1,q) 1410 1411 #fun:=function(x) 1412 # local projfun; 1413 # projfun:=em!.fun; 1414 # return VectorSpaceToElement(ps2,projfun(x)!.obj); 1415 #end; 1416 1417 fun := function( subspace ) 1418 local mat1,mat2; 1419 mat1 := subspace!.obj; 1420 if subspace!.type = 1 then 1421 mat1 := [subspace!.obj]; 1422 fi; 1423 mat2 := BlownUpMat(basis,mat1); 1424 return VectorSpaceToElement(ps2,mat2); 1425 end; 1426 1427 1428 #prefun:=function(x) 1429 # local projprefun; 1430 # projprefun:=em!.prefun; 1431 # return VectorSpaceToElement(ps1,projprefun(x)!.obj); 1432 #end; 1433 1434 # new version (2/7/14, much faster since we avoid the repeat loops and Random calls. 1435 prefun := function( subspace ) # This map is the inverse of func and returns an error, or a subspace of geom1 1436 local flag,basvecs,mat1,span,x,v,v1,i,vecs; 1437 flag:=true; 1438 if not subspace in ps2 then 1439 Error("The input is not in the range of the field reduction map!"); 1440 fi; 1441 if not IsInt((Dimension(subspace)+1)/t) then 1442 flag:=false; 1443 else 1444 basvecs:=BasisVectors(basis); 1445 mat1:=[]; 1446 span:=[]; 1447 vecs := Unpack(UnderlyingObject(subspace)); 1448 mat1 := List(vecs,x->List([1..d1],i->x{[(i-1)*t+1..i*t]}*basvecs)); 1449 span := VectorSpaceToElement(AmbientSpace(ps2),BlownUpMat(basis,mat1)); 1450 #the ambientspace makes sure that there is no error message here yet. 1451 if not span=subspace then 1452 flag := false; 1453 fi; 1454 fi; 1455 if flag= false then 1456 Error("The input is not in the range of the field reduction map!"); 1457 fi; 1458 return VectorSpaceToElement(ps1,mat1); 1459 end; 1460 1461 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps1), ElementsOfIncidenceStructure(ps2), 1462 fun, false, prefun); 1463 1464 SetIsInjective( map, true ); 1465 1466 ## Now creating intertwiner 1467 1468 if computeintertwiner then 1469 if (HasIsCanonicalPolarSpace( ps1 ) and IsCanonicalPolarSpace( ps1 )) or 1470 HasCollineationGroup( ps1 ) then 1471 1472 hom := function( m ) 1473 local image; 1474 image := BlownUpMat(basis, m!.mat); 1475 ConvertToMatrixRepNC( image, f2 ); 1476 return CollineationOfProjectiveSpace(image, f2); 1477 end; 1478 hominv := function( m ) 1479 local preimage; 1480 preimage := ShrinkMat(basis, Unpack(m!.mat)); 1481 ConvertToMatrixRepNC( preimage, f1 ); 1482 return CollineationOfProjectiveSpace(preimage, f1); 1483 end; 1484 g1 := IsometryGroup( ps1 ); 1485 gens := GeneratorsOfGroup( g1 ); 1486 newgens := List(gens, hom); 1487 g2 := Group( newgens ); 1488 SetSize(g2, Size(g1)); 1489 twiner := GroupHomomorphismByFunction(g1, g2, hom, hominv); 1490 SetIntertwiner( map, twiner); 1491 fi; 1492 fi; 1493 1494 return map; 1495 end ); 1496 1497 1498#first version: user wants a particular alpha, and basis, agrees with bool=true. 1499############################################################################# 1500#O NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis> ) 1501# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis>, true ) 1502# 1503InstallMethod (NaturalEmbeddingByFieldReduction, 1504 "for a polar space and a field, a finite field element, and a basis", 1505 [IsClassicalPolarSpace, IsField, IsFFE, IsBasis], 1506 function(ps1,f2,alpha,basis) 1507 return NaturalEmbeddingByFieldReduction(ps1,f2,alpha,basis,true); 1508 end ); 1509 1510#second particular version: user wants a particular alpha, and bool, agrees with basis. 1511############################################################################# 1512#O NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <bool> ) 1513# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis>, true ) 1514# where <basis> is the canonical basis of BaseField(geom1) over <f2>. 1515# 1516InstallMethod (NaturalEmbeddingByFieldReduction, 1517 "for a polar space and a field, a finite field element, and a boolean", 1518 [IsClassicalPolarSpace, IsField, IsFFE, IsBool], 1519 function(ps1,f2,alpha,bool) 1520 return NaturalEmbeddingByFieldReduction(ps1,f2,alpha,Basis(AsVectorSpace(f2,BaseField(ps1))),bool); 1521 end ); 1522 1523#third particular version: user wants a particular alpha, agrees with basis and bool. 1524############################################################################# 1525#O NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha> ) 1526# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis>, true ) 1527# where <basis> is the canonical basis of BaseField(geom1) over <f2>. 1528# 1529InstallMethod (NaturalEmbeddingByFieldReduction, 1530 "for a polar space, a field, and a finite field element", 1531 [IsClassicalPolarSpace, IsField, IsFFE], 1532 function(ps1,f2,alpha) 1533 return NaturalEmbeddingByFieldReduction(ps1,f2,alpha,Basis(AsVectorSpace(f2,BaseField(ps1))),true); 1534 end ); 1535 1536# fourth particular version: user agrees but wants to control intertwiner 1537############################################################################# 1538#O NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <bool> ) 1539# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <one>, <basis>, <bool> ) 1540# where <basis> is the canonical basis of BaseField(geom1) over <f2>, and 1541# <one> is One(BaseField(geom1)). This might be usefull if you agree with the defaults, but doesn't 1542# want the intertwiner. 1543# 1544InstallMethod (NaturalEmbeddingByFieldReduction, 1545 "for a polar space, a field, and a boolean", 1546 [IsClassicalPolarSpace, IsField, IsBool], 1547 function(ps1,f2,bool) 1548 return NaturalEmbeddingByFieldReduction(ps1,f2,One(f2),Basis(AsVectorSpace(f2,BaseField(ps1))),bool); 1549 end ); 1550 1551# fifth particular version: user agrees with everything 1552############################################################################# 1553#O NaturalEmbeddingByFieldReduction( <geom1>, <f2> ) 1554# returns NaturalEmbeddingByFieldReduction( <geom1>, <f2>, <alpha>, <basis>, true ) 1555# where <basis> is the canonical basis of BaseField(geom1) over <f2>, and 1556# <alpha> is One(f2). 1557InstallMethod (NaturalEmbeddingByFieldReduction, 1558 "for a polar space and a field", 1559 [IsClassicalPolarSpace, IsField], 1560 function(ps1,f2) 1561 return NaturalEmbeddingByFieldReduction(ps1,f2,One(f2),Basis(AsVectorSpace(f2,BaseField(ps1))),true); 1562 end ); 1563 1564# CHECKED 28/09/11 jdb 1565############################################################################# 1566#O NaturalEmbeddingByFieldReduction( <geom1>, <geom2> ) 1567# returns NaturalEmbeddingByFieldReduction(<geom1>, <geom2> ) 1568## 1569InstallMethod( NaturalEmbeddingByFieldReduction, 1570 "for two polar spaces", 1571 [ IsClassicalPolarSpace, IsClassicalPolarSpace ], 1572 function( geom1, geom2 ) 1573 return NaturalEmbeddingByFieldReduction( geom1, geom2, true ); 1574 end ); 1575 1576# ml 31/10/2013 1577# changed with new prefun 2/7/14 jdb 1578##################################################################################### 1579# O NaturalEmbeddingByFieldReduction 1580# This NaturalEmbeddingByFieldReduction takes two polar spaces and returns an embedding 1581# obtained by field reduction if such an embedding exists, otherwise it returns an error. 1582## 1583InstallMethod (NaturalEmbeddingByFieldReduction, 1584 "for two classical polar spaces", 1585 [IsClassicalPolarSpace, IsClassicalPolarSpace, IsBool], 1586 function(ps1, ps2, computeintertwiner) 1587 1588 ##################################################################### 1589 # List of possible embeddings of ps1 in PG(r-1, q^t) -> ps2 in PG(rt-1, q) 1590 # 1591 # Hermitian 1592 # H -> H (t odd) 1593 # H -> W (t even) 1594 # H -> Q+ (t even, r even) 1595 # H -> Q- (t even, r odd) 1596 # 1597 # Symplectic 1598 # W -> W (always) 1599 # 1600 # Orthogonal 1601 # r even 1602 # Q+ -> Q+ (always) 1603 # Q- -> Q- (always) 1604 # r odd 1605 # Q -> Q (t odd, q odd) 1606 # Q -> Q+ (t even, q odd) 1607 # Q -> Q- (t even, q odd) 1608 ##################################################################### 1609 1610 local t, r, type1, type2, form1, f1, f2, newps, sigma, map, 1611 gamma, bil1, q, n, alpha, basis, is_square, iso, form2, fun, prefun, 1612 c1, c2, cps2, cps2inv, d1, hom, hominv, g1, g2, gens, newgens, twiner; 1613 1614 type1 := PolarSpaceType(ps1); 1615 type2 := PolarSpaceType(ps2); 1616 if type1 in ["hyperbolic", "parabolic", "elliptic"] then 1617 form1 := QuadraticForm(ps1); 1618 else 1619 form1 := SesquilinearForm(ps1); 1620 fi; 1621 d1 := ps1!.dimension+1; 1622 f1 := ps1!.basefield; 1623 f2 := ps2!.basefield; 1624 q := Size(f2); 1625 1626 if not IsSubset(f1,f2) then 1627 Error("Fields are incompatible"); 1628 fi; 1629 t := Log(Size(f1),q); 1630 r := ProjectiveDimension(ps1)+1; 1631 basis := CanonicalBasis(AsVectorSpace(f2,f1)); 1632 is_square := function(x, f) return IsZero(x) or IsEvenInt(LogFFE(x,PrimitiveRoot(f))); end; 1633 1634 if IsSesquilinearForm(form1) then 1635 if type1 = "hermitian" and type2 = "hermitian" and IsOddInt(t) then 1636 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction"); 1637 # need alpha to be fixed by sigma: 1638 alpha := One(f1); 1639 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1640 form2 := HermitianFormFieldReduction(form1,f2,alpha,basis); 1641 elif type1 = "hermitian" and type2 = "symplectic" and IsEvenInt(t) then 1642 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction"); 1643 if IsEvenInt(q) then 1644 # need alpha to be fixed by sigma: 1645 alpha := One(f1); 1646 else 1647 # need sigma(alpha)=-alpha 1648 sigma := Sqrt(Size(f1)); 1649 alpha := First(f1, x->not IsZero(x) and x^sigma = -x); 1650 fi; 1651 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1652 form2 := HermitianFormFieldReduction(form1,f2,alpha,basis); 1653 1654 elif type1 = "hermitian" and type2 = "hyperbolic" and IsEvenInt(t) and IsEvenInt(r) 1655 and IsOddInt(q) then 1656 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction"); 1657 alpha := One(f1); 1658 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1659 form2 := HermitianFormFieldReduction(form1,f2,alpha,basis); 1660 1661 elif type1 = "hermitian" and type2 = "elliptic" and IsEvenInt(t) and IsOddInt(r) 1662 and IsOddInt(q) then 1663 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction"); 1664 alpha := One(f1); 1665 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1666 form2 := HermitianFormFieldReduction(form1,f2,alpha,basis); 1667 1668 elif type1 = "symplectic" and type2 = "symplectic" then 1669 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction"); 1670 alpha := One(f1); 1671 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1672 form2 := BilinearFormFieldReduction(form1,f2,alpha,basis); 1673 else 1674 Error("These polar spaces are not suitable for field reduction."); 1675 fi; 1676 else # form1 is a quadratic form 1677 if type1 = "hyperbolic" and type2 = "hyperbolic" then 1678 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction"); 1679 alpha := One(f1); 1680 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1681 form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis); 1682 elif type1 = "elliptic" and type2 = "elliptic" then 1683 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction"); 1684 alpha := One(f1); 1685 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1686 form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis); 1687 elif type1 = "parabolic" and type2 = "parabolic" and IsOddInt(t) and IsOddInt(q) then 1688 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction"); 1689 alpha := One(f1); 1690 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1691 form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis); 1692 1693 # If ps1 is parabolic and ps2 is hyperbolic or elliptic, 1694 # then the choice of alpha that we will use for the embedding 1695 # depends on the isometry type of ps1. The isometry type is determined by 1696 # gamma being a square or not, where gamma is 1697 # (-1)^(r-1)/2 times the determinant of the bilinear form, divided by two. 1698 # The conditions we use here are taken from [Lavrauw - Van de Voorde: "Field 1699 # reduction and linear sets in finite geometry", preprint], they are 1700 # somewhat easier to work with than the conditions in Gill's paper. 1701 1702 elif type1 = "parabolic" and type2 = "hyperbolic" and IsEvenInt(t) and IsOddInt(q) then 1703 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction: Parabolic -> Hyperbolic"); 1704 bil1:=AssociatedBilinearForm(form1); # important to use this instead of 1705 # BilinearFormByQuadraticForm (see documentation of the Forms package). 1706 gamma:=((-1)^((r-1)/2)) * Determinant(bil1!.matrix)/2; 1707 if q^(t/2) mod 4 = 1 then # the product of alpha and gamma must be a nonsquare 1708 alpha := First(f1, a -> not IsZero(a) and not is_square( a*gamma, f1 ) ); 1709 else # the product of alpha and gamma must be a square 1710 alpha := First(f1, a -> not IsZero(a) and is_square( a*gamma, f1 ) ); 1711 fi; 1712 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1713 form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis); 1714 1715 elif type1 = "parabolic" and type2 = "elliptic" and IsEvenInt(t) and IsOddInt(q) then 1716 Info(InfoFinInG, 1, "These polar spaces are suitable for field reduction: Parabolic -> Elliptic"); 1717 bil1:=AssociatedBilinearForm(form1); # important to use this instead of 1718 # BilinearFormByQuadraticForm (see documentation of the Forms package). 1719 gamma:=((-1)^((r-1)/2)) * Determinant(bil1!.matrix)/2; 1720 if q^(t/2) mod 4 = 1 then # the product of alpha and gamma must be a square 1721 alpha := First(f1, a -> not IsZero(a) and is_square( a*gamma, f1 ) ); 1722 else # the product of alpha and gamma must be a nonsquare 1723 alpha := First(f1, a -> not IsZero(a) and not is_square( a*gamma, f1 ) ); 1724 fi; 1725 #map := NaturalEmbeddingByFieldReduction( ps1, f2, alpha, basis ); 1726 form2 := QuadraticFormFieldReduction(form1,f2,alpha,basis); 1727 1728 else 1729 Error("These polar spaces are not suitable for field reduction."); 1730 fi; 1731 fi; 1732 1733 c1 := BaseChangeToCanonical( form2 ); 1734 if not IsCanonicalPolarSpace(ps2) then 1735 if type2 in ["parabolic", "hyperbolic", "elliptic"] then 1736 c2 := BaseChangeToCanonical(QuadraticForm(ps2)); 1737 else 1738 c2 := BaseChangeToCanonical(SesquilinearForm(ps2)); 1739 fi; 1740 cps2 := c2^-1 * c1; 1741 else 1742 cps2 := c1; 1743 fi; 1744 cps2inv := cps2^-1; 1745 1746 fun := function( subspace ) 1747 local mat1,mat2; 1748 mat1 := subspace!.obj; 1749 if subspace!.type = 1 then 1750 mat1 := [subspace!.obj]; 1751 fi; 1752 mat2 := BlownUpMat(basis,mat1); 1753 return VectorSpaceToElement(ps2,mat2*cps2inv); 1754 end; 1755 1756 #this version of prefun is new (2/7/14). 1757 1758 prefun := function( subspace ) # This map is the inverse of func and returns an error, or a subspace of geom1 1759 local flag,basvecs,mat1,span,x,v,v1,i,vecs; 1760 flag:=true; 1761 if not subspace in ps2 then 1762 Error("The input is not in the range of the field reduction map!"); 1763 fi; 1764 if not IsInt((Dimension(subspace)+1)/t) then 1765 flag:=false; 1766 else 1767 basvecs:=BasisVectors(basis); 1768 vecs := Unpack(UnderlyingObject(subspace))*cps2; 1769 mat1 := List(vecs,x->List([1..d1],i->x{[(i-1)*t+1..i*t]}*basvecs)); 1770 span := VectorSpaceToElement(AmbientSpace(ps2),BlownUpMat(basis,mat1)*cps2inv); #the ambientspace makes sure that there is no error message here yet. 1771 if not span = subspace then 1772 flag := false; 1773 fi; 1774 fi; 1775 if flag = false then 1776 Error("The input is not in the range of the field reduction map!"); 1777 fi; 1778 return VectorSpaceToElement(ps1,mat1); 1779 end; 1780 1781 #iso := IsomorphismPolarSpacesNC(AmbientGeometry(Range(map)), ps2); 1782 #morphism := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps1), 1783 # ElementsOfIncidenceStructure(ps2), 1784 # x -> iso!.fun(map!.fun(x)), false, x -> map!.prefun(iso!.prefun(x)) ); 1785 1786 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps1), 1787 ElementsOfIncidenceStructure(ps2), 1788 fun, false, prefun); 1789 SetIsInjective( map, true ); 1790 1791 if (HasIsCanonicalPolarSpace( ps1 ) and IsCanonicalPolarSpace( ps1 )) or 1792 HasCollineationGroup( ps1 ) then 1793 if computeintertwiner then 1794 hom := function( m ) 1795 local image; 1796 image := BlownUpMat(basis, m!.mat); 1797 ConvertToMatrixRepNC( image, f2 ); 1798 return CollineationOfProjectiveSpace(cps2 * image * cps2inv, f2); 1799 end; 1800 hominv := function( m ) 1801 local preimage; 1802 preimage := ShrinkMat(basis, cps2inv * Unpack(m!.mat) * cps2); 1803 ConvertToMatrixRepNC( preimage, f1 ); 1804 return CollineationOfProjectiveSpace(preimage, f1); 1805 end; 1806 g1 := IsometryGroup( ps1 ); 1807 gens := GeneratorsOfGroup( g1 ); 1808 newgens := List(gens, hom); 1809 g2 := Group( newgens ); 1810 SetSize(g2, Size(g1)); 1811 twiner := GroupHomomorphismByFunction(g1, g2, hom, hominv); 1812 SetIntertwiner( map, twiner); 1813 fi; 1814 fi; 1815 1816 SetIntertwiner(map, hom ); 1817 1818 return map; 1819end ); 1820 1821# added 2/7/14 jdb 1822##################################################################################### 1823# O NaturalEmbeddingByFieldReduction 1824# This NaturalEmbeddingByFieldReduction takes two polar spaces and returns an embedding 1825# obtained by field reduction if such an embedding exists, otherwise it returns an error. 1826## 1827InstallMethod (NaturalEmbeddingByFieldReduction, 1828 "for two classical polar spaces", 1829 [IsClassicalPolarSpace, IsClassicalPolarSpace], 1830 function(ps1, ps2) 1831 return NaturalEmbeddingByFieldReduction(ps1,ps2,true); 1832 end ); 1833 1834 1835############################################################################# 1836# 1837# 1838# Embeddings by SUBFIELDS 1839# 1840# 1841############################################################################# 1842 1843 1844# CHECKED 11/06/14 jdb 1845# found another cvec/cmat bug thanks to our testuser Geertrui! 1846############################################################################# 1847#O NaturalEmbeddingBySubfield( <geom1>, <geom2> ) 1848# returns the embedding of <geom1> in <geom2>, two projective spaces of the 1849# same dimension, over the fields K and L respectively, where L is an extension 1850# of K. 1851## 1852InstallMethod( NaturalEmbeddingBySubfield, 1853 "for two projective spaces", 1854 [ IsProjectiveSpace, IsProjectiveSpace ], 1855 function( geom1, geom2 ) 1856 local map, f1, f2, func, prefun, g1, g2, 1857 gens1, gens2, hom, twinerfunc, twinerprefun; 1858 f1 := geom1!.basefield; 1859 f2 := geom2!.basefield; 1860 if geom1!.dimension <> geom2!.dimension or not IsSubset(f2,f1) then 1861 Error("Dimensions and/or field sizes are incompatible"); return; 1862 fi; 1863 1864 func := x -> VectorSpaceToElement(geom2, Unpack(x!.obj)); #here an unpack is needed to avoid field incompatibilites. 1865 1866 prefun := function( x ) 1867 local xmat; 1868 if not ForAll( Flat(x!.obj), i -> i in f1 ) then 1869 Error("Element does not have all of its coordinates in ", f1); 1870 fi; 1871 xmat := Unpack(x!.obj); 1872 if x!.type = 1 then 1873 ConvertToVectorRep(xmat); 1874 else 1875 ConvertToMatrixRep(xmat); 1876 fi; 1877 return VectorSpaceToElement(geom1, xmat ); # the same here. 1878 end; 1879 1880 map := GeometryMorphismByFunction( ElementsOfIncidenceStructure(geom1), 1881 ElementsOfIncidenceStructure(geom2), 1882 func, false, prefun ); 1883 SetIsInjective( map, true ); 1884 1885 ## Intertwiner... 1886 1887 twinerfunc := function( x ) 1888 local xmat; 1889 if not IsOne(x!.frob) then 1890 Info(InfoFinInG, 1, "<el> is not in the source of the intertwiner"); 1891 return fail; 1892 fi; 1893 xmat := x!.mat; 1894 return CollineationOfProjectiveSpace( Unpack(x!.mat), f2 ); #here was an unpack needed! 1895 end; 1896 1897 twinerprefun := function( y ) 1898 local ymat,q; 1899 if not IsOne(y!.frob) then 1900 Info(InfoFinInG, 1, "<el> is not in the range of the intertwiner"); 1901 return fail; 1902 fi; 1903 ymat := Unpack(y!.mat); #here too! 1904 q := ConvertToMatrixRep(ymat); 1905 if not q = Size(f1) then 1906 Info(InfoFinInG, 1, "<el> is not in the range of the intertwiner"); 1907 return fail; 1908 fi; 1909 return CollineationOfProjectiveSpace(ymat,f1); 1910 end; 1911 1912 g1 := ProjectivityGroup( geom1 ); 1913 gens1 := GeneratorsOfGroup( g1 ); 1914 gens2 := List(gens1, twinerfunc ); 1915 g2 := Group(gens2); 1916 SetSize(g2, Size(g1)); 1917 hom := GroupHomomorphismByFunction(g1, g2, twinerfunc, twinerprefun); 1918 SetIntertwiner(map, hom); 1919 return map; 1920 end ); 1921 1922# CHECKED 25/7/14 jdb. With new (and correct now) prefun. 1923# cmat changes 21/3/14 and 24/3/2014, after a nice cultural weekend :-) 1924############################################################################# 1925#O NaturalEmbeddingBySubfield( <geom1>, <geom2>, <bool> ) 1926# returns the embedding of <geom1> in <geom2>, two polar spaces of the 1927# same dimension, over the fields K and L respectively, where L is an extension 1928# of K. An Intertwiner is computed if <bool> is true. 1929# The prefun principle is taken from NaturalDualityHermitian. 1930## 1931InstallMethod( NaturalEmbeddingBySubfield, 1932 "for two polar spaces and a boolean", 1933 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsBool ], 1934 function( geom1, geom2, computeintertwiner ) 1935 local map, f1, f2, q1, q2, gamma, func, prefun, g1, g2, gens1, gens2, f, 1936 r, type1, type2, iso, gram, newgram, form, ps, hom, twinerfunc, twinerprefun, 1937 change, invchange, basis, d, n, bmat, one, i, c1, c2, form2; 1938 1939 # 1940 # Check Kleidman and Liebeck Table 4.5.A 1941 # There, the conditions on the polarity types can be found. 1942 # 1943 # All possible cases: 1944 # W -> W 1945 # W -> H (quadratic) 1946 # O^eps -> H (q odd, quadratic) 1947 # H -> H (odd degree) 1948 # O^eps -> O^eps' (eps = eps'^r) 1949 # 1950 1951 f1 := geom1!.basefield; q1 := Size(f1); 1952 f2 := geom2!.basefield; q2 := Size(f2); 1953 type1 := PolarSpaceType(geom1); 1954 type2 := PolarSpaceType(geom2); 1955 form2 := SesquilinearForm( geom2 ); #will be changed in last case only 1956 if geom1!.dimension = geom2!.dimension and IsSubset(f2, f1) then 1957 r := LogInt(q2, q1); 1958 gram := GramMatrix( SesquilinearForm(geom1) ); 1959 if [type1, type2] = ["symplectic", "hermitian" ] and r = 2 then 1960 gamma := First( f2, t -> not IsZero(t) and IsZero(t^q1+t)); 1961 newgram := gamma * gram; 1962 form := HermitianFormByMatrix(newgram, f2); 1963 elif [type1, type2] = ["symplectic", "symplectic"] then 1964 form := BilinearFormByMatrix(gram, f2); 1965 elif [type1, type2] = ["hermitian", "hermitian"] and IsOddInt(r) then 1966 form := HermitianFormByMatrix(gram, f2); 1967 elif type1 in ["elliptic", "parabolic", "hyperbolic"] and 1968 type2 = "hermitian" and r=2 and IsOddInt(q2) then 1969 form := HermitianFormByMatrix(gram, f2); 1970 elif (IsOddInt(r) and type2 in ["elliptic", "parabolic", "hyperbolic"] and 1971 type1 = type2) or (IsEvenInt(r) and [type1, type2] in [["elliptic","hyperbolic"], 1972 ["hyperbolic","hyperbolic"], ["parabolic","parabolic"]]) then 1973 gram := GramMatrix( QuadraticForm(geom1) ); 1974 form := QuadraticFormByMatrix(gram, f2); 1975 form2 := QuadraticForm(geom2); 1976 else 1977 Error("Not possible for these geometries\n"); 1978 fi; 1979 else 1980 Error("Dimensions and/or field sizes are incompatible"); return; 1981 fi; 1982 1983 c1 := BaseChangeToCanonical( form ); 1984 if not IsCanonicalPolarSpace( geom2 ) then 1985 c2 := BaseChangeToCanonical( form2 ); 1986 change := c2^-1 * c1; 1987 else 1988 change := c1; 1989 fi; 1990 invchange := change^-1; 1991 1992 d := geom1!.dimension+1; 1993 basis := Basis(AsVectorSpace(f1,f2)); 1994 n := Length(basis); 1995 bmat := NullMat(d,d*n,f1); 1996 one := One(f1); 1997 for i in [1..d] do 1998 bmat[i][1+(i-1)*n] := one; 1999 od; 2000 2001 func := function( el ) 2002 return VectorSpaceToElement(geom2,Unpack(el!.obj) * invchange); 2003 end; 2004 2005 prefun := function( el ) 2006 local vec,nvec,list,bgen; 2007 vec := Unpack(el!.obj) * change; 2008 if el!.type = 1 then 2009 nvec := vec / First(vec,x->not IsZero(x)); 2010 if not ForAll( nvec, i -> i in f1 ) then 2011 Error("Element is not in the range of the geometry morphism"); 2012 fi; 2013 ConvertToVectorRepNC(nvec,f1); #necessary, also if appearantly vec is already over f1. 2014 else 2015 bgen := BlownUpMat(basis,vec); 2016 list := SumIntersectionMat(bgen, bmat)[2]; #hard coded Meet operation :-) 2017 nvec := List(list,x->ShrinkVec(f2,f1,x)); 2018 if not (IsMatrix(nvec) and Length(nvec) = el!.type) then 2019 #return fail; 2020 Error("Element is not in the range of the geometry morphism"); 2021 fi; 2022 ConvertToMatrixRepNC(nvec,f1); 2023 fi; 2024 return VectorSpaceToElement(geom1,nvec); 2025 end; 2026 2027 map := GeometryMorphismByFunction( ElementsOfIncidenceStructure(geom1), 2028 ElementsOfIncidenceStructure(geom2), 2029 func, false, prefun ); 2030 SetIsInjective( map, true ); 2031 2032 twinerfunc := function( x ) 2033 local xmat; 2034 if not IsOne(x!.frob) then 2035 Info(InfoFinInG, 1, "<el> is not in the source of the intertwiner"); 2036 return fail; 2037 fi; 2038 xmat := x!.mat; 2039 return CollineationOfProjectiveSpace( change * Unpack(x!.mat) * invchange, f2 ); #here was an unpack needed! 2040 end; 2041 2042 twinerprefun := function( y ) 2043 local ymat,q; 2044 if not IsOne(y!.frob) then 2045 Info(InfoFinInG, 1, "<el> is not in the range of the intertwiner"); 2046 return fail; 2047 fi; 2048 ymat := invchange * Unpack(y!.mat) * change; #here too! 2049 ymat := ymat/First(ymat[1],x->not IsZero(x)); 2050 if not ForAll( Flat(ymat), i -> i in f1 ) then 2051 Info(InfoFinInG, 1, "<el> is not in the range of the intertwiner"); 2052 return fail; 2053 else 2054 ConvertToMatrixRepNC(ymat,f1); 2055 return CollineationOfProjectiveSpace(ymat,f1); 2056 fi; 2057 end; 2058 2059 if HasCollineationGroup( geom1 ) then 2060 g1 := SimilarityGroup( geom1 ); 2061 gens1 := GeneratorsOfGroup( g1 ); 2062 gens2 := List(gens1, twinerfunc ); 2063 g2 := Group(gens2); 2064 SetSize(g2, Size(g1)); 2065 hom := GroupHomomorphismByFunction(g1, g2, twinerfunc, twinerprefun); 2066 SetIntertwiner(map, hom); 2067 else 2068 Info(InfoFinInG, 1, "No intertwiner computed. <geom1> must have a collineation group computed"); 2069 fi; 2070 2071 return map; 2072 2073 end ); 2074 2075# CHECKED 28/09/11 jdb 2076############################################################################# 2077#O NaturalEmbeddingBySubfield( <geom1>, <geom2> ) 2078# returns NaturalEmbeddingBySubfield( <geom1>, <geom2>, true ) 2079## 2080InstallMethod( NaturalEmbeddingBySubfield, 2081 "for two polar spaces", 2082 [ IsClassicalPolarSpace, IsClassicalPolarSpace ], 2083 function( geom1, geom2 ) 2084 return NaturalEmbeddingBySubfield( geom1, geom2, true ); 2085 end ); 2086 2087 2088############################################################################# 2089# 2090# PROJECTIONS 2091# 2092############################################################################# 2093 2094# CHECKED 28/09/11 jdb 2095############################################################################# 2096#O NaturalProjectionBySubspace( <ps>, <v> ) 2097# returns the morphism from the projective space to the quotient space of the 2098# subspace <v>. It is checked if <v> is a subspace of <ps>. 2099## 2100InstallMethod( NaturalProjectionBySubspace, 2101 "for a projective space and a subspace of the projective space", 2102 [ IsProjectiveSpace, IsSubspaceOfProjectiveSpace ], 2103 function(ps, v) 2104 2105 ## map from geometry of elements incident with v 2106 ## to the projective space of lesser dimension 2107 2108 local psdim, b, vdim, vs, sub, func, pre, 2109 Wvectors, mb, compl, gen, bas, img, 2110 canbas, zero, basimgs, ps2, map, hom, f; 2111 if not v in ps then 2112 Error("<v> is not an element of <ps>"); 2113 fi; 2114 psdim := ps!.dimension; 2115 f := ps!.basefield; 2116 b := Unpack(v!.obj); #cmat unpack necessary for infra. 2117 vdim := v!.type; 2118 if vdim = 1 then b:=[b]; fi; 2119 vs := ps!.vectorspace; 2120 sub := Subspace(vs, b); 2121 2122 ## if v has dimension j, then the new geometry 2123 ## has dimension psdim - vdim 2124 2125 ps2 := ProjectiveSpace( psdim - vdim, f ); 2126 2127 ## This code was adapted from Thomas Breuer's 2128 ## "NaturalHomomorphismBySubspace" code 2129 2130 Wvectors:= b; 2131 mb:= MutableBasis( f, Wvectors ); 2132 compl:= []; 2133 for gen in BasisVectors( Basis( vs ) ) do 2134 if not IsContainedInSpan( mb, gen ) then 2135 Add( compl, gen ); 2136 CloseMutableBasis( mb, gen ); 2137 fi; 2138 od; 2139 bas := BasisNC( vs, Concatenation( Wvectors, compl ) ); 2140 2141 # Compute the linear mapping by images. 2142 img:= FullRowModule( f, Length( compl ) ); 2143 canbas:= CanonicalBasis( img ); 2144 zero:= Zero( img ); 2145 # Matrix of the linear transformation we want 2146 basimgs:= Concatenation( ListWithIdenticalEntries( Size(Wvectors), zero ), 2147 BasisVectors( canbas ) ); 2148 2149 func := function( x ) 2150 local y; 2151 if not v in x then 2152 Error("Subspace is not incident with the subspace of projection"); 2153 fi; 2154 y := List(Unpack(Unwrap(x)),i-> Coefficients(bas,i))*basimgs; #was x^_, now UnWrap(x), cmat unpack 2155 if not IsEmpty(y) then 2156 ## Note: TriangulizeMat does not return a matrix of full 2157 ## rank, whereas SemiEchelonMat does! 2158 y := SemiEchelonMat(y)!.vectors; ## JB: 3/11/2012. Moved this line from the "else" to here so it also applies to the first case. 2159 2160 if x!.type - vdim = 1 then 2161 y := y[1]; 2162 #ConvertToVectorRep(y, f); 2163 fi; 2164 #else 2165 # ConvertToMatrixRepNC(y, f); 2166 #fi; 2167 fi; 2168 if not IsEmpty(y) then 2169 #return Wrap(ps2, x!.type - vdim, y); 2170 return VectorSpaceToElement(ps2,y); 2171 else 2172 return EmptySubspace(ps2); 2173 fi; 2174 end; 2175 pre := function( y ) 2176 local x; 2177 x := Unpack(y!.obj); #cmat unpack 2178 if y!.type = 1 then x := [x]; fi; 2179 x := Concatenation(x * compl, b); 2180 #ConvertToMatrixRepNC(x, f); 2181 return VectorSpaceToElement(ps, x); 2182 end; 2183 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps), 2184 ElementsOfIncidenceStructure(ps2), func, pre ); 2185 return map; 2186 end ); 2187 2188# CHECKED 28/09/11 jdb 2189############################################################################# 2190#O NaturalProjectionBySubspaceNC( <ps>, <v> ) 2191## This operation is just like its namesake except that it 2192## has no checks 2193## 2194InstallMethod( NaturalProjectionBySubspaceNC, 2195 "for a projective space and a singular subspace", 2196 [ IsProjectiveSpace, IsSubspaceOfProjectiveSpace ], 2197 function(ps, v) 2198 2199 ## map from geometry of elements incident with v 2200 ## to the projective space of lesser dimension 2201 2202 local psdim, b, vdim, vs, sub, func, pre, 2203 Wvectors, mb, compl, gen, bas, img, 2204 canbas, zero, basimgs, ps2, map, hom, f; 2205 2206 psdim := ps!.dimension; 2207 f := ps!.basefield; 2208 b := v!.obj; 2209 vdim := v!.type; 2210 if vdim = 1 then b:=[b]; fi; 2211 vs := ps!.vectorspace; 2212 sub := Subspace(vs, b); 2213 2214 ## if v has dimension j, then the new geometry 2215 ## has dimension psdim - vdim 2216 2217 ps2 := ProjectiveSpace( psdim - vdim, f ); 2218 2219 ## This code was adapted from Thomas Breuer's 2220 ## "NaturalHomomorphismBySubspace" code 2221 2222 Wvectors:= b; 2223 mb:= MutableBasis( f, Wvectors ); 2224 compl:= []; 2225 for gen in BasisVectors( Basis( vs ) ) do 2226 if not IsContainedInSpan( mb, gen ) then 2227 Add( compl, gen ); 2228 CloseMutableBasis( mb, gen ); 2229 fi; 2230 od; 2231 bas := BasisNC( vs, Concatenation( Wvectors, compl ) ); 2232 2233 # Compute the linear mapping by images. 2234 img:= FullRowModule( f, Length( compl ) ); 2235 canbas:= CanonicalBasis( img ); 2236 zero:= Zero( img ); 2237 # Matrix of the linear transformation we want 2238 basimgs:= Concatenation( ListWithIdenticalEntries( Size(Wvectors), zero ), 2239 BasisVectors( canbas ) ); 2240 2241 func := function( x ) 2242 local y; 2243 y := List(x!.obj,i-> Coefficients(bas,i))*basimgs; 2244 y := SemiEchelonMat(y)!.vectors; 2245 if x!.type - vdim = 1 then 2246 y := y[1]; 2247 ConvertToVectorRep(y, f); 2248 else 2249 ConvertToMatrixRepNC(y, f); 2250 fi; 2251 if not IsEmpty(y) then 2252 return Wrap(ps2, x!.type - vdim, y); 2253 else 2254 return EmptySubspace(ps2); 2255 fi; 2256 end; 2257 2258 pre := function( y ) 2259 local x; 2260 x := y!.obj; 2261 if y!.type = 1 then x := [x]; fi; 2262 x := Concatenation(x * compl, b); 2263 ConvertToMatrixRepNC(x, f); 2264 return VectorSpaceToElement(ps, x); 2265 end; 2266 2267 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps), 2268 ElementsOfIncidenceStructure(ps2), func, pre ); 2269 return map; 2270 end ); 2271 2272# CHECKED 28/09/11 jdb 2273############################################################################# 2274#O QUO( <ps>, <v> ) 2275# returns the quotient space of the subspace <v> of the projective space <ps> 2276# it is checked if <v> is a subspace of <ps> 2277## 2278InstallOtherMethod(QUO, 2279 "for a projective space and a subspace", 2280 [ IsProjectiveSpace and IsProjectiveSpaceRep, IsSubspaceOfProjectiveSpace], 2281 function( ps, v ) 2282 if not v in ps then 2283 Error( "Subspace does not belong to the projective space" ); 2284 fi; 2285 return Range( NaturalProjectionBySubspace( ps, v ) )!.geometry; 2286 end ); 2287 2288# CHECKED 28/09/11 jdb 2289#cmat change 21/3/14 2290############################################################################# 2291#O NaturalProjectionBySubspace( <ps>, <v> ) 2292# returns the morphism from the polar space to the quotient space of the 2293# subspace <v>. It is checked if <v> is a subspace of <ps>. 2294## 2295InstallMethod( NaturalProjectionBySubspace, 2296 "for a polar space and a subspace", 2297 [ IsClassicalPolarSpace, IsSubspaceOfClassicalPolarSpace ], 2298 function(ps, v) 2299 2300 ## map from geometry of elements incident with v 2301 ## to the polar space of the same type but of lesser dimension 2302 local pstype, psdim, b, vdim, vs, func, pre, 2303 Wvectors, mb, compl, gen, bas, img, canbas, zero, basimgs, 2304 ps2, map, hom, a, m, newform, f, perp; 2305 if not v in ps then 2306 Error("<v> is not an element of <ps>"); 2307 fi; 2308 pstype := PolarSpaceType(ps); 2309 psdim := ps!.dimension; 2310 f := ps!.basefield; 2311 b := Unpack(v!.obj); #cmat unpack necessary for infra. 2312 vdim := v!.type; 2313 if vdim = 1 then b:=[b]; fi; 2314 perp := PolarMap(ps); 2315 vs := VectorSpace(f, Unpack(perp(v)!.obj)); #PolarMap maps to elements, containing a cmat. 2316 2317 ## if v has dimension j, then the new geometry 2318 ## has dimension psdim - 2*vardim 2319 2320 Wvectors:= b; 2321 mb:= MutableBasis( f, Wvectors ); 2322 compl:= []; 2323 for gen in BasisVectors( Basis( vs ) ) do 2324 if not IsContainedInSpan( mb, gen ) then 2325 Add( compl, gen ); 2326 CloseMutableBasis( mb, gen ); 2327 fi; 2328 od; 2329 bas := BasisNC( vs, Concatenation( Wvectors, compl ) ); 2330 2331 # Compute the linear mapping by images. 2332 img:= FullRowModule( f, Length( compl ) ); 2333 canbas:= CanonicalBasis( img ); 2334 zero:= Zero( img ); 2335 # Matrix of the linear transformation we want 2336 basimgs:= Concatenation( List( Wvectors, v -> zero ), 2337 BasisVectors( canbas ) ); 2338 #ConvertToMatrixRep(basimgs, f); #useles now. 2339 a := compl; 2340 2341 if HasQuadraticForm( ps ) then 2342 m := QuadraticForm(ps)!.matrix; 2343 newform := QuadraticFormByMatrix(a * m * TransposedMat(a), f); 2344 else 2345 m := SesquilinearForm(ps)!.matrix; 2346 if pstype = "hermitian" then 2347 #newform := HermitianFormByMatrix(a^CompanionAutomorphism(ps) * m * TransposedMat(a), f); #finally found the bug, jdb gets thirrrrrrsty now. 2348 newform := HermitianFormByMatrix(a * m * TransposedMat(a^CompanionAutomorphism(ps)), f); 2349 2350 else 2351 newform := BilinearFormByMatrix(a * m * TransposedMat(a), f); 2352 fi; 2353 fi; 2354 ps2 := PolarSpace( newform ); 2355 2356 2357 func := function( x ) 2358 local y; 2359 if not v in x then 2360 Error("Subspace is not incident with the subspace of projection"); 2361 fi; 2362 if not x in ps then 2363 Error("Subspace is not an element of the polar space"); 2364 fi; 2365 if v = x then return 2366 EmptySubspace(ps2); 2367 else 2368 y := List(Unpack(x!.obj),i-> Coefficients(bas,i))*basimgs; #cmat unpack here 2369 y := SemiEchelonMat(y)!.vectors; 2370 if x!.type - vdim = 1 then 2371 y := y[1]; 2372 #ConvertToVectorRep(y, f); 2373 #else 2374 # ConvertToMatrixRepNC(y, f); 2375 fi; 2376 return VectorSpaceToElement(ps2, y); 2377 fi; 2378 end; 2379 2380 pre := function( y ) 2381 local x; 2382 if not y in ps2 then 2383 Error("Subspace is not an element of the polar space"); 2384 fi; 2385 x := Unpack(y!.obj); #cmat unpack here. 2386 if y!.type = 1 then x := [x]; fi; 2387 x := Concatenation(x * compl, StructuralCopy( b )); 2388 #ConvertToMatrixRepNC(x, f); #useless now. 2389 return VectorSpaceToElement(ps, x); 2390 end; 2391 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps), 2392 ElementsOfIncidenceStructure(ps2), func, false, pre ); 2393 return map; 2394 end ); 2395 2396# CHECKED 28/09/11 jdb 2397############################################################################# 2398#O NaturalProjectionBySubspaceNC( <ps>, <v> ) 2399## This operation is just like its namesake except that it 2400## has no checks 2401## 2402InstallMethod( NaturalProjectionBySubspaceNC, 2403 "for a polar space and a singular subspace", 2404 [ IsClassicalPolarSpace, IsSubspaceOfClassicalPolarSpace ], 2405 function(ps, v) 2406 2407 ## map from geometry of elements incident with v 2408 ## to the polar space of the same type but of lesser dimension 2409 2410 local pstype, psdim, b, vdim, vs, func, pre, 2411 Wvectors, mb, compl, gen, bas, img, canbas, zero, basimgs, 2412 ps2, map, hom, a, m, newform, f, perp; 2413 2414 pstype := PolarSpaceType(ps); 2415 psdim := ps!.dimension; 2416 f := ps!.basefield; 2417 b := v!.obj; 2418 vdim := v!.type; 2419 if vdim = 1 then b:=[b]; fi; 2420 perp := PolarMap(ps); 2421 vs := VectorSpace(f, perp(v)!.obj); 2422 2423 ## if v has dimension j, then the new geometry 2424 ## has dimension psdim - 2*vdim 2425 2426 Wvectors:= b; 2427 mb:= MutableBasis( f, Wvectors ); 2428 compl:= []; 2429 for gen in BasisVectors( Basis( vs ) ) do 2430 if not IsContainedInSpan( mb, gen ) then 2431 Add( compl, gen ); 2432 CloseMutableBasis( mb, gen ); 2433 fi; 2434 od; 2435 bas := BasisNC( vs, Concatenation( Wvectors, compl ) ); 2436 2437 # Compute the linear mapping by images. 2438 img:= FullRowModule( f, Length( compl ) ); 2439 canbas:= CanonicalBasis( img ); 2440 zero:= Zero( img ); 2441 # Matrix of the linear transformation we want 2442 basimgs:= Concatenation( List( Wvectors, v -> zero ), 2443 BasisVectors( canbas ) ); 2444 ConvertToMatrixRep(basimgs, f); 2445 a := compl; 2446 2447 if HasQuadraticForm( ps ) then 2448 m := QuadraticForm(ps)!.matrix; 2449 newform := QuadraticFormByMatrix(a * m * TransposedMat(a), f); 2450 else 2451 m := SesquilinearForm(ps)!.matrix; 2452 if pstype = "hermitian" then 2453 newform := HermitianFormByMatrix(a * m * TransposedMat(a^CompanionAutomorphism(ps)), f); 2454 else 2455 newform := BilinearFormByMatrix(a * m * TransposedMat(a), f); 2456 fi; 2457 fi; 2458 ps2 := PolarSpace( newform ); 2459 2460 func := function( x ) 2461 local y; 2462 y := List(x!.obj,i-> Coefficients(bas,i))*basimgs; 2463 y := SemiEchelonMat(y)!.vectors; 2464 if x!.type - vdim = 1 then 2465 y := y[1]; 2466 ConvertToVectorRepNC(y, f); 2467 else 2468 ConvertToMatrixRepNC(y, f); 2469 fi; 2470 return VectorSpaceToElement(ps2, y); 2471 end; 2472 2473 pre := function( y ) 2474 local x; 2475 x := y!.obj; 2476 if y!.type = 1 then x := [x]; fi; 2477 x := Concatenation(x * compl, StructuralCopy( b )); 2478 ConvertToMatrixRepNC(x, f); 2479 return VectorSpaceToElement(ps, x); 2480 end; 2481 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(ps), 2482 ElementsOfIncidenceStructure(ps2), func, false, pre ); 2483 return map; 2484 end ); 2485 2486############################################################################# 2487# 2488# Klein correspondence 2489# 2490############################################################################# 2491 2492############################################################################# 2493# two helper operations. They can be used by the users, but on the other hand 2494# the form of the Klein quadric is fixed, so this is against the philiosphy of 2495# FinInG, at least for user functions. Also, there are no checks. So we will 2496# describe these in the technical section of the documentation. 2497############################################################################# 2498 2499# CHECKED 28/09/11 jdb 2500# changed 28/3/14 jdb 2501############################################################################# 2502#O PluckerCoordinates( <lobj> ) 2503# returns the Plucker coordinates of the line represented by <lobj>. We accept 2504# a matrix representing the line. No check on whether this is a line of PG(3,q),` 2505# so use with care. cmat note: whether l is a cmat or not, coords will be a plain 2506# list (so no cvec). This is the internal version accepting a matrix. 2507## 2508InstallMethod( PluckerCoordinates, 2509 "for a matrix representing a line of PG(3,q)", 2510 [ IsMatrix ], 2511 function( lobj ) 2512 local pij, u, v, coords; 2513 pij := function(u,v,i,j) 2514 return u[i]*v[j] - u[j] * v[i]; 2515 end; 2516 u := lobj[1]; 2517 v := lobj[2]; 2518 coords := [pij(u,v,1,2),pij(u,v,1,3),pij(u,v,1,4), 2519 pij(u,v,2,3),pij(u,v,4,2),pij(u,v,3,4)]; 2520 return coords; 2521 end ); 2522 2523# CHECKED 28/09/11 jdb 2524# changed 28/3/14 jdb 2525############################################################################# 2526#O PluckerCoordinates( <line> ) 2527# returns the Plucker coordinates of the line represented by <lobj>. We accept 2528# a matrix representing the line. No check on whether this is a line of PG(3,q),` 2529# so use with care. cmat note: whether l is a cmat or not, coords will be a plain 2530# list (so no cvec). This is the user version accepting a subspace of a projective 2531# space. 2532## 2533#InstallMethod( PluckerCoordinates, 2534# "for a matrix representing a line of PG(3,q)", 2535# [ IsSubspaceOfProjectiveSpace ], 2536# function( l ) 2537# local pij, u, v, coords,lobj; 2538# if l!.type <> 2 or Dimension(l!.geo) <> 3 then 2539# Error(" <l> must be a line of a PG(3,q)"); 2540# fi; 2541# lobj := l!.obj; 2542# pij := function(u,v,i,j) 2543# return u[i]*v[j] - u[j] * v[i]; 2544# end; 2545# u := lobj[1]; 2546# v := lobj[2]; 2547# coords := [pij(u,v,1,2),pij(u,v,1,3),pij(u,v,1,4), 2548# pij(u,v,2,3),pij(u,v,4,2),pij(u,v,3,4)]; 2549# return coords; 2550# end ); 2551 2552 2553# CHECKED 28/09/11 jdb 2554# changed 28/3/14 jdb 2555############################################################################# 2556#O InversePluckerCoordinates( <var> ) 2557# returns a list of two vectors spanning the line with Plucker coordinates <var> 2558# no check on whether this is really a line of the particular Q+(5,q):X0X5+X1X4+X2X3 2559# caution: the list l is not Triangulized! 2560# cmat note: l will be an ordinary matrix. 2561## 2562InstallMethod( InversePluckerCoordinates, 2563 "for a vector repr. a point of Q+(5,q): X0X5+X1X4+X2X3=0", 2564 [ IsVector ], 2565 2566 ## The point must satisfy the Pluecker relation x1x6+x2x5+x3x4=0. 2567 function( x ) 2568 local pairs, i, pair, l, f, zero; 2569 pairs := [[1,2],[1,3],[1,4],[2,3],[4,2],[3,4]]; 2570 i := PositionNonZero(x); 2571 pair := pairs[i]; 2572 zero := Zero(x[1]); 2573 l := []; 2574 if 1 in pair then 2575 Add(l, [ zero, x[1], x[2], x[3] ]); 2576 fi; 2577 if 2 in pair then 2578 Add(l, [ x[1], zero, -x[4], x[5] ]); 2579 fi; 2580 if 3 in pair then 2581 Add(l, [ x[2], x[4], zero, -x[6] ]); 2582 fi; 2583 if 4 in pair then 2584 Add(l, [ x[3], -x[5], x[6], zero ]); 2585 fi; 2586 return l; 2587 end ); 2588 2589############################################################################# 2590# Klein correspondence: the user operations. 2591############################################################################# 2592 2593# added may 2014 jdb 2594############################################################################# 2595#O PluckerCoordinates( <l> ) 2596# returns the Plucker coordinates of the line <l>. The is the user variant 2597# of the operation. It accepts a line of PG(3,q), does some checking, and 2598# uses the helper operation. 2599## 2600InstallMethod( PluckerCoordinates, 2601 "for a line of PG(3,q)", 2602 [ IsSubspaceOfProjectiveSpace ], 2603 function( l ) 2604 if Dimension(AmbientSpace(l)) <> 3 then 2605 Error("<l> is not a line of a three dimensional projective space"); 2606 elif l!.type <> 2 then 2607 Error("<l> is not a line"); 2608 fi; 2609 return PluckerCoordinates(l!.obj); 2610 end ); 2611 2612# added may 2014 jdb 2613############################################################################# 2614#O KleinCorrespondence( <f>, <computeintertwiner> ) 2615# returns the well known morphism from the lines of PG(3,f), f a finite field 2616# to the points of Q+(5,q): x0x5+x1x4+x2x3 = 0. This is the bare essential 2617# of some geometry morphisms that follow. For didactical reasons, it looks 2618# useful to me to have this version that maps to a fixed hyperbolic quadric. 2619# Of course a flexible verion of this operation is also present. 2620## 2621# The interwiner: - the frobenius automorphism commutes with taking PluckerCoordinates 2622# so we only have to deal with the matrices in the twiner/pretwiner 2623# funcs. 2624# - A projectivity is known if its image of a frame is known. So: 2625# going from PGL(4,q) -> PGO+(6,q): the standard basepoints of PG(5,q) 2626# are points of the quadric, compute the lines (InversePlucker), their image, 2627# , computer the point (Plucker), this gives the matrix of the element 2628# of PGO+(5,q) where each row is determined up to a factor. It turns out 2629# using order as in twinerfunc, the needed factors are always the same: 2630# [1,1,1,1,1,-1]. This explains the '-' at newmat[6] line. 2631# going from PGO+(5,q) -> PGL(4,q): cave canem: projectivities swapping 2632# greek and latin planes do not induce a projectivity of PG(3,q)! 2633# The principle is the same as above, just more work is needed to compute 2634# the factors here (they seem not to be independant of the group element now). 2635# Checking whether greek/latins are swapped is done by mapping a point 2636# to a generator, computing the image, mapping back, and see whether 2637# the three lines span PG(3,q). If so, we have the point, if not, 2638# the image of the point is a plane and the projectivity of Q+(5,q) 2639# swaps the greeks/latins and induces in fact a correlation of PG(3,q). 2640## 2641InstallMethod( KleinCorrespondence, 2642 "for a finite field and a boolean", 2643 [ IsField, IsBool ], 2644 function( f, computeintertwiner ) 2645 local i, form, map, pg, mat, pre, plucker, ps, inv, one, twinerfunc, 2646 twinerprefun, coll1, gens1, coll2, gens2, hom, id; 2647 2648 one := One(f); 2649 2650 ## The form for the resulting Plucker coordinates is 2651 ## x1x6+x2x5+x3x4 = 0 2652 2653 mat := NullMat(6, 6, f); 2654 for i in [1..3] do 2655 mat[i][7-i] := one; 2656 od; 2657 form := QuadraticFormByMatrix(mat, f); 2658 ps := PolarSpace( form ); 2659 pg := ProjectiveSpace(3,f); 2660 2661 plucker := 2662 function( l ) 2663 local pt1; 2664 pt1 := PluckerCoordinates( l!.obj ); 2665 return VectorSpaceToElement(ps, pt1); 2666 end; 2667 2668 inv := 2669 function( x ) 2670 local l; 2671 l := InversePluckerCoordinates( x!.obj ); 2672 return VectorSpaceToElement(pg, l); 2673 end; 2674 2675 map := GeometryMorphismByFunction(Lines(pg), Points(ps), plucker, inv); 2676 SetIsBijective(map, true); 2677 2678 twinerfunc := function(g) 2679 local mat,newmat; 2680 mat := Unpack(g!.mat); 2681 newmat := []; 2682 newmat[1] := PluckerCoordinates([mat[2],mat[1]]); 2683 newmat[2] := PluckerCoordinates([mat[3],mat[1]]); 2684 newmat[3] := PluckerCoordinates([mat[4],mat[1]]); 2685 newmat[4] := PluckerCoordinates([mat[3],mat[2]]); 2686 newmat[5] := PluckerCoordinates([mat[4],-mat[2]]); 2687 newmat[6] := -PluckerCoordinates([-mat[4],mat[3]]); 2688 return ProjElWithFrob(newmat,g!.frob,f); 2689 end; 2690 2691 id := IdentityMat(4, f); 2692 2693 twinerprefun := function( g ) 2694 local mat, newmat, lines, pts, ipts, ilines, e, x, ept, 2695 ielines, ie, cs, iept; 2696 mat := Unpack(g!.mat); 2697 lines:= []; 2698 lines[1] := [[id[1],id[2]],[id[1],id[3]],[id[1],id[4]]]; 2699 lines[2] := [[id[2],id[3]],[id[2],id[4]]]; 2700 lines[3] := [[id[3],id[4]],[id[3],id[1]]]; 2701 lines[4] := [[id[4],id[1]],[id[4],id[2]]]; 2702 pts := List(lines,x->List(x,y->PluckerCoordinates(y))); 2703 ipts := List(pts,x->x*mat); 2704 ilines := List(ipts,x->List(x,y->InversePluckerCoordinates(y))); 2705 if Rank(Union(ilines[1])) = 3 then 2706 Info(InfoFinInG, 1, "<el> is not inducing a collineation of PG(3,q)"); 2707 return fail; 2708 fi; 2709 ipts := List(ilines, x->SumIntersectionMat(x[1],x[2])[2]); 2710 newmat := List(ipts,x->x[1]); 2711 e := [1,1,1,1]*one; 2712 ept := List([[e,id[1]],[e,id[2]]],y->PluckerCoordinates(y)); 2713 iept := List(ept,x->x*mat); 2714 ielines := List(iept,x->InversePluckerCoordinates(x)); 2715 ie := SumIntersectionMat(ielines[1],ielines[2])[2]; 2716 cs := SolutionMat(newmat,ie[1]); 2717 for i in [1..4] do 2718 newmat[i] := newmat[i]*cs[i]; 2719 od; 2720 return ProjElWithFrob(newmat,g!.frob,f); 2721 end; 2722 2723 if computeintertwiner then 2724 coll1 := CollineationGroup(PG(3,f)); 2725 gens1 := GeneratorsOfGroup(coll1); 2726 gens2 := List(gens1, twinerfunc); 2727 coll2 := GroupWithGenerators(gens2); 2728 hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 2729 SetIntertwiner( map, hom ); 2730 fi; 2731 2732 return map; 2733 end ); 2734 2735############################################################################# 2736#O KleinCorrespondence( <q> ) 2737# returns KleinCorrespondence( GF(q), true ) 2738## 2739InstallMethod( KleinCorrespondence, 2740 "for a prime power", 2741 [ IsPosInt ], 2742 q -> KleinCorrespondence(GF(q),true) 2743 ); 2744 2745############################################################################# 2746#O KleinCorrespondence( <q> ) 2747# returns KleinCorrespondence( GF(q), computeintertwiner ) 2748## 2749InstallMethod( KleinCorrespondence, 2750 "for a prime power and a boolean", 2751 [ IsPosInt, IsBool ], 2752 function(q, computeintertwiner ) 2753 return KleinCorrespondence(GF(q),computeintertwiner); 2754 end ); 2755 2756############################################################################# 2757#O KleinCorrespondence( <f> ) 2758# returns KleinCorrespondence( GF(q), true ) 2759## 2760InstallMethod( KleinCorrespondence, 2761 "for a finite field", 2762 [ IsField ], 2763 f -> KleinCorrespondence(f,true) 2764 ); 2765 2766 2767# CHECKED 28/09/11 jdb 2768############################################################################# 2769#O KleinCorrespondence( <quadric>, <computeintertwiner> ) 2770# returns the well known morphism from the lines of PG(3,q) to the points 2771# of Q+(5,q). Of course, the bilinear form determining the latter, can be chosen 2772# by the users. This method is exactly the same as the method for 2773# KleinCorrespondence( <f>, <computeintertwiner> ), but adds base changes. 2774## 2775InstallMethod( KleinCorrespondence, 2776 "for a hyperbolic quadric and a boolean", 2777 [ IsClassicalPolarSpace, IsBool ], 2778 function( quadric, computeintertwiner ) 2779 local f, i, form, map, pg, mat, twinerfunc, twinerprefun, 2780 pre, func, ps, iso, one, c, c1, c2, id, cinv, coll1, gens1, 2781 coll2, gens2, hom; 2782 if ProjectiveDimension(quadric) <> 5 then 2783 Error("<ps> is not Klein's quadric"); 2784 fi; 2785 if not IsHyperbolicQuadric(quadric) then 2786 Error("<ps> is not Klein's quadric"); 2787 fi; 2788 f := quadric!.basefield; 2789 one := One(f); 2790 2791 ## The form for the resulting Plucker coordinates is 2792 ## x1x6+x2x5+x3x4 = 0 2793 2794 mat := NullMat(6, 6, f); 2795 for i in [1..3] do 2796 mat[i][7-i] := one; 2797 od; 2798 form := QuadraticFormByMatrix(mat, f); 2799 ps := PolarSpace( form ); 2800 c1 := BaseChangeToCanonical( form ); 2801 if not IsCanonicalPolarSpace(quadric) then 2802 c2 := BaseChangeToCanonical(QuadraticForm(quadric)); 2803 c := c2^-1 * c1; 2804 else 2805 c := c1; 2806 fi; 2807 cinv := c^-1; 2808 pg := ProjectiveSpace(3,f); 2809 2810 func := function( el ) 2811 local pt1; 2812 if el!.type <> 2 then 2813 Error("<el> is not a line "); 2814 fi; 2815 pt1 := PluckerCoordinates( el!.obj ); 2816 return VectorSpaceToElement(quadric, pt1 * cinv ); #base change is here 2817 end; 2818 2819 pre := function( el ) 2820 local l; 2821 if el!.type <> 1 then 2822 Error("<el> is not a point "); 2823 fi; 2824 l := InversePluckerCoordinates( el!.obj * c ); #base change is here 2825 return VectorSpaceToElement(pg, l); 2826 end; 2827 2828 map := GeometryMorphismByFunction(Lines(pg), Points(quadric), func, pre); 2829 SetIsBijective(map, true); 2830 2831 twinerfunc := function(g) 2832 local mat,newmat,frob; 2833 mat := Unpack(g!.mat); 2834 frob := g!.frob; 2835 newmat := []; 2836 newmat[1] := PluckerCoordinates([mat[2],mat[1]]); 2837 newmat[2] := PluckerCoordinates([mat[3],mat[1]]); 2838 newmat[3] := PluckerCoordinates([mat[4],mat[1]]); 2839 newmat[4] := PluckerCoordinates([mat[3],mat[2]]); 2840 newmat[5] := PluckerCoordinates([mat[4],-mat[2]]); 2841 newmat[6] := -PluckerCoordinates([-mat[4],mat[3]]); 2842 return ProjElWithFrob(c * newmat * cinv^frob,frob,f); #base change is here. 2843 end; 2844 2845 id := IdentityMat(4, f); 2846 2847 twinerprefun := function( g ) 2848 local mat, newmat, lines, pts, ipts, ilines, e, x, ept, 2849 ielines, ie, cs, iept, frob; 2850 frob := g!.frob; 2851 mat := cinv * Unpack(g!.mat) * c^frob; #base change is here. 2852 lines:= []; 2853 lines[1] := [[id[1],id[2]],[id[1],id[3]],[id[1],id[4]]]; 2854 lines[2] := [[id[2],id[3]],[id[2],id[4]]]; 2855 lines[3] := [[id[3],id[4]],[id[3],id[1]]]; 2856 lines[4] := [[id[4],id[1]],[id[4],id[2]]]; 2857 pts := List(lines,x->List(x,y->PluckerCoordinates(y))); 2858 ipts := List(pts,x->x*mat); 2859 ilines := List(ipts,x->List(x,y->InversePluckerCoordinates(y))); 2860 if Rank(Union(ilines[1])) = 3 then 2861 Info(InfoFinInG, 1, "<el> is not inducing a collineation of PG(3,q)"); 2862 return fail; 2863 fi; 2864 ipts := List(ilines, x->SumIntersectionMat(x[1],x[2])[2]); 2865 newmat := List(ipts,x->x[1]); 2866 e := [1,1,1,1]*one; 2867 ept := List([[e,id[1]],[e,id[2]]],y->PluckerCoordinates(y)); 2868 iept := List(ept,x->x*mat); 2869 ielines := List(iept,x->InversePluckerCoordinates(x)); 2870 ie := SumIntersectionMat(ielines[1],ielines[2])[2]; 2871 cs := SolutionMat(newmat,ie[1]); 2872 for i in [1..4] do 2873 newmat[i] := newmat[i]*cs[i]; 2874 od; 2875 return ProjElWithFrob(newmat,g!.frob,f); 2876 end; 2877 2878 if computeintertwiner then 2879 coll1 := CollineationGroup(PG(3,f)); 2880 gens1 := GeneratorsOfGroup(coll1); 2881 gens2 := List(gens1, twinerfunc); 2882 coll2 := GroupWithGenerators(gens2); 2883 hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 2884 SetIntertwiner( map, hom ); 2885 fi; 2886 2887 return map; 2888 2889 end ); 2890 2891############################################################################# 2892#O KleinCorrespondence( <quadric> ) 2893# returns KleinCorrespondence( <quadric>, true ) 2894## 2895InstallMethod( KleinCorrespondence, 2896 "for a hyperbolic quadric", 2897 [ IsClassicalPolarSpace ], 2898 quadric -> KleinCorrespondence(quadric, true) 2899 ); 2900 2901# added may 2014 jdb. 2902############################################################################# 2903#O KleinCorrespondenceExtended( <f>, <computeintertwiner> ) 2904# I had really a lot of fun with the above methods and the mathematics behind. 2905# Therefore I decided to include an "extension" which also computes the image 2906# of points and planes of PG(3,q) under the Klein correspondence. This morphism 2907# becomes an isomorphism from PG(3,q) as incidence geometry to the incidence 2908# geometry consisting of the points and the two systems of generators of Q+(5,q). 2909# All mathematics is explained in the KleinCorrespondence variants above. For the 2910# moment I recycle the interwiner from the appropriate KleinCorrespondence's. 2911# When I will have more time, I'll maybe implement the interwiner from the 2912# CorrelationCollineationGroup(PG(3,q)) to PGO+(6,q). 2913## 2914InstallMethod( KleinCorrespondenceExtended, 2915 "for a field and a boolean", 2916 [ IsField, IsBool ], 2917 function( f, computeintertwiner ) 2918 local i, form, map, pg, mat, pre, func, ps, one, twinerfunc, 2919 twinerprefun, coll1, gens1, coll2, gens2, hom, em; 2920 2921 one := One(f); 2922 2923 ## The form for the resulting Plucker coordinates is 2924 ## x1x6+x2x5+x3x4 = 0 2925 2926 mat := NullMat(6, 6, f); 2927 for i in [1..3] do 2928 mat[i][7-i] := one; 2929 od; 2930 form := QuadraticFormByMatrix(mat, f); 2931 ps := PolarSpace( form ); 2932 pg := ProjectiveSpace(3,f); 2933 2934 func := function( el ) 2935 local t, vec, newvec, ptvec, elvec; 2936 t := el!.type; 2937 elvec := Unpack(el!.obj); 2938 if t = 1 then 2939 vec := BasisVectors(Basis(Lines(el)!.factorspace)); 2940 newvec := [PluckerCoordinates([elvec,vec[1]]), PluckerCoordinates([elvec,vec[2]]), PluckerCoordinates([elvec,vec[3]])]; 2941 return VectorSpaceToElement(ps,newvec); 2942 elif t = 2 then 2943 return VectorSpaceToElement(ps,PluckerCoordinates(elvec)); 2944 elif t = 3 then 2945 newvec := [PluckerCoordinates( elvec{[1,2]}), PluckerCoordinates( elvec{[2,3]} ), PluckerCoordinates( elvec{[1,3]} )]; 2946 return VectorSpaceToElement(ps,newvec); 2947 fi; 2948 end; 2949 2950 pre := function( el ) 2951 local elvec,t,newvec,lines,space; 2952 t := el!.type; 2953 elvec := Unpack(el!.obj); 2954 if t = 1 then 2955 newvec := InversePluckerCoordinates( elvec ); 2956 return VectorSpaceToElement(pg, newvec); 2957 elif t = 3 then 2958 newvec := List(elvec,x->InversePluckerCoordinates( x )); 2959 lines := List(newvec,x->VectorSpaceToElement(pg,x)); 2960 space := Meet(lines); 2961 if IsEmptySubspace(space) then 2962 return Span(lines); 2963 else 2964 return space; 2965 fi; 2966 elif t = 2 then 2967 Error("Lines of Klein quadric have no preimage"); 2968 fi; 2969 end; 2970 2971 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(pg), ElementsOfIncidenceStructure(ps), func, pre); 2972 SetIsBijective(map, true); 2973 2974 if computeintertwiner then 2975 em := KleinCorrespondence(f,computeintertwiner); 2976 hom := Intertwiner(em); 2977 SetIntertwiner( map, hom ); 2978 fi; 2979 2980 return map; 2981 end ); 2982 2983############################################################################# 2984#O KleinCorrespondenceExtended( <q> ) 2985# returns KleinCorrespondenceExtended( GF(q), true ) 2986## 2987InstallMethod( KleinCorrespondenceExtended, 2988 "for a prime power", 2989 [ IsPosInt ], 2990 q -> KleinCorrespondenceExtended(GF(q),true) 2991 ); 2992 2993# added may 2014 jdb. 2994############################################################################# 2995#O KleinCorrespondenceExtended( <quadric>, <computeintertwiner> ) 2996# The same as KleinCorrespondenceExtended, but accepting a user defined 2997# hyperbolic quadric 2998## 2999InstallMethod( KleinCorrespondenceExtended, 3000 "for a field and a boolean", 3001 [ IsClassicalPolarSpace, IsBool ], 3002 function( quadric, computeintertwiner ) 3003 local i, form, map, pg, mat, pre, func, ps, one, twinerfunc, f, 3004 twinerprefun, coll1, gens1, coll2, gens2, hom, em, c, c1, c2, cinv; 3005 3006 f := quadric!.basefield; 3007 one := One(f); 3008 3009 ## The form for the resulting Plucker coordinates is 3010 ## x1x6+x2x5+x3x4 = 0 3011 3012 mat := NullMat(6, 6, f); 3013 for i in [1..3] do 3014 mat[i][7-i] := one; 3015 od; 3016 form := QuadraticFormByMatrix(mat, f); 3017 ps := PolarSpace( form ); 3018 pg := ProjectiveSpace(3,f); 3019 3020 c1 := BaseChangeToCanonical( form ); 3021 if not IsCanonicalPolarSpace(quadric) then 3022 c2 := BaseChangeToCanonical(QuadraticForm(quadric)); 3023 c := c2^-1 * c1; 3024 else 3025 c := c1; 3026 fi; 3027 cinv := c^-1; 3028 3029 func := function( el ) 3030 local t, vec, newvec, ptvec, elvec; 3031 t := el!.type; 3032 elvec := Unpack(el!.obj); 3033 if t = 1 then 3034 vec := BasisVectors(Basis(Lines(el)!.factorspace)); 3035 newvec := [PluckerCoordinates([elvec,vec[1]]), PluckerCoordinates([elvec,vec[2]]), PluckerCoordinates([elvec,vec[3]])]; 3036 return VectorSpaceToElement(quadric,newvec * cinv); #base change is here... 3037 elif t = 2 then 3038 return VectorSpaceToElement(quadric,PluckerCoordinates(elvec) * cinv); # ... and here ... 3039 elif t = 3 then 3040 newvec := [PluckerCoordinates( elvec{[1,2]}), PluckerCoordinates( elvec{[2,3]} ), PluckerCoordinates( elvec{[1,3]} )]; 3041 return VectorSpaceToElement(quadric,newvec * cinv); #... and here. 3042 fi; 3043 end; 3044 3045 pre := function( el ) 3046 local elvec,t,newvec,lines,space; 3047 t := el!.type; 3048 elvec := Unpack(el!.obj) * c; # base change is here. 3049 if t = 1 then 3050 newvec := InversePluckerCoordinates( elvec ); 3051 return VectorSpaceToElement(pg, newvec); 3052 elif t = 3 then 3053 newvec := List(elvec,x->InversePluckerCoordinates( x )); 3054 lines := List(newvec,x->VectorSpaceToElement(pg,x)); 3055 space := Meet(lines); 3056 if IsEmptySubspace(space) then 3057 return Span(lines); 3058 else 3059 return space; 3060 fi; 3061 elif t = 2 then 3062 Error("Lines of Klein's quadric have no preimage in the source of <em>"); 3063 fi; 3064 end; 3065 3066 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(pg), ElementsOfIncidenceStructure(quadric), func, pre); 3067 SetIsBijective(map, true); 3068 3069 if computeintertwiner then 3070 em := KleinCorrespondence(quadric,true); 3071 hom := Intertwiner(em); 3072 SetIntertwiner( map, hom ); 3073 fi; 3074 3075 return map; 3076 end ); 3077 3078############################################################################# 3079#O KleinCorrespondenceExtended( <quadric> ) 3080# returns KleinCorrespondenceExtended( quadric, true ) 3081## 3082InstallMethod( KleinCorrespondenceExtended, 3083 "for a prime power", 3084 [ IsClassicalPolarSpace ], 3085 quadric -> KleinCorrespondenceExtended(quadric,true) 3086 ); 3087 3088 3089############################################################################# 3090# 3091# DUALITIES between W(q) and Q(4,q) and H(3,q^2) and Q-(5,q) 3092# 3093############################################################################# 3094 3095############################################################################# 3096# two helper operations. These were derived from the previous version of 3097# NaturalDuality. The new NaturalDuality will interface these. 3098############################################################################# 3099 3100# Added april 2014. jdb 3101############################################################################# 3102#O NaturalDualitySymplectic( <w>, <q4q>, <computeintertwiner>, <reverse> ) 3103# returns the well known isomorphism between W(3,q) and Q(4,q). 3104# Both polar spaces may be user defined. 3105# Setup: - Plucker Coordinates map lines of PG(3,q) on points of Q+(5,q): X0X5+X1X4+X2X3 = 0 3106# - restricting to lines of W(3,q), the range is the points of Q(4,q): X0^2 = X1X4+X2X3, which is the above 3107# intersecting with the hyperplane X0+X5 = 0. 3108# - When using PluckerCoordinates, we only need the first 5 coordinates. When using InversePluckerCoordinates, 3109# we make the sixth coordinate equal to minus the first. 3110# - points of W(3,q): take its pole under the polarity associated with W(3,q), this defines a plane. 3111# From this plane's underlying object, we have three lines -> three points of Q+(5,q) spanning a generator 3112# then intersect this generator with X0+X5=0 to find the line of Q(4,q). 3113# - lines of Q(4,q): take the two spanning points -> two lines of W -> Meet of these lines is the point 3114# we are looking for. 3115# - some base changes are necessary. 3116# - all the linear algebra is hard coded. 3117# - the intertwiners: inspired by the intertwiners of the Klein Correspondence. 3118# - except for the filters, there is not real check whether the input the correct GQ. 3119############################################################################# 3120## 3121InstallMethod( NaturalDualitySymplectic, 3122 "for a symplectic GQ and a parabolic quadric", 3123 [ IsClassicalGQ, IsClassicalGQ, IsBool, IsBool ], 3124 function( w, q4q, computeintertwiner, reverse ) 3125 local f, one, mat, form_quadric, quadric, data, form_w, cq4q, cw, can, 3126 func, pre, formw, c1, c2, delta, hyp, coll1, coll2, gens1, gens2, hom, 3127 twinerfunc, twinerprefun, cq4qinv, cwinv, id; 3128 f := w!.basefield; 3129 one := One(f); 3130 mat := NullMat(5, 5, f); 3131 mat[1][1] := one; 3132 mat[2][5] := -one; 3133 mat[3][4] := -one; 3134 form_quadric := QuadraticFormByMatrix(mat, f); 3135 c1 := BaseChangeToCanonical( form_quadric ); 3136 if not IsCanonicalPolarSpace(q4q) then 3137 c2 := BaseChangeToCanonical(QuadraticForm(q4q)); 3138 cq4q := c2^-1 * c1; 3139 else 3140 cq4q := c1; 3141 fi; 3142 cq4qinv := cq4q^-1; 3143 formw := SesquilinearForm( w ); 3144 delta := PolarityOfProjectiveSpace(w); 3145 hyp := IdentityMat(6,f){[1..5]}; #this and next are in fact HyperplaneByDualCoorindates([1,0,0,0,0,1]..) 3146 hyp[1][6] := -one; 3147 if IsCanonicalPolarSpace( w ) then 3148 func := function( el ) 3149 local list,plane,vec; 3150 if el!.type = 2 then #dealing with a line 3151 return VectorSpaceToElement(q4q, PluckerCoordinates(el!.obj){[1..5]} * cq4q^-1 ); 3152 else 3153 plane := el^delta; 3154 vec := Unpack(plane!.obj); #we have the plane, next line: use three lines in it. 3155 list := [ PluckerCoordinates(vec{[1,2]}), PluckerCoordinates(vec{[2,3]}), PluckerCoordinates(vec{[1,3]}) ]; 3156 plane := SumIntersectionMat(list, hyp)[2]; #hard coded Meet operation :-) 3157 list := List(plane,x->x{[1..5]}*cq4q^-1); #remember that we only need the first five coordinates, and do not forget base change 3158 return VectorSpaceToElement(q4q, list); 3159 fi; 3160 end; 3161 pre := function( el ) 3162 local vec; 3163 if el!.type = 1 then #dealing with a point 3164 vec := Unpack(el!.obj)*cq4q; 3165 vec[6] := -vec[1]; 3166 return VectorSpaceToElement(w, InversePluckerCoordinates(vec) ); 3167 else 3168 vec := Unpack(el!.obj)*cq4q; 3169 vec[1][6] := -vec[1][1]; 3170 vec[2][6] := -vec[2][1]; 3171 return VectorSpaceToElement(w, SumIntersectionMat(InversePluckerCoordinates(vec[1]), InversePluckerCoordinates(vec[2]))[2] ); 3172 fi; 3173 end; 3174 else 3175 cw := BaseChangeToCanonical( formw ); 3176 func := function( el ) 3177 local list,plane,vec; 3178 if el!.type = 2 then #dealing with a line 3179 return VectorSpaceToElement(q4q, PluckerCoordinates(Unpack(el!.obj)*cw^-1){[1..5]} * cq4q^-1); 3180 else 3181 plane := el^delta; 3182 vec := Unpack(plane!.obj)*cw^-1; 3183 list := [ PluckerCoordinates(vec{[1,2]}), PluckerCoordinates(vec{[2,3]}), PluckerCoordinates(vec{[1,3]}) ]; 3184 plane := SumIntersectionMat(list, hyp)[2]; 3185 list := List(plane,x->x{[1..5]}*cq4q^-1); 3186 return VectorSpaceToElement(q4q, list); 3187 fi; 3188 end; 3189 pre := function( el ) 3190 local vec; 3191 if el!.type = 1 then #dealing with a point 3192 vec := Unpack(el!.obj)*cq4q; 3193 vec[6] := -vec[1]; 3194 return VectorSpaceToElement(w, InversePluckerCoordinates(vec) * cw); 3195 else; 3196 vec := Unpack(el!.obj)*cq4q; 3197 vec[1][6] := -vec[1][1]; 3198 vec[2][6] := -vec[2][1]; 3199 return Meet( VectorSpaceToElement(w, InversePluckerCoordinates(vec[1]) * cw ), 3200 VectorSpaceToElement(w, InversePluckerCoordinates(vec[2]) * cw ) ); 3201 fi; 3202 end; 3203 fi; 3204 3205 if not reverse then 3206 data := rec( func := func, pre := pre); 3207 else 3208 data := rec( func := pre, pre := func); 3209 fi; 3210 3211 if computeintertwiner then 3212 3213 id := IdentityMat(4, f); 3214 3215 if IsCanonicalPolarSpace( w ) then 3216 3217 twinerfunc := function(g) 3218 local mat,newmat,frob; 3219 mat := Unpack(g!.mat); 3220 frob := g!.frob; 3221 newmat := []; 3222 newmat[2] := PluckerCoordinates([mat[3],mat[1]]){[1..5]}; 3223 newmat[3] := PluckerCoordinates([mat[4],mat[1]]){[1..5]}; 3224 newmat[4] := -PluckerCoordinates([-mat[3],mat[2]]){[1..5]}; 3225 newmat[5] := PluckerCoordinates([mat[4],-mat[2]]){[1..5]}; 3226 newmat[1] := PluckerCoordinates([mat[2]+mat[3],mat[1]+mat[4]]){[1..5]} - newmat[2] - newmat[5]; 3227 return ProjElWithFrob(cq4q * newmat * cq4qinv^(frob^-1),frob,f); #base change is here. 3228 return newmat; 3229 end; 3230 3231 twinerprefun := function( g ) 3232 local mat, newmat, lines, pts, ipts, ilines, e, x, ept, 3233 ielines, ie, cs, iept, frob, i, j; 3234 frob := g!.frob; 3235 mat := cq4qinv * Unpack(g!.mat) * cq4q^(frob^-1); 3236 lines:= []; 3237 lines[1] := [[id[1],id[3]],[id[1],id[4]]]; 3238 lines[2] := [[id[2],id[3]],[id[2],id[4]]]; 3239 lines[3] := [[id[3],id[1]],[id[3],id[2]]]; 3240 lines[4] := [[id[4],id[1]],[id[4],id[2]]]; 3241 pts := List(lines,x->List(x,y->PluckerCoordinates(y){[1..5]})); 3242 ipts := List(pts,x->x*mat); 3243 for i in [1..Length(ipts)] do 3244 for j in [1..2] do 3245 ipts[i][j][6] := -ipts[i][j][1]; 3246 od; 3247 od; 3248 ilines := List(ipts,x->List(x,y->InversePluckerCoordinates(y))); 3249 ipts := List(ilines, x->SumIntersectionMat(x[1],x[2])[2]); 3250 newmat := List(ipts,x->x[1]); 3251 e := [1,1,1,1]*one; 3252 ept := List([[e,id[1]+id[4]],[e,id[1]+id[2]]],y->PluckerCoordinates(y){[1..5]}); 3253 iept := List(ept,x->x*mat); 3254 iept[1][6] := -iept[1][1]; 3255 iept[2][6] := -iept[2][1]; 3256 3257 ielines := List(iept,x->InversePluckerCoordinates(x)); 3258 ie := SumIntersectionMat(ielines[1],ielines[2])[2]; 3259 cs := SolutionMat(newmat,ie[1]); 3260 for i in [1..4] do 3261 newmat[i] := newmat[i]*cs[i]; 3262 od; 3263 return ProjElWithFrob(newmat,g!.frob,f); 3264 end; 3265 3266 else 3267 3268 cw := BaseChangeToCanonical( SesquilinearForm(w) ); 3269 cwinv := cw^-1; 3270 3271 twinerfunc := function(g) 3272 local mat,newmat,frob; 3273 frob := g!.frob; 3274 mat := cw * Unpack(g!.mat) * cwinv^(frob^-1); 3275 newmat := []; 3276 newmat[2] := PluckerCoordinates([mat[3],mat[1]]){[1..5]}; 3277 newmat[3] := PluckerCoordinates([mat[4],mat[1]]){[1..5]}; 3278 newmat[4] := -PluckerCoordinates([-mat[3],mat[2]]){[1..5]}; 3279 newmat[5] := PluckerCoordinates([mat[4],-mat[2]]){[1..5]}; 3280 newmat[1] := PluckerCoordinates([mat[2]+mat[3],mat[1]+mat[4]]){[1..5]} - newmat[2] - newmat[5]; 3281 return ProjElWithFrob(cq4q * newmat * cq4qinv^(frob^-1),frob,f); #base change is here. 3282 #return newmat; 3283 end; 3284 3285 twinerprefun := function( g ) 3286 local mat, newmat, lines, pts, ipts, ilines, e, x, ept, 3287 ielines, ie, cs, iept, frob, i, j; 3288 frob := g!.frob; 3289 mat := cq4qinv * Unpack(g!.mat) * cq4q^(frob^-1); 3290 lines:= []; 3291 lines[1] := [[id[1],id[3]],[id[1],id[4]]]; 3292 lines[2] := [[id[2],id[3]],[id[2],id[4]]]; 3293 lines[3] := [[id[3],id[1]],[id[3],id[2]]]; 3294 lines[4] := [[id[4],id[1]],[id[4],id[2]]]; 3295 pts := List(lines,x->List(x,y->PluckerCoordinates(y){[1..5]})); 3296 ipts := List(pts,x->x*mat); 3297 for i in [1..Length(ipts)] do 3298 for j in [1..2] do 3299 ipts[i][j][6] := -ipts[i][j][1]; 3300 od; 3301 od; 3302 ilines := List(ipts,x->List(x,y->InversePluckerCoordinates(y))); 3303 ipts := List(ilines, x->SumIntersectionMat(x[1],x[2])[2]); 3304 newmat := List(ipts,x->x[1]); 3305 e := [1,1,1,1]*one; 3306 ept := List([[e,id[1]+id[4]],[e,id[1]+id[2]]],y->PluckerCoordinates(y){[1..5]}); 3307 iept := List(ept,x->x*mat); 3308 iept[1][6] := -iept[1][1]; 3309 iept[2][6] := -iept[2][1]; 3310 3311 ielines := List(iept,x->InversePluckerCoordinates(x)); 3312 ie := SumIntersectionMat(ielines[1],ielines[2])[2]; 3313 cs := SolutionMat(newmat,ie[1]); 3314 for i in [1..4] do 3315 newmat[i] := newmat[i]*cs[i]; 3316 od; 3317 return ProjElWithFrob(cwinv * newmat * cw^(frob^-1),g!.frob,f); 3318 end; 3319 fi; 3320 if not reverse then 3321 data.twinerfunc := twinerfunc; 3322 data.twinerprefun := twinerprefun; 3323 else 3324 data.twinerfunc := twinerprefun; 3325 data.twinerprefun := twinerfunc; 3326 fi; 3327 fi; 3328 3329 return data; 3330 end ); 3331 3332# Added april 2014. jdb 3333############################################################################# 3334#O NaturalDualityHermitian( <h>, <q5q>, <computeintertwiner>, <reverse> ) 3335# returns the well known isomorphism between H(3,q^2) and Q-(5,q). 3336# Both polar spaces may be user defined. 3337# Setup: - Plucker Coordinates map lines of PG(3,q^2) on points of Q+(5,q^2): X0X5+X1X4+X2X3 = 0 3338# - restricting to lines of H(3,q^2), the range is the points of Q-(5,q). The details (thanks jb for pointing these out!) 3339# 1. Suppose l is a line of H(3,q^2), pl its Plucker coordinates. 3340# then up to a scalar, pl is of the form (x,y,z,z^q,y^q,x^q). We first make sure that 3341# our vector is really in this form (up to no scalar!). 3342# 2. The projectivity of PG(5,q^2) which maps the Plucker coordinates of the lines of H(3,q^2) 3343# to PG(5,q). See "Finite Projective Spaces of Three Dimensions" by Hirschfeld and Thas (section 19.2). 3344# is defined. 3345# 3. Applying this collineation on plucker coordinates (in the right form, see above), makes 3346# sure this is a point of the Elliptic Quadric Q-(5,q): x1^2+x2^2+x3^2+x4^2+x5^2+x6^2 - e(x1x6+x2x5+x3x4) 3347# 4. The result pl*x might be over GF(q) modulo a GF(q^2) 3348# factor, then this causes problems in the VectorSpaceToElement 3349# call, since using cvec/cmat, now requires this to be a vector 3350# over GF(q). So we have to normalize first, then do the base change 3351# then use VectorSpaceToElement. 3352# - the inverse is easier: the combination of InversePluckerCoordinates and xinv. 3353# - points of H(3,q^2): take its pole under the polarity associated with H(3,q^2), this defines a plane. 3354# From this plane's underlying object, we have three lines -> three points of Q+(5,q^2) spanning a generator. 3355# Apply x on this generator and the intersection with the standard PG(5,q) yields a line of Q^-(5,q). 3356# To compute this intersection we first blow up, use the hard coded Meet, and then Shrink. 3357# - lines of Q^-(5,q): take the two spanning points -> two lines of H -> Meet of these lines is the point 3358# we are looking for. 3359# - some base changes are necessary. 3360# - all the linear algebra is hard coded. 3361# - except for the filters, there is not real check whether the input the correct GQ. 3362# - the intertwiners: I can tell you: this is pain in the ass! 3363# - twinerfun: from PGammaU(4,q^2) to PGammaO-(6,q): 3364# 1. part one is taken from the Klein correspondence. This gives an element of 3365# PGammaO+(6,q^2), stabilizing some pointset that is actually a Q-(5,q). 3366# We know the collineation x, and use it to make this a collineation of the Q-(5,q) 3367# 2. The frobenius automorphism of this collineation is just the acion of frob on the 3368# subfield. 3369# - twinerprefun: 3370# 1. part one goes from Q-(5,q) to Q+(5,q^2). Note that we need to compute frob2 from 3371# the given frob, since although frob acts also on the big field, its inverse is not 3372# the same, and this is really important! 3373# 2. part two starts a bit like the Klein correspondence. If the greeks/latins are 3374# swapped by the computed collineation of Q+, we apply the special collineation. 3375# this swaps back the greeks/latins, and stabilizes the Q-(5,q). Now we can use the 3376# same code as in the Klein correspondence, modulo the switch field automorphism when 3377# necessary. 3378############################################################################# 3379## 3380InstallMethod( NaturalDualityHermitian, 3381 "for H(3,q^2) and Q-(5,q)", 3382 [ IsClassicalGQ, IsClassicalGQ, IsBool, IsBool ], 3383 function( h, q5q, computeintertwiner, reverse ) 3384 ## The way this works is that we map the lines of h to the canonical H(3,q^2), 3385 ## which Klein corresponds to points of Q+(5,q^2) using the usual plucker map. 3386 ## This subgeometry is then mapped to PG(5,q), where we can associate it to 3387 ## Q-(5,q). 3388 3389 #local f, frob, q, one, iso, eq, pg5, alpha, mat1, mat2, x, xinv, 3390 # func, pre, i, map, e, ps, form, iso2; 3391 3392 local f, frob, q, one, alpha, e, mat1, mat2, i, form_quadric, c1, c2, cq5q, 3393 func, pre, data, x, xinv, formh, ch, chinv, bmat, delta, basis, twinerfunc, 3394 twinerprefun, coll1,coll2, gens1, gens2, cq5qinv, hom, id, id6, special, theta; 3395 3396 f := h!.basefield; 3397 frob := FrobeniusAutomorphism(f); 3398 q := Sqrt(Size(f)); 3399 one := One(f); 3400 3401 #first set up the elliptic quadric we get if we start from the standard hermitian polar space. 3402 3403 alpha := First(f, a -> (a^q)^2 <> a^2); 3404 e := alpha^(q-1) + alpha^(1-q); 3405 3406 ## The form for the Elliptic Quadric that we get is 3407 ## x1^2+x2^2+x3^2+x4^2+x5^2+x6^2 - e(x1x6+x2x5+x3x4) 3408 3409 mat1 := IdentityMat(6, GF(q)); 3410 mat2 := NullMat(6, 6, f); 3411 for i in [1..3] do 3412 mat2[2*i-1][8-2*i] := one; 3413 od; 3414 mat1 := mat1 - e * mat2; 3415 form_quadric := QuadraticFormByMatrix(mat1, GF(q)); 3416 3417 # do the necessary base changes. 3418 3419 c1 := BaseChangeToCanonical( form_quadric ); 3420 if not IsCanonicalPolarSpace(q5q) then 3421 c2 := BaseChangeToCanonical(QuadraticForm(q5q)); 3422 cq5q := c2^-1 * c1; 3423 else 3424 cq5q := c1; 3425 fi; 3426 cq5qinv := cq5q^-1; 3427 3428 #see comments in header which collineation x represents. 3429 3430 mat1 := IdentityMat(6, f) * alpha; 3431 mat2 := NullMat(6, 6, f); 3432 for i in [1..6] do 3433 mat2[i][7-i] := one; 3434 od; 3435 x := mat1 + mat2 * alpha^q; 3436 x := List(x,y->y); 3437 xinv := x^-1; 3438 3439 formh := SesquilinearForm( h ); 3440 3441 delta := PolarityOfProjectiveSpace(h); 3442 bmat := NullMat(6,12,GF(q)); 3443 for i in [1..6] do 3444 bmat[i][2*i-1] := one; 3445 od; 3446 basis := Basis(AsVectorSpace(GF(q),GF(q^2))); 3447 3448 if IsCanonicalPolarSpace( h ) then 3449 func := function( el ) 3450 local pl, mu, pos, vec, plane, bgen, list; 3451 3452 if el!.type = 2 then #dealing with a line 3453 pl := PluckerCoordinates( Unpack(el!.obj) ); #the Unpack might be unnecessary, but harmless anyway. 3454 #see the comments in the header for what we do here. 3455 pos := PositionNonZero( pl ); 3456 mu := First( AsList(f), m -> m^(q-1) = pl[pos] / pl[7-pos]); 3457 pl := mu * pl; 3458 vec := pl*x; 3459 MultRowVector(vec,Inverse( vec[PositionNonZero(vec)] )); 3460 return VectorSpaceToElement(q5q, vec * cq5q^-1); 3461 else 3462 plane := el^delta; 3463 vec := Unpack(plane!.obj); #we have the plane, next line: use three lines in it. 3464 list := [ PluckerCoordinates(vec{[1,2]}), PluckerCoordinates(vec{[2,3]}), PluckerCoordinates(vec{[1,3]}) ]; 3465 bgen := BlownUpMat(basis,list*x); 3466 list := SumIntersectionMat(bgen, bmat)[2]; #hard coded Meet operation :-) 3467 vec := List(list,x->ShrinkVec(GF(q^2),GF(q),x)); 3468 return VectorSpaceToElement(q5q, vec * cq5q^-1); 3469 fi; 3470 end; 3471 3472 pre := function( el ) 3473 local vec ; 3474 if el!.type = 1 then #dealing with a point 3475 vec := Unpack(el!.obj)*cq5q; 3476 return VectorSpaceToElement(h, InversePluckerCoordinates(vec * xinv) ); 3477 else 3478 vec := (Unpack(el!.obj)*cq5q)*xinv; 3479 return VectorSpaceToElement(h, SumIntersectionMat(InversePluckerCoordinates(vec[1]), InversePluckerCoordinates(vec[2]))[2] ); 3480 fi; 3481 end; 3482 else 3483 ch := BaseChangeToCanonical( formh ); 3484 func := function( el ) 3485 local pl, mu, pos, vec, plane, bgen, list; 3486 if el!.type = 2 then #dealing with a line 3487 pl := PluckerCoordinates( Unpack(el!.obj)*ch^-1 ); 3488 pos := PositionNonZero( pl ); 3489 mu := First( AsList(f), m -> m^(q-1) = pl[pos] / pl[7-pos]); 3490 pl := mu * pl; 3491 vec := pl*x; 3492 MultRowVector(vec,Inverse( vec[PositionNonZero(vec)] )); 3493 return VectorSpaceToElement(q5q, vec * cq5q^-1); 3494 else 3495 plane := el^delta; 3496 vec := Unpack(plane!.obj)*ch^-1; #we have the plane, next line: use three lines in it. 3497 list := [ PluckerCoordinates(vec{[1,2]}), PluckerCoordinates(vec{[2,3]}), PluckerCoordinates(vec{[1,3]}) ]; 3498 bgen := BlownUpMat(basis,list*x); 3499 list := SumIntersectionMat(bgen, bmat)[2]; #hard coded Meet operation :-) 3500 vec := List(list,x->ShrinkVec(GF(q^2),GF(q),x)); 3501 return VectorSpaceToElement(q5q, vec * cq5q^-1); 3502 fi; 3503 end; 3504 3505 pre := function( el ) 3506 local vec; 3507 if el!.type = 1 then #dealing with a point 3508 vec := Unpack(el!.obj)*cq5q; 3509 return VectorSpaceToElement(h, InversePluckerCoordinates(vec * xinv) * ch ); 3510 else 3511 vec := (Unpack(el!.obj)*cq5q)*xinv; 3512 return VectorSpaceToElement(h, SumIntersectionMat(InversePluckerCoordinates(vec[1]), InversePluckerCoordinates(vec[2]))[2] * ch ); 3513 fi; 3514 end; 3515 3516 fi; 3517 3518 #map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(h), ElementsOfIncidenceStructure(q5q), func, pre); 3519 #SetIsBijective( map, true ); 3520 3521 if not reverse then 3522 data := rec( func := func, pre := pre); 3523 else 3524 data := rec( func := pre, pre := func); 3525 fi; 3526 3527 if computeintertwiner then 3528 id := IdentityMat(4,f); 3529 id6 := IdentityMat(6,f); 3530 special := List([1..6],i->id6[7-i]); 3531 i := Log(q,Characteristic(f)); 3532 theta := FrobeniusAutomorphism(f)^i; 3533 3534 if IsCanonicalPolarSpace( h ) then 3535 twinerfunc := function(g) 3536 local mat,newmat,frob,newmat2,n,frob2,j,arg; 3537 frob := g!.frob; 3538 mat := Unpack(g!.mat); 3539 newmat := []; 3540 newmat[1] := PluckerCoordinates([mat[2],mat[1]]); 3541 newmat[2] := PluckerCoordinates([mat[3],mat[1]]); 3542 newmat[3] := PluckerCoordinates([mat[4],mat[1]]); 3543 newmat[4] := PluckerCoordinates([mat[3],mat[2]]); 3544 newmat[5] := PluckerCoordinates([mat[4],-mat[2]]); 3545 newmat[6] := -PluckerCoordinates([-mat[4],mat[3]]); 3546 newmat2 := xinv*newmat*x^(frob^-1); 3547 n := First(newmat2[1],x->not IsZero(x)); 3548 newmat2 := newmat2/n; 3549 if not IsOne(frob) then 3550 j := Log(frob!.power,Characteristic(f)); 3551 else 3552 j := 0; 3553 fi; 3554 frob2 := FrobeniusAutomorphism(GF(q))^(j mod q); 3555 arg := cq5q * newmat2 * cq5qinv^(frob2^-1); 3556 ConvertToMatrixRep(arg); 3557 return ProjElWithFrob(arg,frob2,GF(q)); 3558 end; 3559 3560 twinerprefun := function( g ) 3561 local mat, newmat, lines, pts, ipts, ilines, e, ept, 3562 ielines, ie, cs, iept, frob, frob2, j, n, switch; 3563 frob := g!.frob; 3564 #first setup the frobenius automorphism over GF(q^2)=f. 3565 if not IsOne(frob) then 3566 j := Log(frob!.power,Characteristic(f)); 3567 else 3568 j := 0; 3569 fi; 3570 frob2 := FrobeniusAutomorphism(f)^j; 3571 #base change. Note that frob is used to change from different Q-(5,q)'s, frob2 for Q-(5,q) -> Q+(5,q^2) 3572 mat := x * (cq5qinv * Unpack(g!.mat) * cq5q^(frob^-1) ) * xinv^(frob2^-1); #base change is here. 3573 #now from Q+(5,q^2) to H(3,q^2). 3574 lines:= []; 3575 pts := []; 3576 ipts := []; 3577 ilines := []; 3578 lines[1] := [[id[1],id[2]],[id[1],id[3]],[id[1],id[4]]]; 3579 pts[1] := List(lines[1],y->PluckerCoordinates(y)); 3580 ipts[1] := (pts[1]*mat); 3581 ilines[1] := List(ipts[1],y->InversePluckerCoordinates(y)); 3582 switch := theta^0; 3583 if Rank(Union(ilines[1])) = 3 then 3584 #Print("switch\n"); 3585 mat := (mat^theta) * special; 3586 frob2 := frob2 * theta; 3587 switch := theta; 3588 fi; 3589 n := First(mat[1],x->not IsZero(x)); 3590 mat := mat/n; 3591 lines[1] := [[id[1],id[2]],[id[1],id[3]]]; 3592 lines[2] := [[id[2],id[3]],[id[2],id[4]]]; 3593 lines[3] := [[id[3],id[4]],[id[3],id[1]]]; 3594 lines[4] := [[id[4],id[1]],[id[4],id[2]]]; 3595 pts := List(lines,x->List(x,y->PluckerCoordinates(y))); 3596 ipts := List(pts,x->(x*mat)^switch); 3597 #ipts := List(pts,x->(x*mat)); 3598 ilines := List(ipts,x->List(x,y->InversePluckerCoordinates(y))); 3599 ipts := List(ilines, x->SumIntersectionMat(x[1],x[2])[2]); 3600 newmat := List(ipts,x->x[1]); 3601 e := [1,1,1,1]*one; 3602 ept := List([[e,id[1]],[e,id[2]]],y->PluckerCoordinates(y)); 3603 iept := List(ept,x->(x*mat)^switch); 3604 #iept := List(ept,x->(x*mat)); 3605 ielines := List(iept,x->InversePluckerCoordinates(x)); 3606 ie := SumIntersectionMat(ielines[1],ielines[2])[2]; 3607 cs := SolutionMat(newmat,ie[1]); 3608 for i in [1..4] do 3609 newmat[i] := newmat[i]*cs[i]; 3610 od; 3611 return ProjElWithFrob(newmat,frob2,f); 3612 end; 3613 3614 else 3615 3616 ch := BaseChangeToCanonical( SesquilinearForm(h) ); 3617 chinv := ch^-1; 3618 3619 twinerfunc := function(g) 3620 local mat,newmat,frob,newmat2,n,frob2,j,arg; 3621 frob := g!.frob; 3622 mat := ch * Unpack(g!.mat) * chinv^(frob^-1); 3623 newmat := []; 3624 newmat[1] := PluckerCoordinates([mat[2],mat[1]]); 3625 newmat[2] := PluckerCoordinates([mat[3],mat[1]]); 3626 newmat[3] := PluckerCoordinates([mat[4],mat[1]]); 3627 newmat[4] := PluckerCoordinates([mat[3],mat[2]]); 3628 newmat[5] := PluckerCoordinates([mat[4],-mat[2]]); 3629 newmat[6] := -PluckerCoordinates([-mat[4],mat[3]]); 3630 newmat2 := xinv*newmat*x^(frob^-1); 3631 n := First(newmat2[1],x->not IsZero(x)); 3632 newmat2 := newmat2/n; 3633 if not IsOne(frob) then 3634 j := Log(frob!.power,Characteristic(f)); 3635 else 3636 j := 0; 3637 fi; 3638 frob2 := FrobeniusAutomorphism(GF(q))^(j mod q); 3639 arg := ShallowCopy(cq5q * newmat2 * cq5qinv^(frob2^-1)); 3640 ConvertToMatrixRep(arg); 3641 return ProjElWithFrob(arg,frob2,GF(q)); 3642 end; 3643 3644 twinerprefun := function( g ) 3645 local mat, newmat, lines, pts, ipts, ilines, e, ept, 3646 ielines, ie, cs, iept, frob, frob2, j, n, switch; 3647 frob := g!.frob; 3648 #first setup the frobenius automorphism over GF(q^2)=f. 3649 if not IsOne(frob) then 3650 j := Log(frob!.power,Characteristic(f)); 3651 else 3652 j := 0; 3653 fi; 3654 frob2 := FrobeniusAutomorphism(f)^j; 3655 #base change. Note that frob is used to change from different Q-(5,q)'s, frob2 for Q-(5,q) -> Q+(5,q^2) 3656 mat := x * (cq5qinv * Unpack(g!.mat) * cq5q^(frob^-1) ) * xinv^(frob2^-1); #base change is here. 3657 #now from Q+(5,q^2) to H(3,q^2). 3658 lines:= []; 3659 pts := []; 3660 ipts := []; 3661 ilines := []; 3662 lines[1] := [[id[1],id[2]],[id[1],id[3]],[id[1],id[4]]]; 3663 pts[1] := List(lines[1],y->PluckerCoordinates(y)); 3664 ipts[1] := (pts[1]*mat); 3665 ilines[1] := List(ipts[1],y->InversePluckerCoordinates(y)); 3666 switch := theta^0; 3667 if Rank(Union(ilines[1])) = 3 then 3668 #Print("switch\n"); 3669 mat := (mat^theta) * special; 3670 frob2 := frob2 * theta; 3671 switch := theta; 3672 fi; 3673 n := First(mat[1],x->not IsZero(x)); 3674 mat := mat/n; 3675 lines[1] := [[id[1],id[2]],[id[1],id[3]]]; 3676 lines[2] := [[id[2],id[3]],[id[2],id[4]]]; 3677 lines[3] := [[id[3],id[4]],[id[3],id[1]]]; 3678 lines[4] := [[id[4],id[1]],[id[4],id[2]]]; 3679 pts := List(lines,x->List(x,y->PluckerCoordinates(y))); 3680 ipts := List(pts,x->(x*mat)^switch); 3681 #ipts := List(pts,x->(x*mat)); 3682 ilines := List(ipts,x->List(x,y->InversePluckerCoordinates(y))); 3683 ipts := List(ilines, x->SumIntersectionMat(x[1],x[2])[2]); 3684 newmat := List(ipts,x->x[1]); 3685 e := [1,1,1,1]*one; 3686 ept := List([[e,id[1]],[e,id[2]]],y->PluckerCoordinates(y)); 3687 iept := List(ept,x->(x*mat)^switch); 3688 #iept := List(ept,x->(x*mat)); 3689 ielines := List(iept,x->InversePluckerCoordinates(x)); 3690 ie := SumIntersectionMat(ielines[1],ielines[2])[2]; 3691 cs := SolutionMat(newmat,ie[1]); 3692 for i in [1..4] do 3693 newmat[i] := newmat[i]*cs[i]; 3694 od; 3695 return ProjElWithFrob(chinv * newmat * ch^(frob2^-1),frob2,f); 3696 end; 3697 3698 fi; 3699 if not reverse then 3700 data.twinerfunc := twinerfunc; 3701 data.twinerprefun := twinerprefun; 3702 else 3703 data.twinerfunc := twinerprefun; 3704 data.twinerprefun := twinerfunc; 3705 fi; 3706 fi; 3707 3708 return data; 3709 3710end ); 3711 3712# Added april 2014 jdb. 3713############################################################################# 3714#O NaturalDuality( <gq1>, <gq2>, <bool> ) 3715# This is the interface to the helper functions. It simply checks the input 3716# and decides which NaturalDuality... to use. 3717## 3718InstallMethod( NaturalDuality, 3719 "for a GQ and a GQ", 3720 [ IsClassicalGQ, IsClassicalGQ, IsBool ], 3721 function( gq1, gq2, computeintertwiner ) 3722 local bf1,bf2,data, coll1,coll2,gens1,gens2, map, hom; 3723 bf1 := gq1!.basefield; 3724 bf2 := gq2!.basefield; 3725 if IsSymplecticSpace( gq1 ) and IsParabolicQuadric( gq2 ) then 3726 if bf1 <> bf2 then 3727 Error("<gq1> and <gq2> have a different base field"); 3728 fi; 3729 data := NaturalDualitySymplectic( gq1, gq2, computeintertwiner, false); 3730 elif (IsHermitianPolarSpace( gq1 ) and Dimension( gq1 ) = 3) and IsEllipticQuadric( gq2) then 3731 if Size(bf1) <> Size(bf2)^2 then 3732 Error("base field of <gq1> must have order the square of the base field of <gq2>"); 3733 fi; 3734 data := NaturalDualityHermitian( gq1, gq2, computeintertwiner, false); 3735 elif IsParabolicQuadric( gq1 ) and IsSymplecticSpace( gq2 ) then 3736 if bf1 <> bf2 then 3737 Error("<gq1> and <gq2> have a different base field"); 3738 fi; 3739 data := NaturalDualitySymplectic( gq2, gq1, computeintertwiner, true); 3740 elif IsEllipticQuadric( gq1 ) and (IsHermitianPolarSpace( gq2 ) and Dimension( gq2 ) = 3) then 3741 if Size(bf1^2) <> Size(bf2) then 3742 Error("base field of <gq2> must have order the square of the base field of <gq1>"); 3743 fi; 3744 data := NaturalDualityHermitian( gq2, gq1, computeintertwiner, true); 3745 else 3746 Error("no duality possible between <gq1> and <gq2>"); 3747 fi; 3748 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(gq1), ElementsOfIncidenceStructure(gq2), 3749 data.func, data.pre); 3750 SetIsBijective( map, true ); 3751 if computeintertwiner then 3752 if (HasIsCanonicalPolarSpace( gq1 ) and IsCanonicalPolarSpace( gq1 )) or 3753 HasCollineationGroup( gq1 ) then 3754 coll1 := CollineationGroup(gq1); 3755 gens1 := GeneratorsOfGroup( coll1 ); 3756 gens2 := List(gens1, data.twinerfunc); 3757 coll2 := GroupWithGenerators(gens2); 3758 hom := GroupHomomorphismByFunction(coll1, coll2, data.twinerfunc, data.twinerprefun); 3759 SetIntertwiner( map, hom ); 3760 UseIsomorphismRelation(coll1,coll2); 3761 elif (HasIsCanonicalPolarSpace( gq2 ) and IsCanonicalPolarSpace( gq2 )) or 3762 HasCollineationGroup( gq2 ) then 3763 coll2 := CollineationGroup( gq2 ); 3764 gens2 := GeneratorsOfGroup( coll2 ); 3765 gens1 := List(gens2, data.twinerprefun ); 3766 coll1 := GroupWithGenerators(gens1); 3767 hom := GroupHomomorphismByFunction(coll1, coll2, data.twinerfunc, data.twinerprefun); 3768 SetIntertwiner( map, hom ); 3769 UseIsomorphismRelation(coll1,coll2); 3770 else 3771 Info(InfoFinInG, 1, "No intertwiner computed. One of the polar spaces must have a collineation group computed"); 3772 fi; 3773 fi; 3774 3775 return map; 3776end ); 3777 3778# Added may 2014 jdb. 3779############################################################################# 3780#O NaturalDuality( <gq1>, <gq2> ) 3781# returns NaturalDuality( <gq1>, <gq2>, true) 3782## 3783InstallMethod( NaturalDuality, 3784 "for a GQ and a GQ", 3785 [ IsClassicalGQ, IsClassicalGQ ], 3786 function(gq1,gq2) 3787 return NaturalDuality(gq1,gq2,true); 3788 end); 3789 3790# Added may 2014 jdb. 3791############################################################################# 3792#O NaturalDuality( <gq1>, <computeintertwiner> ) 3793# This is the interface to the helper functions. It simply checks the input 3794# and decides which NaturalDuality... to use. 3795## 3796InstallMethod( NaturalDuality, 3797 "for a GQ and a GQ", 3798 [ IsClassicalGQ, IsBool ], 3799 function( gq1, computeintertwiner ) 3800 local q; 3801 if IsSymplecticSpace( gq1 ) then 3802 return NaturalDuality( gq1, ParabolicQuadric(4, BaseField(gq1)), computeintertwiner ) ; 3803 elif (IsHermitianPolarSpace( gq1 ) and Dimension( gq1 ) = 3) then 3804 q := Sqrt(Size(BaseField(gq1))); 3805 return NaturalDuality( gq1, EllipticQuadric(5,GF(q)), computeintertwiner ); 3806 elif IsParabolicQuadric( gq1 ) then 3807 return NaturalDuality( gq1, SymplecticSpace(3, BaseField(gq1) ), computeintertwiner ); 3808 elif IsEllipticQuadric( gq1 ) then 3809 q := Size(BaseField(gq1)); 3810 return NaturalDuality( gq1, HermitianPolarSpace(3, q^2), computeintertwiner); 3811 else 3812 Error("no duality possible on <gq1>"); 3813 fi; 3814 end ); 3815 3816# Added may 2014 jdb. 3817############################################################################# 3818#O NaturalDuality( <gq1> ) 3819# returns NaturalDuality( <gq1>, true) 3820## 3821InstallMethod( NaturalDuality, 3822 "for a GQ and a GQ", 3823 [ IsClassicalGQ ], 3824 function(gq1) 3825 return NaturalDuality(gq1,true); 3826 end); 3827 3828############################################################################# 3829# 3830# Q(2n,q) -> W(2n-1,q) (q even). 3831# 3832############################################################################# 3833 3834# Added april 2014 jdb. 3835############################################################################# 3836#O IsomorphismPolarSpacesProjectionFromNucleus( <quadric>, <w>, <bool> ) 3837# returns the isomorphism between Q(2n,q) and W(2n-1,q). Both may be user 3838# defined. 3839# standard Q(2n,q): X0^2+X1X2+...X2n-1X2n: nucleus: (1,0,0,...,0,0) 3840# standard W(2n,q): X0Y1+X1Y0+...X2n-2X2n. 3841# projecting from nucleus yields the automorphism. Going back: consider a point 3842# of W(2n-1,q): evaluate it under X1X2+...X2n-1X2n: this yields a value z. 3843# the equation X0^2 = z has exactly one solution -> point of Q(2n,q). 3844# The interwiner: given an element of PGammaO(2n+1,q): it is sufficient to select 3845# a submatrix, sinc the projection is equivalent with removing X0 3846# given an element of PGammaSp: the points (1,0,...0), (0,1,...0), ... (0,0,...0) 3847# are, with an extra 0 in front, also points of Q(2n,q). Compute the elements of 3848# the symplectic points, compute the preimage of this image under the projection 3849# this yields the first column of the matrix of the corresponding element of 3850# PGammaO. Since the nucleus is fixed under the elements of PGammaO, the first 3851# row (except the very first element), is zero. To compute the first element, 3852# we need the image of the n+2 nd point under the given element. So find a 3853# symplectic point that together with the other symplectic points forms part of a 3854# frame, make sure the preimage can be computed, compute the image under el, and 3855# compute its preimage. This gives the very first element of the PGammaO element. 3856# Note that for q=2 this element is necessarily one. Note that the general code 3857# works anyway also for q=2 and n even, which explains the exception in the 3858# twinerprefun. 3859# Finally: base changes are included if Q and W are not canonical. This is straight- 3860# forward. 3861## 3862InstallMethod( IsomorphismPolarSpacesProjectionFromNucleus, 3863 "for two classical polar spaces", 3864 [ IsClassicalPolarSpace, IsClassicalPolarSpace, IsBool ], 3865 function(quadric, w, computeintertwiner) 3866 local q,f, func, pre, nucleus, n, mat, hyp, map, can_form, cquadric, cquadricinv, 3867 cw, cwinv, twinerfunc, twinerprefun, coll1, coll2, hom, ones, one, gens1, gens2; 3868 f := BaseField(quadric); 3869 q := Size(f); 3870 one := One(f); 3871 if not IsEvenInt(q) and f = BaseField(w) then 3872 Error(" Characteristic of basefields must be even and basfields must be equal"); 3873 elif Rank(quadric) <> Rank(w) then 3874 Error(" Rank of polar spaces must be equal "); 3875 fi; 3876 n := quadric!.dimension + 1; 3877 mat := IdentityMat(n,f); 3878 if IsCanonicalPolarSpace(quadric) then 3879 can_form := QuadraticForm(quadric); 3880 cquadric := IdentityMat(n,f); 3881 cquadricinv := cquadric; 3882 else 3883 cquadric := BaseChangeToCanonical(QuadraticForm(quadric)); 3884 can_form := QuadraticFormByMatrix( CanonicalQuadraticForm("parabolic", n, f), f); 3885 cquadricinv := cquadric^-1; 3886 fi; 3887 if IsCanonicalPolarSpace(w) then 3888 cw := IdentityMat(n-1,f); 3889 cwinv := cw; 3890 else 3891 cw := BaseChangeToCanonical(SesquilinearForm(w)); 3892 cwinv := cw^-1; 3893 fi; 3894 nucleus := mat[1]; #vector representing the nucleus 3895 hyp := mat{[2..n]}; #hyperplane on which we will project. 3896 func := function( el ) 3897 local vec, proj; 3898 if el!.type = 1 then 3899 vec := [Unpack(el!.obj) * cquadricinv, nucleus]; 3900 else 3901 vec := Concatenation(Unpack(el!.obj) * cquadricinv, [nucleus]); 3902 fi; 3903 proj := SumIntersectionMat(vec, hyp)[2]; #hard coded Meet operation :-) 3904 vec := List(proj,x->x{[2..n]}); #make it a GF(q)^(n-1) vector ends the projection. 3905 return VectorSpaceToElement(w, vec * cw); 3906 end; 3907 pre := function(el) 3908 local vec, i; 3909 if el!.type = 1 then 3910 vec := [Unpack(el!.obj) * cwinv]; 3911 else 3912 vec := Unpack(el!.obj) * cwinv; 3913 fi; 3914 vec := List(vec,x->Concatenation([0*Z(q)^0],x)); 3915 for i in [1..Length(vec)] do 3916 vec[i][1] := (vec[i]^can_form)^(q/2); 3917 od; 3918 return VectorSpaceToElement(quadric, vec * cquadric); 3919 end; 3920 3921 ones := List([1..n],x->one); #all ones for several purposes. 3922 3923 twinerfunc := function( el ) 3924 local mat,frob; 3925 frob := el!.frob; 3926 #mat := Unpack(el!.mat){[2..n]}{[2..n]}; 3927 mat := cquadric * Unpack(el!.mat) * cquadricinv^(frob^-1); 3928 mat := mat{[2..n]}{[2..n]}; 3929 return ProjElWithFrob(cwinv * mat * cw^(frob^-1),frob,f); 3930 end; 3931 3932 twinerprefun := function( el ) 3933 local newmat,mat,frob,i,vec,y,newvec,z,zprime; 3934 frob := el!.frob; 3935 mat := cw * Unpack(el!.mat) * cwinv^(frob^-1); 3936 #mat := Unpack(el!.mat); 3937 newmat := NullMat(n,n,f); 3938 newmat{[2..n]}{[2..n]} := mat; 3939 3940 #compute the first column of newmat (except for newmat[1][1]. 3941 for i in [2..n] do 3942 vec := Concatenation([0*Z(q)^0],mat[i-1])^frob; 3943 newmat[i][1] := ((vec^can_form)^(q/2))^(frob^-1); 3944 od; 3945 #vec := List(TransposedMat(mat),x->Sum(x)^frob); #is the image of (1,1,...,1) under el. 3946 3947 if not (q=2 and IsEvenInt((n-1)/2)) then 3948 vec := ShallowCopy(ones); 3949 vec[1] := 0*one; 3950 vec[2] := Z(q); 3951 z := (vec^can_form)^(q/2); 3952 3953 y := vec*newmat; 3954 y := y[1]; 3955 newvec := (vec * newmat)^frob; #remind that the symplectic part is in [2..n]. 3956 newvec[1] := 0*one; 3957 zprime := ((newvec^can_form)^(q/2))^(frob^-1); 3958 3959 #image of (alpha,1,1,...,1) under el (symplectic point!) 3960 3961 newmat[1][1] := (zprime+y)/z; 3962 else 3963 newmat[1][1] := one; 3964 fi; 3965 3966 return ProjElWithFrob(cquadricinv * newmat * cquadric^(frob^-1), frob, f); 3967 #return ProjElWithFrob( newmat , frob, f); 3968 end; 3969 3970 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(quadric), ElementsOfIncidenceStructure(w), func, pre); 3971 SetIsBijective( map, true ); 3972 3973 if computeintertwiner then 3974 if (HasIsCanonicalPolarSpace( quadric ) and IsCanonicalPolarSpace( quadric )) or 3975 HasCollineationGroup( quadric ) then 3976 coll1 := CollineationGroup(quadric); 3977 gens1 := GeneratorsOfGroup( coll1 ); 3978 gens2 := List(gens1, twinerfunc); 3979 coll2 := GroupWithGenerators(gens2); 3980 hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 3981 SetIntertwiner( map, hom ); 3982 UseIsomorphismRelation(coll1,coll2); 3983 elif (HasIsCanonicalPolarSpace( w ) and IsCanonicalPolarSpace( w )) or 3984 HasCollineationGroup( w ) then 3985 coll2 := CollineationGroup( w ); 3986 gens2 := GeneratorsOfGroup( coll2 ); 3987 gens1 := List(gens2, twinerprefun ); 3988 coll1 := GroupWithGenerators(gens1); 3989 hom := GroupHomomorphismByFunction(coll1, coll2, twinerfunc, twinerprefun); 3990 SetIntertwiner( map, hom ); 3991 UseIsomorphismRelation(coll1,coll2); 3992 else 3993 Info(InfoFinInG, 1, "No intertwiner computed. One of the polar spaces must have a collineation group computed"); 3994 fi; 3995 fi; 3996 3997 return map; 3998 end ); 3999 4000# Added july 2014 jdb. 4001############################################################################# 4002#O SelfDualitySymplectic 4003# combines NaturalDualitySymplectic with IsomorphismPolarSpacesProjectionFromNucleus, 4004# most of the code is recycled from these operations, and is documented there. 4005# Note that we have less base changes to deal with. Starting from a canonical W(3,q), 4006# the obtained Q(4,q) has form X_0^2+X1X4+X2X3=0. The base change X4<->X2 is represented 4007# by perm5 at the Q(4,q) side and perm4 at the projected W(3,q) side. 4008# This operation is a helper operation for SelfDuality, there are no checks 4009# on the input. 4010# order: W(3,q) -> Q(4,q) -> W(3,q), by Plucker and then projection. 4011# -> twinerfunc: 1. PsP(4,q) -> PGO(5,q) by Plucker (twinerfunc of NaturalDualitySymplectic) 4012# 2. PGO(5,q) -> PsP(4,q) by projection (twinerfunc of Isomorphism...Nucleus) 4013# -> twinerprefun: 1. PsP(4,q) -> PPGO(5,q) by inverse projection (twinerprefun of Isomorphism...Nucleus) 4014# 2. PGO(5,q) -> PsP(4,q) by inverse Plucker (twinerprefun of NaturalDualitySymplectic) 4015############################################################################# 4016# 4017InstallMethod( SelfDualitySymplectic, 4018 "for a symplectic GQ and a boolean", 4019 [ IsClassicalGQ, IsBool ], 4020 function( w, computeintertwiner ) 4021 local f, one, mat, quad_form, quadric, data, form_w, cw, cwinv, q, map, 4022 func, pre, formw, delta, hyp, coll1, hom, ones, can_form, 4023 twinerfunc, twinerprefun, cq4qinv, id, hyp2, nucleus, perm4, gram, perm5; 4024 f := w!.basefield; 4025 one := One(f); 4026 formw := SesquilinearForm( w ); 4027 delta := PolarityOfProjectiveSpace(w); 4028 hyp := IdentityMat(6,f){[1..5]}; #this and next are in fact HyperplaneByDualCoorindates([1,0,0,0,0,1]..) 4029 hyp[1][6] := -one; 4030 nucleus := [1,0,0,0,0]*one; 4031 mat := IdentityMat(5,f); 4032 hyp2 := mat{[2..5]}; 4033 perm4 := IdentityMat(4,f){[1,4,3,2]}; 4034 perm5 := IdentityMat(5,f){[1,2,5,4,3]}; 4035 gram := [[1,0,0,0,0],[0,0,0,0,1],[0,0,0,1,0],[0,0,0,0,0],[0,0,0,0,0]]*one; 4036 quad_form := QuadraticFormByMatrix(gram,f); 4037 q := Size(f); 4038 cw := IdentityMat(4,f); 4039 if not IsCanonicalPolarSpace( w ) then 4040 cw := BaseChangeToCanonical( formw ); 4041 fi; 4042 cwinv := cw^-1; 4043 can_form := QuadraticFormByMatrix( CanonicalQuadraticForm("parabolic", 5, f), f); 4044 ones := List([1..5],x->one); #all ones for several purposes. 4045 id := IdentityMat(4, f); 4046 4047 func := function( el ) 4048 local list,plane,vec,proj; 4049 if el!.type = 2 then #dealing with a line 4050 vec := [ PluckerCoordinates(Unpack(el!.obj) * cwinv){[1..5]}, nucleus]; 4051 proj := SumIntersectionMat(vec, hyp2)[2]; 4052 vec := List(proj,x->x{[2..5]}); 4053 return VectorSpaceToElement(w, ( vec * perm4 ) * cw); 4054 else 4055 plane := el^delta; 4056 vec := Unpack(plane!.obj) * cwinv; 4057 list := [ PluckerCoordinates(vec{[1,2]}), PluckerCoordinates(vec{[2,3]}), PluckerCoordinates(vec{[1,3]}) ]; 4058 plane := SumIntersectionMat(list, hyp)[2]; #hard coded Meet operation :-) 4059 list := List(plane,x->x{[1..5]}); 4060 vec := Concatenation(list, [nucleus]); 4061 proj := SumIntersectionMat(vec, hyp2)[2]; 4062 vec := List(proj,x->x{[2..5]}); 4063 return VectorSpaceToElement(w, ( vec * perm4 ) * cw); 4064 fi; 4065 end; 4066 4067 pre := function( el ) 4068 local vec,i; 4069 if el!.type = 1 then #dealing with a point 4070 vec := [Unpack(el!.obj) * cwinv] * perm4; 4071 else 4072 vec := (Unpack(el!.obj) * cwinv )* perm4; 4073 fi; 4074 vec := List(vec,x->Concatenation([0*Z(q)^0],x)); 4075 for i in [1..Length(vec)] do 4076 vec[i][1] := (vec[i]^quad_form)^(q/2); 4077 od; 4078 if el!.type = 1 then #dealing with a point 4079 vec[1][6] := -vec[1][1]; 4080 return VectorSpaceToElement(w, InversePluckerCoordinates(vec[1]) * cw ); 4081 else 4082 vec[1][6] := -vec[1][1]; 4083 vec[2][6] := -vec[2][1]; 4084 return VectorSpaceToElement(w, (SumIntersectionMat(InversePluckerCoordinates(vec[1]), InversePluckerCoordinates(vec[2]))[2]) * cw ); 4085 fi; 4086 end; 4087 4088 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(w), ElementsOfIncidenceStructure(w), func, pre); 4089 SetIsBijective( map, true ); 4090 4091 twinerfunc := function(g) 4092 local mat,newmat,frob; 4093 frob := g!.frob; 4094 mat := cw * Unpack(g!.mat) * cwinv^(frob^-1); 4095 newmat := []; 4096 newmat[2] := PluckerCoordinates([mat[3],mat[1]]){[1..5]}; 4097 newmat[3] := PluckerCoordinates([mat[4],mat[1]]){[1..5]}; 4098 newmat[4] := -PluckerCoordinates([-mat[3],mat[2]]){[1..5]}; 4099 newmat[5] := PluckerCoordinates([mat[4],-mat[2]]){[1..5]}; 4100 newmat[1] := PluckerCoordinates([mat[2]+mat[3],mat[1]+mat[4]]){[1..5]} - newmat[2] - newmat[5]; 4101 newmat := perm5 * newmat * perm5^(frob^-1); 4102 newmat := newmat{[2..5]}{[2..5]}; 4103 return ProjElWithFrob(cwinv * newmat * cw^(frob^-1),frob,f); 4104 end; 4105 4106 twinerprefun := function( el ) 4107 local newmat,mat,frob,i,vec,y,newvec,z,zprime, 4108 lines, pts, ipts, ilines, e, x, ept, ielines, 4109 ie, cs, iept, j; 4110 frob := el!.frob; 4111 mat := cw * Unpack(el!.mat) * cwinv^(frob^-1); 4112 newmat := NullMat(5,5,f); 4113 newmat{[2..5]}{[2..5]} := mat; 4114 4115 #compute the first column of newmat (except for newmat[1][1]. 4116 for i in [2..5] do 4117 vec := Concatenation([0*Z(q)^0],mat[i-1])^frob; 4118 newmat[i][1] := ((vec^can_form)^(q/2))^(frob^-1); 4119 od; 4120 4121 if not q = 2 then 4122 vec := ShallowCopy(ones); 4123 vec[1] := 0*one; 4124 vec[2] := Z(q); 4125 z := (vec^can_form)^(q/2); 4126 4127 y := vec*newmat; 4128 y := y[1]; 4129 newvec := (vec * newmat)^frob; #remind that the symplectic part is in [2..n]. 4130 newvec[1] := 0*one; 4131 zprime := ((newvec^can_form)^(q/2))^(frob^-1); 4132 4133 #image of (alpha,1,1,...,1) under el (symplectic point!) 4134 4135 newmat[1][1] := (zprime+y)/z; 4136 else 4137 newmat[1][1] := one; 4138 fi; 4139 4140 mat := perm5 * newmat * perm5; 4141 4142 lines:= []; 4143 lines[1] := [[id[1],id[3]],[id[1],id[4]]]; 4144 lines[2] := [[id[2],id[3]],[id[2],id[4]]]; 4145 lines[3] := [[id[3],id[1]],[id[3],id[2]]]; 4146 lines[4] := [[id[4],id[1]],[id[4],id[2]]]; 4147 pts := List(lines,x->List(x,y->PluckerCoordinates(y){[1..5]})); 4148 ipts := List(pts,x->x*mat); 4149 for i in [1..Length(ipts)] do 4150 for j in [1..2] do 4151 ipts[i][j][6] := -ipts[i][j][1]; 4152 od; 4153 od; 4154 ilines := List(ipts,x->List(x,y->InversePluckerCoordinates(y))); 4155 ipts := List(ilines, x->SumIntersectionMat(x[1],x[2])[2]); 4156 newmat := List(ipts,x->x[1]); 4157 e := [1,1,1,1]*one; 4158 ept := List([[e,id[1]+id[4]],[e,id[1]+id[2]]],y->PluckerCoordinates(y){[1..5]}); 4159 iept := List(ept,x->x*mat); 4160 iept[1][6] := -iept[1][1]; 4161 iept[2][6] := -iept[2][1]; 4162 4163 ielines := List(iept,x->InversePluckerCoordinates(x)); 4164 ie := SumIntersectionMat(ielines[1],ielines[2])[2]; 4165 cs := SolutionMat(newmat,ie[1]); 4166 for i in [1..4] do 4167 newmat[i] := newmat[i]*cs[i]; 4168 od; 4169 return ProjElWithFrob(cwinv * newmat * cw^(frob^-1),frob,f); 4170 4171 end; 4172 4173 if computeintertwiner then 4174 if (HasIsCanonicalPolarSpace( w ) and IsCanonicalPolarSpace( w )) or 4175 HasCollineationGroup( w ) then 4176 coll1 := CollineationGroup(w); 4177 hom := GroupHomomorphismByFunction(coll1, coll1, twinerfunc, twinerprefun); 4178 SetIntertwiner( map, hom ); 4179 #UseIsomorphismRelation(coll1,coll2); 4180 else 4181 Info(InfoFinInG, 1, "No intertwiner computed. The polar space must have a collineation group computed"); 4182 fi; 4183 fi; 4184 4185 return map; 4186end ); 4187 4188# Added july 2014 jdb. 4189############################################################################# 4190#O SelfDualityParabolic 4191# combines NaturalDualitySymplectic with IsomorphismPolarSpacesProjectionFromNucleus, 4192# most of the code is recycled from these operations, and is documented there. 4193# Note that we have less base changes to deal with. Starting from a canonical W(3,q), 4194# the obtained Q(4,q) has form X_0^2+X1X4+X2X3=0. The base change X4<->X2 is represented 4195# by perm5 at the Q(4,q) side and perm4 at the projected W(3,q) side. 4196# This operation is a helper operation for SelfDuality, there are no checks 4197# on the input. 4198# order: Q(4,q) -> W(3,q) -> Q(4,q), by projection and then Plucker. 4199# -> twinerfunc: 1. PGO(5,q) -> PsP(4,q) by projection (twinerfunc of Isomorphism...Nucleus) 4200# 2. PsP(4,q) -> PGO(5,q) by Plucker (twinerfunc of NaturalDualitySymplectic) 4201# -> twinerprefun: 1. PGO(5,q) -> PsP(4,q) by inverse Plucker (twinerprefun of NaturalDualitySymplectic) 4202# 2. PsP(4,q) -> PPGO(5,q) by inverse projection (twinerprefun of Isomorphism...Nucleus) 4203############################################################################# 4204# 4205InstallMethod( SelfDualityParabolic, 4206 "for a symplectic GQ and a boolean", 4207 [ IsClassicalGQ, IsBool ], 4208 function( q4q, computeintertwiner ) 4209 local f, one, mat, quad_form, quadric, data, cq, cqinv, q, map, can_form, 4210 func, pre, formq, delta, hyp, coll1, coll2, gens1, gens2, hom, ones, 4211 twinerfunc, twinerprefun, id, hyp2, nucleus, perm4, perm5, gram; 4212 4213 f := q4q!.basefield; 4214 one := One(f); 4215 hyp := IdentityMat(6,f){[1..5]}; #this and next are in fact HyperplaneByDualCoorindates([1,0,0,0,0,1]..) 4216 hyp[1][6] := -one; 4217 mat := IdentityMat(5,f); 4218 hyp2 := mat{[2..5]}; 4219 perm4 := IdentityMat(4,f){[1,4,3,2]}; 4220 perm5 := IdentityMat(5,f){[1,2,5,4,3]}; 4221 quad_form := QuadraticFormByMatrix(CanonicalQuadraticForm("parabolic",5,f),f); 4222 q := Size(f); 4223 nucleus := [1,0,0,0,0]*one; 4224 delta := CanonicalGramMatrix("symplectic",4,f); #we will hard code the polarity :-) 4225 formq := QuadraticForm(q4q); 4226 4227 cq := IdentityMat(5,f); 4228 if not IsCanonicalPolarSpace( q4q ) then 4229 cq := BaseChangeToCanonical( formq ); 4230 fi; 4231 cqinv := cq^-1; 4232 4233 func := function( el ) 4234 local list,plane,vec,proj; 4235 if el!.type = 1 then 4236 vec := [Unpack(el!.obj) * cqinv, nucleus]; 4237 else 4238 vec := Concatenation(Unpack(el!.obj) * cqinv, [nucleus]); 4239 fi; 4240 proj := SumIntersectionMat(vec, hyp2)[2]; #hard coded Meet operation :-) 4241 vec := List(proj,x->x{[2..5]}); #make it a GF(q)^(n-1) vector ends the projection. 4242 if el!.type = 2 then 4243 vec := [PluckerCoordinates(vec){[1..5]}] * perm5; 4244 return VectorSpaceToElement(q4q,vec * cq); 4245 else 4246 plane := NullspaceMat(TransposedMat(vec*delta)); 4247 list := [ PluckerCoordinates(plane{[1,2]}), PluckerCoordinates(plane{[2,3]}), PluckerCoordinates(plane{[1,3]}) ]; 4248 plane := SumIntersectionMat(list, hyp)[2]; #hard coded Meet operation :-) 4249 list := List(plane,x->x{[1..5]}) * perm5; 4250 return VectorSpaceToElement(q4q,list * cq); 4251 fi; 4252 end; 4253 4254 pre := function( el ) 4255 local vec,ivec,i; 4256 if el!.type = 1 then #dealing with a point 4257 vec := [Unpack(el!.obj) * cqinv ] * perm5; 4258 vec[1][6] := -vec[1][1]; 4259 ivec := InversePluckerCoordinates(vec[1]); 4260 else 4261 vec := (Unpack(el!.obj) * cqinv )* perm5; 4262 vec[1][6] := -vec[1][1]; 4263 vec[2][6] := -vec[2][1]; 4264 ivec := SumIntersectionMat(InversePluckerCoordinates(vec[1]), InversePluckerCoordinates(vec[2]))[2]; 4265 fi; 4266 ivec := List(ivec,x->Concatenation([0*Z(q)^0],x)); 4267 for i in [1..Length(ivec)] do 4268 ivec[i][1] := (ivec[i]^quad_form)^(q/2); 4269 od; 4270 return VectorSpaceToElement(q4q,ivec * cq); 4271 end; 4272 4273 map := GeometryMorphismByFunction(ElementsOfIncidenceStructure(q4q), ElementsOfIncidenceStructure(q4q), func, pre); 4274 SetIsBijective( map, true ); 4275 4276 can_form := QuadraticFormByMatrix( CanonicalQuadraticForm("parabolic", 5, f), f); 4277 ones := List([1..5],x->one); #all ones for several purposes. 4278 id := IdentityMat(4, f); 4279 4280 twinerfunc := function( el ) 4281 local mat,frob, newmat; 4282 frob := el!.frob; 4283 mat := cq * Unpack(el!.mat) * cqinv^(frob^-1); 4284 mat := mat{[2..5]}{[2..5]}; 4285 newmat := []; 4286 newmat[2] := PluckerCoordinates([mat[3],mat[1]]){[1..5]}; 4287 newmat[3] := PluckerCoordinates([mat[4],mat[1]]){[1..5]}; 4288 newmat[4] := -PluckerCoordinates([-mat[3],mat[2]]){[1..5]}; 4289 newmat[5] := PluckerCoordinates([mat[4],-mat[2]]){[1..5]}; 4290 newmat[1] := PluckerCoordinates([mat[2]+mat[3],mat[1]+mat[4]]){[1..5]} - newmat[2] - newmat[5]; 4291 newmat := perm5 * newmat * perm5; 4292 return ProjElWithFrob(cqinv * newmat * cq^(frob^-1),frob,f); 4293 end; 4294 4295 twinerprefun := function( el ) 4296 local newmat,mat,frob,i,vec,y,newvec,z,zprime, 4297 lines, pts, ipts, ilines, e, x, ept, ielines, 4298 ie, cs, iept, j; 4299 frob := el!.frob; 4300 mat := perm5 * cq * Unpack(el!.mat) * cqinv^(frob^-1) * perm5; 4301 4302 lines:= []; 4303 lines[1] := [[id[1],id[3]],[id[1],id[4]]]; 4304 lines[2] := [[id[2],id[3]],[id[2],id[4]]]; 4305 lines[3] := [[id[3],id[1]],[id[3],id[2]]]; 4306 lines[4] := [[id[4],id[1]],[id[4],id[2]]]; 4307 pts := List(lines,x->List(x,y->PluckerCoordinates(y){[1..5]})); 4308 ipts := List(pts,x->x*mat); 4309 for i in [1..Length(ipts)] do 4310 for j in [1..2] do 4311 ipts[i][j][6] := -ipts[i][j][1]; 4312 od; 4313 od; 4314 ilines := List(ipts,x->List(x,y->InversePluckerCoordinates(y))); 4315 ipts := List(ilines, x->SumIntersectionMat(x[1],x[2])[2]); 4316 newmat := List(ipts,x->x[1]); 4317 e := [1,1,1,1]*one; 4318 ept := List([[e,id[1]+id[4]],[e,id[1]+id[2]]],y->PluckerCoordinates(y){[1..5]}); 4319 iept := List(ept,x->x*mat); 4320 iept[1][6] := -iept[1][1]; 4321 iept[2][6] := -iept[2][1]; 4322 4323 ielines := List(iept,x->InversePluckerCoordinates(x)); 4324 ie := SumIntersectionMat(ielines[1],ielines[2])[2]; 4325 cs := SolutionMat(newmat,ie[1]); 4326 for i in [1..4] do 4327 newmat[i] := newmat[i]*cs[i]; 4328 od; 4329 mat := ShallowCopy(newmat); 4330 4331 newmat := NullMat(5,5,f); 4332 newmat{[2..5]}{[2..5]} := mat; 4333 4334 #compute the first column of newmat (except for newmat[1][1]. 4335 for i in [2..5] do 4336 vec := Concatenation([0*Z(q)^0],mat[i-1])^frob; 4337 newmat[i][1] := ((vec^can_form)^(q/2))^(frob^-1); 4338 od; 4339 4340 if not q = 2 then 4341 vec := ShallowCopy(ones); 4342 vec[1] := 0*one; 4343 vec[2] := Z(q); 4344 z := (vec^can_form)^(q/2); 4345 4346 y := vec*newmat; 4347 y := y[1]; 4348 newvec := (vec * newmat)^frob; #remind that the symplectic part is in [2..n]. 4349 newvec[1] := 0*one; 4350 zprime := ((newvec^can_form)^(q/2))^(frob^-1); 4351 4352 #image of (alpha,1,1,...,1) under el (symplectic point!) 4353 4354 newmat[1][1] := (zprime+y)/z; 4355 else 4356 newmat[1][1] := one; 4357 fi; 4358 4359 return ProjElWithFrob(cqinv * newmat * cq^(frob^-1),frob,f); 4360 4361 end; 4362 4363 if computeintertwiner then 4364 if (HasIsCanonicalPolarSpace( q4q ) and IsCanonicalPolarSpace( q4q )) or 4365 HasCollineationGroup( q4q ) then 4366 coll1 := CollineationGroup(q4q); 4367 hom := GroupHomomorphismByFunction(coll1, coll1, twinerfunc, twinerprefun); 4368 SetIntertwiner( map, hom ); 4369 #UseIsomorphismRelation(coll1,coll2); 4370 else 4371 Info(InfoFinInG, 1, "No intertwiner computed. The polar space must have a collineation group computed"); 4372 fi; 4373 fi; 4374 4375 return map; 4376end ); 4377 4378# Added july 2014 jdb. 4379############################################################################# 4380#O SelfDuality( <gq>, <bool> ) 4381# This is the interface to the helper functions. It simply checks the input 4382# and decides which NaturalDuality... to use. 4383## 4384InstallMethod( SelfDuality, 4385 "for a GQ and a boolean", 4386 [ IsClassicalGQ, IsBool ], 4387 function( gq, computeintertwiner ) 4388 local f; 4389 f := gq!.basefield; 4390 if not IsEvenInt(Characteristic(f)) then 4391 Error( "<gq> must be a GQ over a field of even characteristic"); 4392 fi; 4393 if IsSymplecticSpace(gq) then 4394 return SelfDualitySymplectic(gq, computeintertwiner ); 4395 elif IsParabolicQuadric( gq ) then 4396 return SelfDualityParabolic(gq, computeintertwiner ); 4397 else 4398 Error("<gq> must be a symplectic GQ or a parabolic quadric"); 4399 fi; 4400end ); 4401 4402# Added july 2014 jdb. 4403############################################################################# 4404#O SelfDuality( <gq>, <bool> ) 4405# This is the interface to the helper functions. It returns SelfDuality(<gq>,true) 4406## 4407InstallMethod( SelfDuality, 4408 "for a GQ and a boolean", 4409 [ IsClassicalGQ ], 4410 function( gq ) 4411 return SelfDuality(gq, true); 4412end ); 4413 4414 4415