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