Skip to content

Commit

Permalink
Deduplicate the canonicalization of used ops of derivations
Browse files Browse the repository at this point in the history
  • Loading branch information
zickgraf committed Oct 9, 2024
1 parent 5097571 commit 9b4dfff
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 183 deletions.
5 changes: 2 additions & 3 deletions CAP/gap/Derivations.gd
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,8 @@ DeclareCategory( "IsDerivedMethod", IsAttributeStoringRep );
#! <C>IsCapCategory</C>. The output of <A>category_filter</A> must not
#! change during the installation of operations. In particular, it must
#! not rely on `CanCompute` to check conditions.
#! @Arguments target_op_name, description, used_ops_with_multiples, func, weight, category_filter
DeclareOperation( "CreateDerivation",
[ IsString, IsString, IsDenseList, IsFunction, IsPosInt, IsFunction ] );
#! @Arguments target_op_name, description, used_ops_with_multiples, func, weight, category_filter, loop_multiplier, category_getters
DeclareGlobalFunction( "CreateDerivation" );

#! @Description
#! A description of the derivation.
Expand Down
290 changes: 111 additions & 179 deletions CAP/gap/Derivations.gi
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,8 @@ InstallGlobalFunction( "DeactivateDerivationInfo",
SetInfoLevel( DerivationInfo, 0 );
end );

InstallMethod( CreateDerivation,
[ IsString, IsString, IsDenseList, IsFunction, IsPosInt, IsFunction ],

function( target_op_name, description, used_op_names_with_multiples_and_category_getters, func, weight, category_filter )
local number_of_proposed_arguments, current_function_argument_number, wrapped_category_filter, derivation;
InstallGlobalFunction( CreateDerivation, function( target_op_name, description, used_ops_with_multiples_and_category_getters, func, weight, category_filter, loop_multiplier, category_getters )
local number_of_proposed_arguments, current_function_argument_number, used_op_names_with_multiples_and_category_getters, collected_list, wrapped_category_filter, derivation, x;

if target_op_name <> "internal dummy function of a final derivation" then

Expand Down Expand Up @@ -60,6 +57,66 @@ function( target_op_name, description, used_op_names_with_multiples_and_category
fi;
# =#

# canonicalize used ops
used_op_names_with_multiples_and_category_getters := [ ];

for x in used_ops_with_multiples_and_category_getters do

if Length( x ) < 2 or not IsFunction( x[1] ) or not IsInt( x[2] ) then

Error( "preconditions must be of the form `[op, mult, getter]`, where `getter` is optional" );

fi;

if (Length( x ) = 2 or (Length( x ) = 3 and x[3] = fail)) and NameFunction( x[1] ) = target_op_name then

Error( "A derivation for ", target_op_name, " has itself as a precondition. This is not supported because we cannot compute a well-defined weight.\n" );

fi;

if Length( x ) = 2 then

Add( used_op_names_with_multiples_and_category_getters, [ NameFunction( x[1] ), x[2], fail ] );

elif Length( x ) = 3 then

if x <> fail and not (IsFunction( x[3] ) and NumberArgumentsFunction( x[3] ) = 1) then

Error( "the category getter must be a single-argument function" );

fi;

Add( used_op_names_with_multiples_and_category_getters, [ NameFunction( x[1] ), x[2], x[3] ] );

else

Error( "The list of preconditions must be a list of pairs or triples." );

fi;

od;

#= comment for Julia
if target_op_name <> "internal dummy function of a final derivation" then

collected_list := CAP_INTERNAL_FIND_APPEARANCE_OF_SYMBOL_IN_FUNCTION( func, RecNames( CAP_INTERNAL_METHOD_NAME_RECORD ), loop_multiplier, CAP_INTERNAL_METHOD_RECORD_REPLACEMENTS, category_getters );

if Length( collected_list ) <> Length( used_op_names_with_multiples_and_category_getters ) or not ForAll( collected_list, c -> c in used_op_names_with_multiples_and_category_getters ) then

SortBy( used_op_names_with_multiples_and_category_getters, x -> x[1] );
SortBy( collected_list, x -> x[1] );

Print(
"WARNING: You have installed a derivation for ", target_op_name, " with preconditions ", used_op_names_with_multiples_and_category_getters,
" but the automated detection has detected the following list of preconditions: ", collected_list, ".\n",
"If this is a bug in the automated detection, please report it.\n"
);

fi;

fi;
# =#

if NumberArgumentsFunction( category_filter ) = 0 or NumberArgumentsFunction( category_filter ) > 1 then

Error( "the CategoryFilter of a derivation must accept exactly one argument" );
Expand Down Expand Up @@ -194,75 +251,19 @@ end );
InstallGlobalFunction( AddDerivation,

function( graph, target_op, description, used_ops_with_multiples_and_category_getters, func, weight, category_filter, loop_multiplier, category_getters, function_called_before_installation, is_with_given_derivation, is_autogenerated_by_CompilerForCAP )
local target_op_name, operations_in_graph, used_op_names_with_multiples_and_category_getters, collected_list, derivation, x;
local target_op_name, derivation, x;

target_op_name := NameFunction( target_op );

## get used ops
operations_in_graph := Operations( graph );

used_op_names_with_multiples_and_category_getters := [ ];

for x in used_ops_with_multiples_and_category_getters do

if Length( x ) < 2 or not IsFunction( x[1] ) or not IsInt( x[2] ) then

Error( "preconditions must be of the form `[op, mult, getter]`, where `getter` is optional" );

fi;

if (Length( x ) = 2 or (Length( x ) = 3 and x[3] = fail)) and x[1] = target_op then

Error( "A derivation for ", target_op_name, " has itself as a precondition. This is not supported because we cannot compute a well-defined weight.\n" );

fi;

if Length( x ) = 2 then

Add( used_op_names_with_multiples_and_category_getters, [ NameFunction( x[1] ), x[2], fail ] );

elif Length( x ) = 3 then

if x <> fail and not (IsFunction( x[3] ) and NumberArgumentsFunction( x[3] ) = 1) then

Error( "the category getter must be a single-argument function" );

fi;

Add( used_op_names_with_multiples_and_category_getters, [ NameFunction( x[1] ), x[2], x[3] ] );

else

Error( "The list of preconditions must be a list of pairs or triples." );

fi;

od;

#= comment for Julia
collected_list := CAP_INTERNAL_FIND_APPEARANCE_OF_SYMBOL_IN_FUNCTION( func, operations_in_graph, loop_multiplier, CAP_INTERNAL_METHOD_RECORD_REPLACEMENTS, category_getters );

if Length( collected_list ) <> Length( used_op_names_with_multiples_and_category_getters ) or not ForAll( collected_list, c -> c in used_op_names_with_multiples_and_category_getters ) then

SortBy( used_op_names_with_multiples_and_category_getters, x -> x[1] );
SortBy( collected_list, x -> x[1] );

Print(
"WARNING: You have installed a derivation for ", target_op_name, " with preconditions ", used_op_names_with_multiples_and_category_getters,
" but the automated detection has detected the following list of preconditions: ", collected_list, ".\n",
"If this is a bug in the automated detection, please report it.\n"
);

fi;
# =#

derivation := CreateDerivation(
target_op_name,
description,
used_op_names_with_multiples_and_category_getters,
used_ops_with_multiples_and_category_getters,
func,
weight,
category_filter
category_filter,
loop_multiplier,
category_getters
);

if function_called_before_installation <> false then
Expand All @@ -283,13 +284,13 @@ InstallGlobalFunction( AddDerivation,
Add( graph!.derivations_by_target.(target_op_name), derivation );
derivation!.position_in_derivations_by_target := Length( graph!.derivations_by_target.(target_op_name) );

for x in used_op_names_with_multiples_and_category_getters do
for x in UsedOperationsWithMultiplesAndCategoryGetters( derivation ) do
# We add all operations, even those with category getters: In case the category getter
# returns the category itself, this allows to recursively trigger derivations correctly.
Add( graph!.derivations_by_used_ops.(x[1]), derivation );
od;

if IsEmpty( used_op_names_with_multiples_and_category_getters ) then
if IsEmpty( UsedOperationsWithMultiplesAndCategoryGetters( derivation ) ) then

Add( graph!.derivations_by_used_ops.none, derivation );

Expand Down Expand Up @@ -632,7 +633,7 @@ InstallGlobalFunction( AddFinalDerivationBundle, FunctionWithNamedArguments(
[ "FunctionCalledBeforeInstallation", false ],
],
function( CAP_NAMED_ARGUMENTS, description, can_compute, cannot_compute, additional_functions... )
local weight, category_filter, loop_multiplier, category_getters, function_called_before_installation, operations_in_graph, operations_to_install, union_of_collected_lists, derivations, used_op_names_with_multiples_and_category_getters, collected_list, dummy_derivation, final_derivation, i, current_additional_func, x;
local weight, category_filter, loop_multiplier, category_getters, function_called_before_installation, operations_to_install, union_of_collected_lists, derivations, derivation, used_op_names_with_multiples_and_category_getters, dummy_derivation, final_derivation, i, x, current_additional_func;

weight := CAP_NAMED_ARGUMENTS.Weight;
category_filter := CAP_NAMED_ARGUMENTS.CategoryFilter;
Expand Down Expand Up @@ -668,14 +669,29 @@ InstallGlobalFunction( AddFinalDerivationBundle, FunctionWithNamedArguments(

od;

for x in can_compute do

if Length( x ) < 2 or not IsFunction( x[1] ) or not IsInt( x[2] ) then

Error( "preconditions must be of the form `[op, mult, getter]`, where `getter` is optional" );

fi;

# check that preconditions do not appear in cannot_compute (which in particular includes all operations installed by this final derivation, as checked above)
if (Length( x ) = 2 or (Length( x ) = 3 and x[3] = fail)) and x[1] in cannot_compute then

Error( "A final derivation for ", NameFunction( additional_functions[1][1] ), " has precondition ", NameFunction( x[1] ), " which is also in its exclude list.\n" );

fi;

od;

if Length( additional_functions ) = 1 and StartsWith( NameFunction( additional_functions[1][1] ), "IsomorphismFrom" ) then

Print( "WARNING: You are installing a final derivation for ", NameFunction( additional_functions[1][1] ), " which does not include its inverse. You should probably use a bundled final derivation to also install its inverse.\n" );

fi;

operations_in_graph := Operations( CAP_INTERNAL_DERIVATION_GRAPH );

## Find symbols in functions
operations_to_install := [ ];

Expand All @@ -685,70 +701,20 @@ InstallGlobalFunction( AddFinalDerivationBundle, FunctionWithNamedArguments(

for current_additional_func in additional_functions do

used_op_names_with_multiples_and_category_getters := [ ];

for x in current_additional_func[2] do

if Length( x ) < 2 or not IsFunction( x[1] ) or not IsInt( x[2] ) then

Error( "preconditions must be of the form `[op, mult, getter]`, where `getter` is optional" );

fi;

if (Length( x ) = 2 or (Length( x ) = 3 and x[3] = fail)) and x[1] = current_additional_func[1] then

Error( "A final derivation for ", NameFunction( current_additional_func[1] ), " has itself as a precondition. This is not supported because we cannot compute a well-defined weight.\n" );

fi;

if Length( x ) = 2 then

Add( used_op_names_with_multiples_and_category_getters, [ NameFunction( x[1] ), x[2], fail ] );

elif Length( x ) = 3 then

if x <> fail and not (IsFunction( x[3] ) and NumberArgumentsFunction( x[3] ) = 1) then

Error( "the category getter must be a single-argument function" );

fi;

Add( used_op_names_with_multiples_and_category_getters, [ NameFunction( x[1] ), x[2], x[3] ] );

else

Error( "The list of preconditions must be a list of pairs or triples." );

fi;

od;

#= comment for Julia
# see AddDerivation
collected_list := CAP_INTERNAL_FIND_APPEARANCE_OF_SYMBOL_IN_FUNCTION( Last( current_additional_func ), operations_in_graph, loop_multiplier, CAP_INTERNAL_METHOD_RECORD_REPLACEMENTS, category_getters );

if Length( collected_list ) <> Length( used_op_names_with_multiples_and_category_getters ) or not ForAll( collected_list, c -> c in used_op_names_with_multiples_and_category_getters ) then

SortBy( used_op_names_with_multiples_and_category_getters, x -> x[1] );
SortBy( collected_list, x -> x[1] );

Print(
"WARNING: You have installed a final derivation for ", NameFunction( current_additional_func[1] ), " with preconditions ", used_op_names_with_multiples_and_category_getters,
" but the automated detection has detected the following list of preconditions: ", collected_list, ".\n",
"If this is a bug in the automated detection, please report it.\n"
);

fi;
# =#

Add( derivations, CreateDerivation(
derivation := CreateDerivation(
NameFunction( current_additional_func[1] ),
Concatenation( description, " (final derivation)" ),
used_op_names_with_multiples_and_category_getters,
current_additional_func[2],
current_additional_func[3],
weight,
category_filter
) );
category_filter,
loop_multiplier,
category_getters
);

Add( derivations, derivation );

used_op_names_with_multiples_and_category_getters := UsedOperationsWithMultiplesAndCategoryGetters( derivation );

# Operations may use operations from the same final derivation as long as the latter are installed before the former.
# In this case, the used operations are no preconditions and thus should not go into union_of_collected_lists.
Expand All @@ -760,48 +726,24 @@ InstallGlobalFunction( AddFinalDerivationBundle, FunctionWithNamedArguments(

od;

# see AddDerivation
used_op_names_with_multiples_and_category_getters := [ ];
# only used to check if we can install all the derivations in `derivations`
dummy_derivation := CreateDerivation(
"internal dummy function of a final derivation",
"dummy derivation",
can_compute,
ReturnTrue,
1,
category_filter,
loop_multiplier,
category_getters
);

for x in can_compute do

if Length( x ) < 2 or not IsFunction( x[1] ) or not IsInt( x[2] ) then

Error( "preconditions must be of the form `[op, mult, getter]`, where `getter` is optional" );

fi;

# check that preconditions do not appear in cannot_compute (which in particular includes all operations installed by this final derivation, as checked above)
if (Length( x ) = 2 or (Length( x ) = 3 and x[3] = fail)) and x[1] in cannot_compute then

Error( "A final derivation for ", TargetOperation( derivations[1] ), " has precondition ", x[1], " which is also in its exclude list.\n" );

fi;

if Length( x ) = 2 then

Add( used_op_names_with_multiples_and_category_getters, [ NameFunction( x[1] ), x[2], fail ] );

elif Length( x ) = 3 then

if x <> fail and not (IsFunction( x[3] ) and NumberArgumentsFunction( x[3] ) = 1) then

Error( "the category getter must be a single-argument function" );

fi;

Add( used_op_names_with_multiples_and_category_getters, [ NameFunction( x[1] ), x[2], x[3] ] );

else

Error( "The list of preconditions must be a list of pairs or triples." );

fi;

od;
used_op_names_with_multiples_and_category_getters := UsedOperationsWithMultiplesAndCategoryGetters( dummy_derivation );

if Length( union_of_collected_lists ) <> Length( used_op_names_with_multiples_and_category_getters ) or not ForAll( union_of_collected_lists, c -> c in used_op_names_with_multiples_and_category_getters ) then

used_op_names_with_multiples_and_category_getters := ShallowCopy( used_op_names_with_multiples_and_category_getters );

SortBy( used_op_names_with_multiples_and_category_getters, x -> x[1] );
SortBy( union_of_collected_lists, x -> x[1] );

Expand All @@ -813,16 +755,6 @@ InstallGlobalFunction( AddFinalDerivationBundle, FunctionWithNamedArguments(

fi;

# only used to check if we can install all the derivations in `derivations`
dummy_derivation := CreateDerivation(
"internal dummy function of a final derivation",
"dummy derivation",
used_op_names_with_multiples_and_category_getters,
ReturnTrue,
1,
category_filter
);

final_derivation := rec(
dummy_derivation := dummy_derivation,
cannot_compute := List( cannot_compute, x -> NameFunction( x ) ),
Expand Down
Loading

0 comments on commit 9b4dfff

Please sign in to comment.