Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Helpers for Information about Operations and Filters #1593

Merged
merged 5 commits into from
Aug 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions doc/ref/create.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,12 @@ avoid their being overwritten accidentally.
See also Section <Ref Sect="More About Global Variables"/>.

<#Include Label="DeclareCategory">
<#Include Label="TypeOfOperation">
<#Include Label="IsCategory">
<#Include Label="IsRepresentation">
<#Include Label="IsProperty">
<#Include Label="IsAttribute">
<#Include Label="CategoryByName">
<#Include Label="DeclareRepresentation">
<#Include Label="DeclareAttribute">
<#Include Label="DeclareProperty">
Expand All @@ -1188,6 +1194,7 @@ See also Section <Ref Sect="More About Global Variables"/>.
<#Include Label="InstallValue">
<#Include Label="DeclareSynonym">
<#Include Label="FlushCaches">
<#Include Label="FilterByName">

</Section>

Expand Down
1 change: 1 addition & 0 deletions doc/ref/makedocreldata.g
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ GAPInfo.ManualDataRef:= rec(
"../../lib/field.gd",
"../../lib/files.gd",
"../../lib/filter.g",
"../../lib/filter.gi",
"../../lib/fitfree.gd",
"../../lib/fldabnum.gd",
"../../lib/float.gd",
Expand Down
6 changes: 4 additions & 2 deletions hpcgap/lib/filter.g
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,10 @@ BIND_GLOBAL( "IS_FILTER_ATOMIC", function(x)
end);

BIND_GLOBAL( "IsFilter",
x -> IS_OPERATION( x ) and
( FLAG1_FILTER( x ) <> 0 or IS_FILTER_ATOMIC(x) ) );
x -> IS_IDENTICAL_OBJ(x, IS_OBJECT)
or (IS_OPERATION( x )
and ( ( FLAG1_FILTER( x ) <> 0 and FLAGS_FILTER(x) <> false)
or IS_FILTER_ATOMIC(x) ) ) );


## Global Rank declarations
Expand Down
72 changes: 72 additions & 0 deletions hpcgap/lib/filter.gi
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
##
## Copyright (C) 1996, Lehrstuhl D für Mathematik, RWTH Aachen, Germany
## (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland
## Copyright (C) 2002 The GAP Group
##
## This software is licensed under the GPL 2 or later, please refer
## to the COPYRIGHT.md and LICENSE files for details.
##

#############################################################################
##
#V IdOfFilter
##
## <#GAPDoc Label="IdOfFilter">
## <ManSection>
## <Func Name="IdOfFilter" Arg="filter"/>
## <Func Name="IdOfFilterByName" Arg="name"/>
##
## <Description>
## finds the id of the filter <A>filter</A>, or the id of the filter
## with name <A>name</A> respectively.
## The id of a filter is equal to the
## position of this filter in the global FILTERS list.
## <P/>
## Note that not every <C>filter</C> for which <C>IsFilter(filter)</C>
## returns <C>true</C> has an ID, only elementary filters do.
## </Description>
## </ManSection>
## <#/GAPDoc>
##
## Note that the filter ID is stored in FLAG1_FILTER for most filters,
## testers have the ID stored in FLAG2_FILTER, so the code below is
## more efficient than just iterating over the FILTERS list.
##
##
BIND_GLOBAL( "IdOfFilter",
function(filter)
local fid;
atomic readonly FILTER_REGION do
fid := FLAG1_FILTER(filter);
if fid > 0 and FILTERS[fid] = filter then
return fid;
fi;
fid := FLAG2_FILTER(filter);
if fid > 0 and FILTERS[fid] = filter then
return fid;
fi;
od;
return fail;
end);

BIND_GLOBAL( "IdOfFilterByName",
name -> PositionProperty(FILTERS, f -> NAME_FUNC(f) = name) );

#############################################################################
##
#V FilterByName
##
## <#GAPDoc Label="FilterByName">
## <ManSection>
## <Func Name="FilterByName" Arg="name"/>
##
## <Description>
## finds the filter with name <A>name</A> in the global FILTERS list. This
## is useful to find filters that were created but not bound to a global
## variable.
## </Description>
## </ManSection>
## <#/GAPDoc>
##
BIND_GLOBAL( "FilterByName",
name -> First(FILTERS, f -> NAME_FUNC(f) = name) );
75 changes: 0 additions & 75 deletions hpcgap/lib/function.g
Original file line number Diff line number Diff line change
Expand Up @@ -654,81 +654,6 @@ InstallMethod( ViewObj, "for a function", true, [IsFunction], 0,
Print(" ) ... end");
end);

BIND_GLOBAL( "VIEW_STRING_OPERATION", function ( op )
local class, flags, types, catok, repok, propok, seenprop,
t, res;
class := "Operation";
if IS_IDENTICAL_OBJ(op,IS_OBJECT) then
class := "Filter";
elif IS_CONSTRUCTOR(op) then
class := "Constructor";
elif IsFilter(op) then
class := "Filter";
flags := FLAGS_FILTER(op);
if flags <> false then
flags := TRUES_FLAGS(flags);
else
flags := [];
fi;
atomic readonly FILTER_REGION do
types := INFO_FILTERS{flags};
od;
catok := true;
repok := true;
propok := true;
seenprop := false;
for t in types do
if not t in FNUM_REPS then
repok := false;
fi;
if not t in FNUM_CATS then
catok := false;
fi;
if not t in FNUM_PROS and not t in FNUM_TPRS then
propok := false;
fi;
if t in FNUM_PROS then
seenprop := true;
fi;
od;
if seenprop and propok then
class := "Property";
elif catok then
class := "Category";
elif repok then
class := "Representation";
fi;
elif Tester(op) <> false then
# op is an attribute
class := "Attribute";
fi;

# Horrible.
res := "<";
APPEND_LIST(res, class);
APPEND_LIST(res, " \"");
APPEND_LIST(res, NAME_FUNC(op));
APPEND_LIST(res, "\">");
return res;
end);

BIND_GLOBAL( "PRINT_OPERATION",
function ( op )
Print(VIEW_STRING_OPERATION(op));
end);

InstallMethod( ViewObj,
"for an operation",
[ IsOperation ],
PRINT_OPERATION );

InstallMethod( ViewString,
"for an operation",
[ IsOperation ],
function(op)
return VIEW_STRING_OPERATION(op);
end);

#############################################################################
##
#E
1 change: 1 addition & 0 deletions hpcgap/lib/read1.g
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ ReadLib( "permutat.g" );
ReadLib( "trans.g" );
ReadLib( "pperm.g" );

ReadLib( "filter.gi" );
ReadLib( "object.gi" );
ReadLib( "listcoef.gd" );
ReadLib( "info.gd" );
Expand Down
50 changes: 50 additions & 0 deletions hpcgap/tst/testinstall/type.tst
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#
gap> test := x -> List([IsFilter, IsCategory, IsRepresentation, IsAttribute, IsProperty, IsOperation], f -> f(x));;
gap> test(IsFinite);
[ true, false, false, false, true, true ]

#
gap> test(IsMagma);
[ true, true, false, false, false, true ]

#
gap> test(IsCommutative);
[ true, false, false, false, true, true ]

#
gap> test(Size);
[ false, false, false, true, false, true ]

#
gap> test(Group);
[ false, false, false, false, false, false ]

#
gap> test((1,2,3));
[ false, false, false, false, false, false ]

#
gap> test("hello, world");
[ false, false, false, false, false, false ]

#
gap> FilterByName("IsCommutative");
<Property "IsCommutative">
gap> CategoryByName("IsMagma");
<Category "IsMagma">

#
gap> atomic readonly FILTER_REGION do filters := Immutable(FILTERS); od;
gap> ForAll([1..Length(filters)], id -> id = IdOfFilter(filters[id]));
true

#
gap> TypeOfOperation(IsFilter);
Error, <oper> must be an operation

#
gap> List([IsAbelian, HasIsAbelian, IsMutable, \+, Size, AbelianGroupCons, Setter(IS_MUTABLE_OBJ)], TypeOfOperation);
[ "Property", "Filter", "Category", "Operation", "Attribute", "Constructor",
"Setter" ]

#
6 changes: 4 additions & 2 deletions lib/filter.g
Original file line number Diff line number Diff line change
Expand Up @@ -301,8 +301,10 @@ end );
## as all objects satisfy IsObject!
##
BIND_GLOBAL( "IsFilter",
x -> IS_IDENTICAL_OBJ(x, IS_OBJECT) or
( IS_OPERATION( x ) and ( FLAG1_FILTER( x ) <> 0 or x in FILTERS ) ) ) ;
x -> IS_IDENTICAL_OBJ(x, IS_OBJECT)
or ( IS_OPERATION( x )
and ( (FLAG1_FILTER( x ) <> 0 and FLAGS_FILTER(x) <> false)
or x in FILTERS ) ) );
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I am not sure I understand this (both old and new version). Perhaps we can walk through it? (And perhaps the comment above it could be expanded to explain it, too?)

So first we handle IS_OBJECT/IsObject, which is special. OK. next we exclude everything that is not an operation, also fine.

But now in the old code, we look at FLAG1_FILTER( x ), and it is non-zero, we consider x a filter. Aha. But this is not quite right, as we have:

gap> FLAG1_FILTER(Setter(IsPGroup));
625

So I guess that's why you changed this to also requite FLAGS_FILTER(x) <> false ? How about instead or additional using NARG_FUNC(x)=1 ? I simply suggest that as it is immediately clear to me why that's a necessary condition for a filter, while FLAGS_FILTER(x) <> false isn't (simply because I have to look up what it does). But anyway, if using FLAGS_FILTER is "correct", that's also fine.

So... that leaves the question: Why do we need the final or x in FILTERS? Aha:

gap> Length(FILTERS);
2212
gap> Number(FILTERS, x -> IsOperation(x) and FLAG1_FILTER(x) = 0);
905
gap> foo:=Filtered(FILTERS, x -> IsOperation(x) and FLAG1_FILTER(x) = 0);;
gap> for i in [1..10] do Display(Random(foo)); od;
<Filter "HasUpperCentralSeriesOfGroup">
<Filter "HasDecomposedRationalClass">
<Filter "HasOrdersClassRepresentatives">
<Filter "HasCharacteristic">
<Filter "HasFpElmEqualityMethod">
<Filter "HasBlocksAttr">
<Filter "HasLargestElementGroup">
<Filter "HasAlternatingSubgroup">
<Filter "HasExp">
<Filter "HasTestSubnormallyMonomial">
gap> HasExp; Exp;
<Filter "HasExp">
<Attribute "Exp">
gap> HasAlternatingSubgroup; AlternatingSubgroup;
<Filter "HasAlternatingSubgroup">
<Attribute "AlternatingSubgroup">

Aha! So testers for attributes fall through:

gap> FLAG1_FILTER(HasExp);
0
gap> FLAG2_FILTER(HasExp);
308
gap> FLAGS_FILTER(HasExp);
<flag list>

Hmm...

gap> Number(FILTERS, x -> IsOperation(x) and FLAGS_FILTER(x) = false);
0
gap> FLAGS_FILTER(IS_OBJECT);
<flag list>
gap> FLAG1_FILTER(Exp);
0
gap> FLAG2_FILTER(Exp);
308
gap> FLAGS_FILTER(Exp);
<flag list>

At this point I got a headache, and started to wonder: Why don't we add a bit "isFilter" to the header of an operation, and add an IS_FILTER function to the kernel, which just returns that bit?

Note that we don't even need to grow the operation header for this, we could split ENABLED_ATTR into a bitfield. (I'd start by adding an OperationHeader struct, as described previously)

Anyway: none of that is necessary for this PR: even adding a better comment can come in another PR. Let's just not forget...



## Global Rank declarations
Expand Down
70 changes: 70 additions & 0 deletions lib/filter.gi
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
##
## Copyright (C) 1996, Lehrstuhl D für Mathematik, RWTH Aachen, Germany
## (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland
## Copyright (C) 2002 The GAP Group
##
## This software is licensed under the GPL 2 or later, please refer
## to the COPYRIGHT.md and LICENSE files for details.
##

#############################################################################
##
#V IdOfFilter
##
## <#GAPDoc Label="IdOfFilter">
## <ManSection>
## <Func Name="IdOfFilter" Arg="filter"/>
## <Func Name="IdOfFilterByName" Arg="name"/>
##
## <Description>
## finds the id of the filter <A>filter</A>, or the id of the filter
## with name <A>name</A> respectively.
## The id of a filter is equal to the
## position of this filter in the global FILTERS list.
## <P/>
## Note that not every <C>filter</C> for which <C>IsFilter(filter)</C>
## returns <C>true</C> has an ID, only elementary filters do.
## </Description>
## </ManSection>
## <#/GAPDoc>
##
## Note that the filter ID is stored in FLAG1_FILTER for most filters,
## testers have the ID stored in FLAG2_FILTER, so the code below is
## more efficient than just iterating over the FILTERS list.
##
##
BIND_GLOBAL( "IdOfFilter",
function(filter)
local fid;
fid := FLAG1_FILTER(filter);
if fid > 0 and FILTERS[fid] = filter then
return fid;
fi;
fid := FLAG2_FILTER(filter);
if fid > 0 and FILTERS[fid] = filter then
return fid;
fi;
return fail;
end);

BIND_GLOBAL( "IdOfFilterByName",
name -> PositionProperty(FILTERS, f -> NAME_FUNC(f) = name) );

#############################################################################
##
#V FilterByName
##
## <#GAPDoc Label="FilterByName">
## <ManSection>
## <Func Name="FilterByName" Arg="name"/>
##
## <Description>
## finds the filter with name <A>name</A> in the global FILTERS list. This
## is useful to find filters that were created but not bound to a global
## variable.
## </Description>
## </ManSection>
## <#/GAPDoc>
##
BIND_GLOBAL( "FilterByName",
name -> First(FILTERS, f -> NAME_FUNC(f) = name) );
Loading