Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
| Download
GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
Project: cocalc-sagemath-dev-slelievre
Views: 418346<Chapter Label="Objects">1<Heading>Implementing circle objects</Heading>23In this chapter we explain how the &GAP; system may be extended4with new objects using the circle multiplication as an example.5We follow the guidelines given in the &GAP; Reference Manual6(see <Ref Chap="Creating New Objects" BookName="ref"/> and subsequent7chapters), to which we refer for more details.89<Section Label="ObjectsFirst">10<Heading>First attempts</Heading>1112Of course, having two ring elements, you can straightforwardly compute13their circle product defined as <M> r \cdot s = r + s + rs </M>. You can14do this in a command line, and it is a trivial task to write a simplest15function of two arguments that will do this:1617<Example>18<![CDATA[19gap> CircleMultiplication := function(a,b)20> return a+b+a*b;21> end;22function( a, b ) ... end23gap> CircleMultiplication(2,3);241125gap> CircleMultiplication( ZmodnZObj(2,8), ZmodnZObj(5,8) );26ZmodnZObj( 1, 8 )27]]>28</Example>2930However, there is no check whether both arguments belong to the31same ring and whether they are ring elements at all, so it is easy32to obtain some meaningless results:3334<Example>35<![CDATA[36gap> CircleMultiplication( 3, ZmodnZObj(3,8) );37ZmodnZObj( 7, 8 )38gap> CircleMultiplication( [1], [2,3] );39[ 5, 5 ]40]]>41</Example>4243You can include some tests for arguments, and maybe the best way of44doing this would be declaring a new operation for two ring elements,45and installing the previous function as a method for this operation.46This will check automatically if the arguments are ring elements from47the common ring:4849<Example>50<![CDATA[51gap> DeclareOperation( "BetterCircleMultiplication",52> [IsRingElement,IsRingElement] );53gap> InstallMethod( BetterCircleMultiplication,54> IsIdenticalObj,55> [IsRingElement,IsRingElement],56> CircleMultiplication );57gap> BetterCircleMultiplication(2,3);581159gap> BetterCircleMultiplication( ZmodnZObj(2,8), ZmodnZObj(5,8) );60ZmodnZObj( 1, 8 )61]]>62</Example>6364Nevertheless, the functionality gained from such operation would be rather65limited. You will not be able to compute circle product via the infix66operator <C>*</C>, and, moreover, you will not be able to create higher67level objects such as semigroups and groups with respect to the circle68multiplication.<P/>6970In order to "integrate" the circle multiplication into the &GAP; library71properly, instead of defining <E>new</E> operations for existing objects,72we should define <E>new</E> objects for which the infix operator <C>*</C>73will perform the circle multiplication. This approach is explained in the74next two sections.7576</Section>7778<Section Label="ObjectsDefining">79<Heading>Defining circle objects</Heading>8081Thus, we are going to implement <E>circle objects</E>,82for which we can envisage the following functionality:8384<Example>85<![CDATA[86gap> CircleObject( 2 ) * CircleObject( 3 );87CircleObject( 11 )88]]>89</Example>9091First we need to distinguish these new objects from other &GAP; objects.92This is done via the <E>type</E> of the objects, that is mainly determined93by their <E>category</E>, <E>representation</E> and <E>family</E>.94<P/>9596We start with declaring the category <C>IsCircleObject</C> as a subcategory97of <C>IsAssociativeElement></C> and <C>IsMultiplicativeElementWithInverse</C>.98Thus, each circle object will "know" that it is <C>IsAssociativeElement</C>99and <C>IsMultiplicativeElementWithInverse</C>, and this will make it possible100to apply to circle objects such operations as <C>One</C> and <C>Inverse</C>101(the latter is allowed to return <K>fail</K> for a given circle object), and102construct semigroups generated by circle objects.103<P/>104105<Example>106<![CDATA[107gap> DeclareCategory( "IsMyCircleObject",108> IsAssociativeElement and IsMultiplicativeElementWithInverse );109]]>110</Example>111112Further we would like to create semigroups and groups generated by circle113objects. Such structures will be <E>collections</E> of circle objects, so114they will be in the category <C>CategoryCollections( IsCircleObject )</C>.115This is why immediately after we declare the underlying category of circle116objects, we need also to declare the category of their collections:117118<Example>119<![CDATA[120gap> DeclareCategoryCollections( "IsMyCircleObject" );121]]>122</Example>123124On the next step we should think about the internal representation of125circle objects. A natural way would be to store the underlying ring element126in a list-like structure at its first position. We do not foresee any other127data that we need to store internally in the circle object. This is quite128common situation, so we may define first <C>IsPositionalObjectOneSlotRep</C>129that is the list-like representation with only one position in the list, and130then declare a synonym <C>IsDefaultCircleObject</C> that means that we are131dealing with a circle object in one-slot representation:132133<Example>134<![CDATA[135gap> DeclareRepresentation( "IsMyPositionalObjectOneSlotRep",136> IsPositionalObjectRep, [ 1 ] );137gap> DeclareSynonym( "IsMyDefaultCircleObject",138> IsMyCircleObject and IsMyPositionalObjectOneSlotRep );139]]>140</Example>141142Until now we are still unable to create circle objects, because we did not143specify to which family they will belong. Naturally, having a ring, we want144to have all circle objects for elements of this ring in the same family to145be able to multiply them, and we expect circle objects for elements of146different rings to be placed in different families. Thus, it would be nice147to establish one-to-one correspondence between the family of ring elements148and a family of circle elements for this ring. We can store the corresponding149circle family as an attribute of the ring elements family. To do this first150we declare an attribute <C>CircleFamily</C> for families:151152<Example>153<![CDATA[154gap> DeclareAttribute( "MyCircleFamily", IsFamily );155]]>156</Example>157158Now we install the method that stores the corresponding159circle family in this attribute:160161<Example>162<![CDATA[163gap> InstallMethod( MyCircleFamily,164> "for a family",165> [ IsFamily ],166> function( Fam )167> local F;168> # create the family of circle elements169> F:= NewFamily( "MyCircleFamily(...)", IsMyCircleObject );170> if HasCharacteristic( Fam ) then171> SetCharacteristic( F, Characteristic( Fam ) );172> fi;173> # store the type of objects in the output174> F!.MyCircleType:= NewType( F, IsMyDefaultCircleObject );175> # Return the circle family176> return F;177> end );178]]>179</Example>180181Similarly, we want one-to-one correspondence between182circle elements and underlying ring elements. We declare183an attribute <C>CircleObject</C> for a ring element,184and then install the method to create new circle object from185the ring element. This method takes the family of the186ring element, finds corresponding circle family, extracts187from it the type of circle objects and finally creates the188new circle object of that type:189190<Example>191<![CDATA[192gap> DeclareAttribute( "MyCircleObject", IsRingElement );193gap> InstallMethod( MyCircleObject,194> "for a ring element",195> [ IsRingElement ],196> obj -> Objectify( MyCircleFamily( FamilyObj( obj ) )!.MyCircleType,197> [ Immutable( obj ) ] ) );198]]>199</Example>200201Only after entering all code above we are able to create some circle object.202However, it is displayed just as <C><object></C>, though we can get203the underlying ring element using the "!" operator:204205<Example>206<![CDATA[207gap> a:=MyCircleObject(2);208<object>209gap> a![1];2102211]]>212</Example>213214We can check that the intended relation between families holds:215<P/>216217<Example>218<![CDATA[219gap> FamilyObj( MyCircleObject ( 2 ) ) = MyCircleFamily( FamilyObj( 2 ) );220true221]]>222</Example>223224We can not multiply circle objects yet. But before implementing this,225first let us improve the output by installing the method for <C>PrintObj</C>:226227<Example>228<![CDATA[229gap> InstallMethod( PrintObj,230> "for object in `IsMyCircleObject'",231> [ IsMyDefaultCircleObject ],232> function( obj )233> Print( "MyCircleObject( ", obj![1], " )" );234> end );235]]>236</Example>237238This method will be used by <C>Print</C> function, and also239by <C>View</C>, since we did not install special method for240<C>ViewObj</C> for circle objects. As a result of this installation,241the output became more meaningful:242243<Example>244<![CDATA[245gap> a;246MyCircleObject( 2 )247]]>248</Example>249250We need to avoid the usage of "!" operator, which, in general, is not251recommended to the user (for example, if &GAP; developers will252change the internal representation of some object, all &GAP; functions253that deal with it must be adjusted appropriately, while if the user's code254had direct access to that representation via "!", an error may occur).255To do this, we wrap getting the first component of a circle object256in the following operation:257258<Example>259<![CDATA[260gap> DeclareAttribute("UnderlyingRingElement", IsMyCircleObject );261gap> InstallMethod( UnderlyingRingElement,262> "for a circle object",263> [ IsMyCircleObject],264> obj -> obj![1] );265gap> UnderlyingRingElement(a);2662267]]>268</Example>269270</Section>271272<Section Label="ObjectsOperations">273<Heading>Installing operations for circle objects</Heading>274275Now we are finally able to install circle multiplication as a default276method for the multiplication of circle objects, and perform the277computation that we envisaged in the beginning:278279<Example>280<![CDATA[281gap> InstallMethod( \*,282> "for two objects in `IsMyCircleObject'",283> IsIdenticalObj,284> [ IsMyDefaultCircleObject, IsMyDefaultCircleObject ],285> function( a, b )286> return MyCircleObject( a![1] + b![1] + a![1]*b![1] );287> end );288gap> MyCircleObject(2)*MyCircleObject(3);289MyCircleObject( 11 )290]]>291</Example>292293However, this functionality is not enough to form semigroups or groups294generated by circle elements. We need to be able to check whether two295circle objects are equal, and we need to define ordering for them (for296example, to be able to form sets of circle elements). Since we already297have both operations for underlying ring elements, this can be implemented298in a straightforward way:299300<Example>301<![CDATA[302gap> InstallMethod( \=,303> "for two objects in `IsMyCircleObject'",304> IsIdenticalObj,305> [ IsMyDefaultCircleObject, IsMyDefaultCircleObject ],306> function( a, b )307> return a![1] = b![1];308> end );309gap> InstallMethod( \<,310> "for two objects in `IsMyCircleObject'",311> IsIdenticalObj,312> [ IsMyDefaultCircleObject, IsMyDefaultCircleObject ],313> function( a, b )314> return a![1] < b![1];315> end );316]]>317</Example>318319Further, zero element of the ring plays a role of the neutral element320for the circle multiplication, and we add this knowledge to our code321in a form of a method for <C>OneOp</C> that returns circle object322for the corresponding zero object:323324<Example>325<![CDATA[326gap> InstallMethod( OneOp,327> "for an object in `IsMyCircleObject'",328> [ IsMyDefaultCircleObject ],329> a -> MyCircleObject( Zero( a![1] ) ) );330gap> One(a);331MyCircleObject( 0 )332]]>333</Example>334335Now we are already able to create monoids generated by circle objects:336337<Example>338<![CDATA[339gap> S:=Monoid(a);340<commutative monoid with 1 generator>341gap> One(S);342MyCircleObject( 0 )343gap> S:=Monoid( MyCircleObject( ZmodnZObj( 2,8) ) );344<commutative monoid with 1 generator>345gap> Size(S);3462347gap> AsList(S);348[ MyCircleObject( ZmodnZObj( 0, 8 ) ), MyCircleObject( ZmodnZObj( 2, 8 ) ) ]349]]>350</Example>351352Finally, to generate groups using circle objects, we need to add a method353for the <C>InverseOp</C>. In our implementation we will assume that the354underlying ring is a subring of the ring with one, thus, if the circle355inverse for an element <M>x</M> exists, than it can be computed as356<M>-x(1+x)^{-1}</M>:357358<Example>359<![CDATA[360gap> InstallMethod( InverseOp,361> "for an object in `IsMyCircleObject'",362> [ IsMyDefaultCircleObject ],363> function( a )364> local x;365> x := Inverse( One( a![1] ) + a![1] );366> if x = fail then367> return fail;368> else369> return MyCircleObject( -a![1] * x );370> fi;371> end );372gap> MyCircleObject(-2)^-1;373MyCircleObject( -2 )374gap> MyCircleObject(2)^-1;375MyCircleObject( -2/3 )376]]>377</Example>378379The last method already makes it possible to create groups generated by380circle objects (the warning may be ignored):381382<Example>383<![CDATA[384gap> Group( MyCircleObject(2) );385#I default `IsGeneratorsOfMagmaWithInverses' method returns `true' for386[ MyCircleObject( 2 ) ]387<group with 1 generators>388gap> G:=Group( [MyCircleObject( ZmodnZObj( 2,8 ) ) ]);389#I default `IsGeneratorsOfMagmaWithInverses' method returns `true' for390[ MyCircleObject( ZmodnZObj( 2, 8 ) ) ]391<group with 1 generators>392gap> Size(G);3932394gap> AsList(G);395[ MyCircleObject( ZmodnZObj( 0, 8 ) ), MyCircleObject( ZmodnZObj( 2, 8 ) ) ]396]]>397</Example>398399The &GAP; code used in this Chapter, is contained in the files400<File>circle/lib/circle.gd</File> and <File>circle/lib/circle.gi</File>401(without <C>My</C> in identifiers).402For more examples of implementing new &GAP; objects and further details403see <Ref Chap="Creating New Objects" BookName="ref"/> and subsequent404chapters in the &GAP; Reference Manual.405406</Section>407408</Chapter>409410