diff --git a/CAP/gap/Derivations.gd b/CAP/gap/Derivations.gd
index e1a9b7984e..437893edb4 100644
--- a/CAP/gap/Derivations.gd
+++ b/CAP/gap/Derivations.gd
@@ -64,9 +64,8 @@ DeclareCategory( "IsDerivedMethod", IsAttributeStoringRep );
#! IsCapCategory. The output of category_filter 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.
diff --git a/CAP/gap/Derivations.gi b/CAP/gap/Derivations.gi
index 7913392082..f67c8d770e 100644
--- a/CAP/gap/Derivations.gi
+++ b/CAP/gap/Derivations.gi
@@ -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
@@ -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" );
@@ -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
@@ -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 );
@@ -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;
@@ -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 := [ ];
@@ -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.
@@ -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] );
@@ -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 ) ),
diff --git a/CAP/gap/ToolsForCategories.gi b/CAP/gap/ToolsForCategories.gi
index 91cafeb099..d9b5ddbc84 100644
--- a/CAP/gap/ToolsForCategories.gi
+++ b/CAP/gap/ToolsForCategories.gi
@@ -720,7 +720,7 @@ InstallGlobalFunction( CAP_INTERNAL_MERGE_PRECONDITIONS_LIST,
function( list1, list2 )
local pos, current_precondition;
- list2 := StructuralCopy( list2 );
+ list2 := List( list2, x -> ShallowCopy( x ) );
for current_precondition in list1 do