|
# SPDX-License-Identifier: GPL-2.0-or-later
# homalg: A homological algebra meta-package for computable Abelian categories
#
# Implementations
#
## Implementations for functors.
## <#GAPDoc Label="Functors:intro">
## Functors and their natural transformations form the heart of the &homalg; package. Usually, a functor is realized
## in computer algebra systems as a procedure which can be applied to a certain type of objects. In <Cite Key="BR"/>
## it was explained how to implement a functor of Abelian categories -- by itself -- as an object which can be
## further manipulated (composed, derived, ...).
## So in addition to the constructor <Ref Oper="CreateHomalgFunctor" Label="constructor for functors"/>
## which is used to create functors from scratch, &homalg; provides further easy-to-use constructors
## to create new functors out of existing ones:
## <List>
## <Item><Ref Oper="InsertObjectInMultiFunctor" Label="constructor for functors given a multi-functor and an object"/></Item>
## <Item><Ref Oper="RightSatelliteOfCofunctor" Label="constructor of the right satellite of a contravariant functor"/></Item>
## <Item><Ref Oper="LeftSatelliteOfFunctor" Label="constructor of the left satellite of a covariant functor"/></Item>
## <Item><Ref Oper="RightDerivedCofunctor" Label="constructor of the right derived functor of a contravariant functor"/></Item>
## <Item><Ref Oper="LeftDerivedFunctor" Label="constructor of the left derived functor of a covariant functor"/></Item>
## <Item><Ref Oper="ComposeFunctors" Label="constructor for functors given two functors"/></Item>
## </List>
## In &homalg; each functor is implemented as a &GAP4; object.
## <P/>
## So-called installers (&see; <Ref Oper="InstallFunctor"/> and <Ref Oper="InstallDeltaFunctor"/>)
## take such a functor object and create operations in order to apply the functor on objects, morphisms,
## complexes (of objects or again of complexes), and chain morphisms. The installer <Ref Oper="InstallDeltaFunctor"/>
## creates additional operations for <M>\delta</M>-functors in order to compute connecting homomorphisms,
## exact triangles, and associated long exact sequences by starting with a short exact sequence.
## <P/>
## In &homalg; special emphasis is laid on the action of functors on <E>morphisms</E>, as an essential part of the
## very definition of a functor. This is for no obvious reason often neglected in computer algebra systems.
## Starting from a functor where the action on morphisms is also defined, all the above constructors
## again create functors with actions both on objects and on morphisms (and hence on chain complexes and chain morphisms).
## <P/>
## It turned out that in a variety of situations a caching mechanism for functors is not only extremely
## useful (e.g. to avoid repeated expensive computations) but also an absolute necessity for the coherence of data.
## Functors in &homalg; are therefore endowed with a caching mechanism.
## <P/>
## If <M>R</M> is a &homalg; ring in which the component <M>R</M>!.<C>ByASmallerPresentation</C> is set to true
## <Br/><Br/>
## <C>R!.ByASmallerPresentation := true</C>;
## <Br/><Br/>
## any functor which returns an object over <M>R</M> will first apply
## <C>ByASmallerPresentation</C> to its result before returning it. <P/>
## One of the highlights in &homalg; is the computation of Grothendieck's spectral sequences connecting
## the composition of the derivations of two functors with the derived functor of their composite.
## <#/GAPDoc>
####################################
#
# representations:
#
####################################
# a new representation for the GAP-category IsHomalgFunctor:
## <#GAPDoc Label="IsHomalgFunctorRep">
## <ManSection>
## <Filt Type="Representation" Arg="E" Name="IsHomalgFunctorRep"/>
## <Returns><C>true</C> or <C>false</C></Returns>
## <Description>
## The &GAP; representation of &homalg; (multi-)functors. <P/>
## (It is a representation of the &GAP; category <Ref Filt="IsHomalgFunctor"/>.)
## </Description>
## </ManSection>
## <#/GAPDoc>
##
DeclareRepresentation( "IsHomalgFunctorRep",
IsHomalgFunctor,
[ ] );
# a new subrepresentation of the representation IsContainerForWeakPointersRep:
##
DeclareRepresentation( "IsContainerForWeakPointersOnComputedValuesOfFunctorRep",
IsContainerForWeakPointersOnObjectsRep,
[ "weak_pointers", "active", "deleted", "counter", "accessed", "cache_misses", "cache_hits" ] );
####################################
#
# families and types:
#
####################################
# a new family:
BindGlobal( "TheFamilyOfHomalgFunctors",
NewFamily( "TheFamilyOfHomalgFunctors" ) );
# a new type:
BindGlobal( "TheTypeHomalgFunctor",
NewType( TheFamilyOfHomalgFunctors,
IsHomalgFunctorRep ) );
# a new type:
BindGlobal( "TheTypeContainerForWeakPointersOnComputedValuesOfFunctor",
NewType( TheFamilyOfContainersForWeakPointers,
IsContainerForWeakPointersOnComputedValuesOfFunctorRep ) );
####################################
#
# global values:
#
####################################
HOMALG.FunctorOn :=
[ IsStructureObjectOrFinitelyPresentedObjectRep,
IsStaticMorphismOfFinitelyGeneratedObjectsRep,
[ IsComplexOfFinitelyPresentedObjectsRep, IsCocomplexOfFinitelyPresentedObjectsRep ],
[ IsChainMorphismOfFinitelyPresentedObjectsRep, IsCochainMorphismOfFinitelyPresentedObjectsRep ] ];
####################################
#
# methods for operations:
#
####################################
##
InstallMethod( NaturalGeneralizedEmbedding,
"for homalg objects being values of functors on objects",
[ IsStaticFinitelyPresentedObjectRep ],
function( FM )
if not IsBound(FM!.NaturalGeneralizedEmbedding) then
Error( "the object does not have a component \"NaturalGeneralizedEmbedding\"; either the object is not the result of a functor or the functor is not properly implemented (cf. arXiv:math/0701146)\n" );
fi;
return FM!.NaturalGeneralizedEmbedding;
end );
##
InstallMethod( NameOfFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
if not IsBound( Functor!.name ) then
Error( "the provided functor is nameless\n" );
fi;
return Functor!.name;
end );
##
InstallMethod( OperationOfFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
local functor_operation;
if not IsBound( Functor!.operation ) then
Error( "unable to find the functor component \"operation\"\n" );
fi;
functor_operation := ValueGlobal( Functor!.operation );
## for this to work you need to declare one instance of the functor,
## although all methods will be installed using InstallOtherMethod!
if not IsOperation( functor_operation ) and
not IsFunction( functor_operation ) then
Error( "the functor ", NameOfFunctor( Functor ), " neither points to an operation nor to a function\n" );
fi;
return functor_operation;
end );
##
InstallMethod( CategoryOfFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
if not IsBound( Functor!.category ) then
Error( "unable to find the functor component \"category\"\n" );
fi;
return Functor!.category;
end );
##
InstallMethod( DescriptionOfCategory,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
local category;
category := CategoryOfFunctor( Functor );
if not IsBound( category!.description ) then
Error( "unable to find the category component \"description\"\n" );
fi;
return category!.description;
end );
##
InstallMethod( ShortDescriptionOfCategory,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
local category;
category := CategoryOfFunctor( Functor );
if not IsBound( category!.short_description ) then
Error( "unable to find the category component \"short_description\"\n" );
fi;
return category!.short_description;
end );
##
InstallMethod( IsSpecialFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
if IsBound( Functor!.special ) and Functor!.special = true then
return true;
fi;
return false;
end );
##
InstallMethod( MultiplicityOfFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
if IsBound( Functor!.number_of_arguments ) then
return Functor!.number_of_arguments;
fi;
return 1;
end );
##
InstallMethod( IsCovariantFunctor,
"for homalg functors",
[ IsHomalgFunctorRep, IsInt ],
function( Functor, pos )
if pos < 1 then
Error( "the second argument must be a positive integer\n" );
fi;
if IsBound( Functor!.(pos) ) then
if Functor!.( pos )[1][1] = "covariant" then
return true;
elif Functor!.( pos )[1][1] = "contravariant" then
return false;
fi;
fi;
return fail;
end );
##
InstallMethod( IsCovariantFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
return IsCovariantFunctor( Functor, 1 );
end );
##
InstallMethod( IsDistinguishedArgumentOfFunctor,
"for homalg functors",
[ IsHomalgFunctorRep, IsPosInt ],
function( Functor, pos )
local l;
if IsBound( Functor!.(String( pos )) ) and IsBound( Functor!.(String( pos ))[1] ) then
l := Length( Functor!.(String( pos ))[1] );
if l > 1 and Functor!.(String( pos ))[1][l] = "distinguished" then
return true;
fi;
fi;
return false;
end );
##
InstallMethod( IsDistinguishedFirstArgumentOfFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
return IsDistinguishedArgumentOfFunctor( Functor, 1 );
end );
##
InstallMethod( IsAdditiveFunctor,
"for homalg functors",
[ IsHomalgFunctorRep, IsPosInt ],
function( Functor, pos )
local prop;
if IsBound( Functor!.(pos) ) and Length( Functor!.( pos )[1] ) > 1 then
prop := Functor!.( pos )[1][2];
if prop in [ "additive", "left exact", "right exact", "exact", "right adjoint", "left adjoint" ] then
return true;
fi;
fi;
return fail;
end );
##
InstallMethod( IsAdditiveFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
return IsAdditiveFunctor( Functor, 1 );
end );
##
InstallMethod( IsLeftExactFunctor,
"for homalg functors",
[ IsHomalgFunctorRep, IsPosInt ],
function( Functor, pos )
local prop;
if IsBound( Functor!.(pos) ) and Length( Functor!.( pos )[1] ) > 1 then
prop := Functor!.( pos )[1][2];
if prop in [ "left exact", "exact", "right adjoint" ] then
return true;
fi;
fi;
return fail;
end );
##
InstallMethod( IsLeftExactFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
return IsLeftExactFunctor( Functor, 1 );
end );
##
InstallMethod( IsRightExactFunctor,
"for homalg functors",
[ IsHomalgFunctorRep, IsPosInt ],
function( Functor, pos )
local prop;
if IsBound( Functor!.(pos) ) and Length( Functor!.( pos )[1] ) > 1 then
prop := Functor!.( pos )[1][2];
if prop in [ "right exact", "exact", "left adjoint" ] then
return true;
fi;
fi;
return fail;
end );
##
InstallMethod( IsRightExactFunctor,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
return IsRightExactFunctor( Functor, 1 );
end );
##
InstallMethod( IsIdenticalObjForFunctors,
"for two objects",
[ IsObject, IsObject ],
function( o1, o2 )
local l1, l2;
if IsHomalgObjectOrMorphism( o1 ) then
return IsIdenticalObj( o1, o2 );
elif IsString( o1 ) and IsString( o2 ) then
return o1 = o2;
elif IsList( o1 ) and IsList( o2 ) then
l1 := Length( o1 );
l2 := Length( o2 );
return l1 = l2 and
ForAll( [ 1 .. l1 ], i -> IsIdenticalObjForFunctors( o1[i], o2[i] ) );
fi;
return IsIdenticalObj( o1, o2 );
end );
##
InstallMethod( GetContainerForWeakPointersOfFunctorCachedValue,
"for homalg functors",
[ IsHomalgFunctorRep, IsHomalgCategory, IsString ],
function( Functor, category, container )
local name, containers;
## first we ask the functor
if not IsBound( Functor!.( container ) ) then
return fail;
elif Functor!.( container ) = false and
IsBound( category!.do_not_cache_values_of_some_functors ) and
category!.do_not_cache_values_of_some_functors = true then
return fail;
fi;
name := Concatenation( NameOfFunctor( Functor ), "_", container );
containers := category!.containers;
if IsBound( containers!.( name ) ) then
container := containers!.( name );
if IsContainerForWeakPointersRep( container ) then
return container;
fi;
return fail;
elif IsBound( category!.do_not_cache_values_of_functors ) then
return fail;
fi;
container := ContainerForWeakPointers( TheTypeContainerForWeakPointersOnComputedValuesOfFunctor );
containers!.( name ) := container;
return container;
end );
##
InstallMethod( SetFunctorObjCachedValue,
"for homalg functors",
[ IsHomalgFunctorRep, IsList, IsObject ],
function( Functor, args_of_functor, obj )
local arguments_of_functor, p, l, context_of_arguments, arg_all,
genesis, a, category, container;
## convert subobjects into objects
arguments_of_functor :=
List( args_of_functor,
function( a )
if IsStaticFinitelyPresentedSubobjectRep( a ) then
return UnderlyingObject( a );
else
return a;
fi;
end );
if IsBound( Functor!.0 ) then
p := 1;
else
p := 0;
fi;
l := Length( arguments_of_functor );
context_of_arguments := List( arguments_of_functor{[ 1 + p .. l ]}, PositionOfTheDefaultPresentation );
arg_all := rec( );
arg_all.("arguments_of_functor") := arguments_of_functor;
arg_all.("context_of_arguments") := context_of_arguments;
arg_all.("Functor") := Functor;
arg_all.("PositionOfTheDefaultPresentationOfTheOutput")
:= PositionOfTheDefaultPresentation( obj );
arg_all := [ arg_all, obj ];
if not HasGenesis( obj ) then
SetGenesis( obj, [ arg_all ] );
else
genesis := Genesis( obj );
if IsList( genesis ) then
Add( genesis, arg_all );
fi;
fi;
for a in args_of_functor do
category := HomalgCategory( a );
if category <> fail then
break;
fi;
od;
container := GetContainerForWeakPointersOfFunctorCachedValue(
Functor,
category,
"ContainerForWeakPointersOnComputedBasicObjects" );
if container <> fail then
_AddElmWPObj_ForHomalg( container, arg_all );
fi;
return obj;
end );
##
InstallMethod( GetFunctorObjCachedValue,
"for homalg functors",
[ IsHomalgFunctorRep, IsList ],
function( Functor, args_of_functor )
local a, category, container, weak_pointers, lp, active, l_active,
arguments_of_functor, functor_name, p, l, context_of_arguments,
cache_hit, i, arg_old_obj, context, arg_old, obj;
for a in args_of_functor do
category := HomalgCategory( a );
if category <> fail then
break;
fi;
od;
container := GetContainerForWeakPointersOfFunctorCachedValue(
Functor,
category,
"ContainerForWeakPointersOnComputedBasicObjects" );
if container = fail then
return fail;
fi;
weak_pointers := container!.weak_pointers;
lp := LengthWPObj( weak_pointers );
active := Filtered( container!.active, i -> i <= lp );
l_active := Length( active );
## convert subobjects into objects
arguments_of_functor :=
List( args_of_functor,
function( a )
if IsStaticFinitelyPresentedSubobjectRep( a ) then
return UnderlyingObject( a );
else
return a;
fi;
end );
#=====# begin of the core procedure #=====#
functor_name := NameOfFunctor( Functor );
if IsBound( Functor!.0 ) then
p := 1;
else
p := 0;
fi;
l := Length( arguments_of_functor );
context_of_arguments := List( arguments_of_functor{[ 1 + p .. l ]}, PositionOfTheDefaultPresentation );
cache_hit := false;
i := 1;
while i <= l_active do
arg_old_obj := ElmWPObj( weak_pointers, active[i] );
if arg_old_obj <> fail then
arg_old := arg_old_obj[1];
context := arg_old.("context_of_arguments");
arg_old := arg_old.("arguments_of_functor");
obj := arg_old_obj[2];
if l = Length( arg_old ) then
if ForAny( arguments_of_functor, IsHomalgStaticObject ) then
if ForAll( [ 1 .. l ], j -> IsIdenticalObjForFunctors( arg_old[j], arguments_of_functor[j] ) ) then
if ForAll( [ 1 .. l - p ], j -> context[j] = context_of_arguments[j] ) then
cache_hit := true;
break;
elif ForAll( [ 1 .. l - p ],
function( j )
if context[j] = context_of_arguments[j] then
return true;
else
return IsIdenticalObj(
PartOfPresentationRelevantForOutputOfFunctors( arg_old[j+p], context[j] ),
PartOfPresentationRelevantForOutputOfFunctors( arguments_of_functor[j+p], context_of_arguments[j] ) );
fi;
end ) then
cache_hit := true;
break;
elif ForAll( [ 1 .. l - p ],
function( j )
return ComparePresentationsForOutputOfFunctors( arguments_of_functor[j+p], context_of_arguments[j], context[j] );
end ) then
cache_hit := true;
break;
elif IsBound( obj!.IgnoreContextOfArgumentsOfFunctor ) and
obj!.IgnoreContextOfArgumentsOfFunctor = true then
cache_hit := true;
break;
elif IsBound( Functor!.CompareArgumentsForFunctorObj ) and ## no static objects
Functor!.CompareArgumentsForFunctorObj( arg_old, arguments_of_functor ) then
cache_hit := true;
break;
##elif IsBound( Functor!.OnMorphisms ) then
## Error( "TODO: merge the new output with the old one\n" );
fi;
fi;
elif not ( IsBound( Functor!.DontCompareEquality ) and Functor!.DontCompareEquality ) and
ForAll( [ 1 .. l ], j -> arg_old[j] = arguments_of_functor[j] ) then ## no static objects
## this "elif" is extremely important:
## To apply a certain functor (e.g. derived ones) to an object
## we might need to apply another functor to a morphism A. This
## morphisms could be the outcome of a third functor applied to another
## morphism B, and although there is a caching for functors applied
## to morphisms, B often becomes obsolete since it was only used in an
## intermediat step and gets deleted after a while
## (e.g. CompleteImageSquare( alpha1, Functor(morphism), beta1 )).
## Hence B has to be recomputed to get B' and A has to be recomputed
## using B' to get A'. Now A=A' but not identical!
cache_hit := true;
break;
elif IsBound( Functor!.CompareArgumentsForFunctorObj ) and ## no static objects
Functor!.CompareArgumentsForFunctorObj( arg_old, arguments_of_functor ) then
cache_hit := true;
break;
fi;
fi;
i := i + 1;
else ## active[i] is no longer active
Remove( active, i );
l_active := l_active - 1;
fi;
od;
container!.active := active;
container!.deleted := Difference( [ 1 .. lp ], active );
container!.accessed := container!.accessed + 1;
container!.cache_misses := container!.cache_misses + i - 1;
if cache_hit then
container!.cache_hits := container!.cache_hits + 1;
return obj;
fi;
return fail;
end );
##
InstallMethod( FunctorObj,
"for homalg morphisms",
[ IsHomalgFunctorRep, IsList ],
function( Functor, args_of_functor )
local arguments_of_functor, obj, genesis, Functor_orig, arg_pos,
Functor_arg, Functor_post, Functor_pre, post_arg_pos,
functor_orig_operation, m_orig, arg_orig,
functor_pre_operation, m_pre, functor_post_operation, m_post,
arg_pre, arg_post, R;
## convert subobjects into objects
arguments_of_functor :=
List( args_of_functor,
function( a )
if IsStaticFinitelyPresentedSubobjectRep( a ) then
return UnderlyingObject( a );
else
return a;
fi;
end );
obj := GetFunctorObjCachedValue( Functor, args_of_functor );
if obj <> fail then
return obj;
fi;
#=====# begin of the core procedure #=====#
if HasGenesis( Functor ) then
genesis := Genesis( Functor );
if genesis[1] = "InsertObjectInMultiFunctor" then
Functor_orig := genesis[2];
arg_pos := genesis[3];
Functor_arg := genesis[4];
elif genesis[1] = "ComposeFunctors" then
Functor_post := genesis[2][1];
Functor_pre := genesis[2][2];
post_arg_pos := genesis[3];
fi;
fi;
if IsBound( Functor_orig ) then
## the functor is specialized: Functor := Functor_orig( ..., Functor_arg, ... )
functor_orig_operation := OperationOfFunctor( Functor_orig );
m_orig := MultiplicityOfFunctor( Functor_orig );
if IsBound( Functor_orig!.0 ) then
arg_orig := arguments_of_functor{[ 1 .. arg_pos ]};
else
arg_orig := arguments_of_functor{[ 1 .. arg_pos - 1 ]};
fi;
Add( arg_orig, Functor_arg );
Append( arg_orig, arguments_of_functor{[ arg_pos + 1 .. m_orig ]} );
obj := CallFuncList( functor_orig_operation, arg_orig );
elif IsBound( Functor_post ) then
## the functor is composed: Functor := Functor_post @ Functor_pre
functor_pre_operation := OperationOfFunctor( Functor_pre );
functor_post_operation := OperationOfFunctor( Functor_post );
m_pre := MultiplicityOfFunctor( Functor_pre );
m_post := MultiplicityOfFunctor( Functor_post );
arg_pre := arguments_of_functor{[ post_arg_pos .. post_arg_pos + m_pre - 1 ]};
arg_post := Concatenation(
arguments_of_functor{[ 1 .. post_arg_pos - 1 ]},
[ CallFuncList( functor_pre_operation, arg_pre ) ],
arguments_of_functor{[ post_arg_pos + m_pre .. m_post + m_pre - 1 ]}
);
obj := CallFuncList( functor_post_operation, arg_post );
else
obj := CallFuncList( Functor!.OnObjects, arguments_of_functor );
fi;
if IsHomalgStaticObject( obj ) then
R := StructureObject( obj );
if IsBound( R!.ByASmallerPresentation ) then
ByASmallerPresentation( obj );
fi;
fi;
#=====# end of the core procedure #=====#
SetFunctorObjCachedValue( Functor, arguments_of_functor, obj );
return obj;
end );
##
InstallMethod( FunctorMor,
"for homalg morphisms",
[ IsHomalgFunctorRep, IsMorphismOfFinitelyGeneratedObjectsRep, IsList ],
function( Functor, phi, fixed_arguments_of_multi_functor )
local container, weak_pointers, lp, active, l_active, functor_name,
number_of_arguments, pos0, arg_positions, S, T, pos,
arg_before_pos, arg_behind_pos, arg_all, cache_hit, i, l,
arg_old_mor, arg_old, arg_source, arg_target, functor_operation,
F_source, F_target, genesis, Functor_orig, arg_pos, Functor_arg,
Functor_post, Functor_pre, post_arg_pos,
functor_orig_operation, m_orig, arg_orig,
functor_pre_operation, m_pre, functor_post_operation, m_post,
arg_pre, arg_post, emb_source, emb_target, arg_phi, hull_phi, mor;
if not fixed_arguments_of_multi_functor = [ ] and
not ( ForAll( fixed_arguments_of_multi_functor, a -> IsList( a ) and Length( a ) = 2 and IsPosInt( a[1] ) ) ) then
Error( "the last argument has a wrong syntax\n" );
fi;
container := GetContainerForWeakPointersOfFunctorCachedValue(
Functor,
HomalgCategory( phi ),
"ContainerForWeakPointersOnComputedBasicMorphisms" );
if container <> fail then
weak_pointers := container!.weak_pointers;
lp := LengthWPObj( weak_pointers );
active := Filtered( container!.active, i -> i <= lp );
l_active := Length( active );
fi;
#=====# begin of the core procedure #=====#
functor_name := NameOfFunctor( Functor );
number_of_arguments := MultiplicityOfFunctor( Functor );
if IsBound( Functor!.0 ) and IsList( Functor!.0 ) then
number_of_arguments := number_of_arguments + 1;
pos0 := 1;
else
pos0 := 0;
fi;
arg_positions := List( fixed_arguments_of_multi_functor, a -> a[1] );
if Length( arg_positions ) <> number_of_arguments - 1 then
Error( "the number of fixed arguments provided for the functor must be one less than the total number\n" );
elif not IsDuplicateFree( arg_positions ) then
Error( "the provided list of positions is not duplicate free: ", arg_positions, "\n" );
elif arg_positions <> [ ] and Maximum( arg_positions ) > number_of_arguments then
Error( "the list of positions must be a subset of [ 1 .. ", number_of_arguments, " ], but received: :", arg_positions, "\n" );
fi;
S := Source( phi );
T := Range( phi );
pos := Filtered( [ 1 .. number_of_arguments ], a -> not a in arg_positions )[1];
arg_positions := fixed_arguments_of_multi_functor;
Sort( arg_positions, function( v, w ) return v[1] < w[1]; end );
arg_before_pos := List( arg_positions{[ 1 .. pos - 1 ]}, a -> a[2] );
arg_behind_pos := List( arg_positions{[ pos .. number_of_arguments - 1 ]}, a -> a[2] );
arg_all := Concatenation( arg_before_pos, [ phi ], arg_behind_pos );
if container <> fail then
cache_hit := false;
i := 1;
while i <= l_active do
arg_old_mor := ElmWPObj( weak_pointers, active[i] );
if arg_old_mor <> fail then
arg_old := arg_old_mor[1];
l := Length( arg_old );
if l = Length( arg_all ) then
if ForAll( [ 1 .. l ], j -> IsIdenticalObjForFunctors( arg_old[j], arg_all[j] ) ) then
cache_hit := true;
break;
fi;
fi;
i := i + 1;
else ## active[i] is no longer active
Remove( active, i );
l_active := l_active - 1;
fi;
od;
container!.active := active;
container!.deleted := Difference( [ 1 .. lp ], active );
container!.accessed := container!.accessed + 1;
container!.cache_misses := container!.cache_misses + i - 1;
if cache_hit then
container!.cache_hits := container!.cache_hits + 1;
return arg_old_mor[2];
fi;
fi;
pos := pos - pos0;
if IsCovariantFunctor( Functor, pos ) = true then
arg_source := Concatenation( arg_before_pos, [ S ], arg_behind_pos );
arg_target := Concatenation( arg_before_pos, [ T ], arg_behind_pos );
elif IsCovariantFunctor( Functor, pos ) = false then ## not fail
arg_source := Concatenation( arg_before_pos, [ T ], arg_behind_pos );
arg_target := Concatenation( arg_before_pos, [ S ], arg_behind_pos );
else
Error( "the functor ", functor_name, " must be either co- or contravriant in its argument number ", pos, "\n" );
fi;
functor_operation := OperationOfFunctor( Functor );
F_source := CallFuncList( functor_operation, arg_source );
F_target := CallFuncList( functor_operation, arg_target );
if ( HasIsZero( F_source ) and IsZero( F_source ) ) or ( HasIsZero( F_target ) and IsZero( F_target ) ) or ( HasIsZero( phi ) and IsZero( phi ) ) then
mor := TheZeroMorphism( F_source, F_target );
elif HasIsOne( phi ) and IsOne( phi ) then
mor := TheIdentityMorphism( F_source );
else
if HasGenesis( Functor ) then
genesis := Genesis( Functor );
if genesis[1] = "InsertObjectInMultiFunctor" then
Functor_orig := genesis[2];
arg_pos := genesis[3];
Functor_arg := genesis[4];
elif genesis[1] = "ComposeFunctors" then
Functor_post := genesis[2][1];
Functor_pre := genesis[2][2];
post_arg_pos := genesis[3];
fi;
fi;
if IsBound( Functor_orig ) then
## the functor is specialized: Functor := Functor_orig( ..., Functor_arg, ... )
functor_orig_operation := OperationOfFunctor( Functor_orig );
m_orig := MultiplicityOfFunctor( Functor_orig );
if IsBound( Functor_orig!.0 ) then
arg_orig := arg_all{[ 1 .. arg_pos ]};
else
arg_orig := arg_all{[ 1 .. arg_pos - 1 ]};
fi;
Add( arg_orig, Functor_arg );
Append( arg_orig, arg_all{[ arg_pos + 1 .. m_orig ]} );
mor := CallFuncList( functor_orig_operation, arg_orig );
elif IsBound( Functor_post ) then
## the functor is composed: Functor := Functor_post @ Functor_pre
functor_pre_operation := OperationOfFunctor( Functor_pre );
functor_post_operation := OperationOfFunctor( Functor_post );
m_pre := MultiplicityOfFunctor( Functor_pre );
m_post := MultiplicityOfFunctor( Functor_post );
arg_pre := arg_all{[ post_arg_pos .. post_arg_pos + m_pre - 1 ]};
arg_post := Concatenation(
arg_all{[ 1 .. post_arg_pos - 1 ]},
[ CallFuncList( functor_pre_operation, arg_pre ) ],
arg_all{[ post_arg_pos + m_pre .. m_post + m_pre - 1 ]}
);
mor := CallFuncList( functor_post_operation, arg_post );
elif IsBound( Functor!.IsIdentityOnObjects ) and Functor!.IsIdentityOnObjects then
if IsBound( Functor!.OnMorphisms ) then
arg_phi := Concatenation( arg_before_pos, [ phi ], arg_behind_pos );
mor := CallFuncList( Functor!.OnMorphisms, arg_phi );
if IsBound( Functor!.MorphismConstructor ) then
mor := Functor!.MorphismConstructor( mor, F_source, F_target );
## otherwise the result mor cannot automatically be marked IsMorphism
SetIsMorphism( mor, true );
fi;
else
mor := phi;
fi;
elif IsBound( Functor!.OnMorphisms ) then
mor := Functor!.OnMorphisms( F_source, F_target, arg_before_pos, phi, arg_behind_pos );
else ## old style, will be eliminated soon
emb_source := NaturalGeneralizedEmbedding( F_source );
emb_target := NaturalGeneralizedEmbedding( F_target );
if IsBound( Functor!.OnMorphismsHull ) then
arg_phi := Concatenation( arg_before_pos, [ phi ], arg_behind_pos );
hull_phi := CallFuncList( Functor!.OnMorphismsHull, arg_phi );
if IsBound( Functor!.MorphismConstructor ) then
hull_phi := Functor!.MorphismConstructor( hull_phi, Range( emb_source ), Range( emb_target ) );
## otherwise the result mor cannot automatically be marked IsMorphism
SetIsMorphism( hull_phi, true );
fi;
else
hull_phi := phi;
fi;
mor := CompleteImageSquare( emb_source, hull_phi, emb_target );
## CAUTION: this is experimental!!!
if HasIsGeneralizedMonomorphism( emb_source ) and IsGeneralizedMonomorphism( emb_source ) and
HasIsGeneralizedMonomorphism( emb_target ) and IsGeneralizedMonomorphism( emb_target ) and
HasIsMorphism( phi ) and IsMorphism( phi ) then
## check assertion
Assert( 3, IsMorphism( mor ) );
SetIsMorphism( mor, true );
fi;
fi;
fi;
SetPropertiesOfFunctorMor( Functor, phi, mor, pos, arg_before_pos, arg_behind_pos );
#=====# end of the core procedure #=====#
arg_all := [ arg_all, mor ];
if not HasGenesis( mor ) then
SetGenesis( mor, [ arg_all ] );
else
Add( Genesis( mor ), arg_all );
fi;
if container <> fail then
_AddElmWPObj_ForHomalg( container, arg_all );
fi;
return mor;
end );
##
InstallMethod( FunctorMor,
"for homalg morphisms",
[ IsHomalgFunctorRep, IsStaticMorphismOfFinitelyGeneratedObjectsRep ],
function( Functor, phi )
return FunctorMor( Functor, phi, [ ] );
end );
InstallMethod( SetPropertiesOfFunctorMor,
"for homalg morphisms",
[ IsHomalgFunctorRep, IsHomalgMorphism, IsHomalgMorphism, IsInt, IsList, IsList ],
function( Functor, phi, mor, pos, arg_before_pos, arg_behind_pos )
local alpha;
if HasIsIsomorphism( phi ) and IsIsomorphism( phi ) then
Assert( 3, IsIsomorphism( mor ) );
SetIsIsomorphism( mor, true );
fi;
if HasIsMorphism( phi ) and IsMorphism( phi ) then
Assert( 3, IsMorphism( mor ) );
SetIsMorphism( mor, true );
fi;
if IsLeftExactFunctor( Functor, pos ) = true then
if IsCovariantFunctor( Functor, pos ) = true then
if HasIsMonomorphism( phi ) and IsMonomorphism( phi ) then
Assert( 3, IsMonomorphism( mor ) );
SetIsMonomorphism( mor, true );
fi;
if HasKernelEmb( phi ) then
alpha := GetFunctorObjCachedValue( Functor, Concatenation( arg_before_pos, [ KernelEmb( phi ) ], arg_behind_pos ) );
if alpha <> fail then
SetKernelEmb( mor, alpha );
fi;
fi;
fi;
if IsCovariantFunctor( Functor, pos ) = false then
if HasIsEpimorphism( phi ) and IsEpimorphism( phi ) then
Assert( 3, IsMonomorphism( mor ) );
SetIsMonomorphism( mor, true );
fi;
if HasCokernelEpi( phi ) then
alpha := GetFunctorObjCachedValue( Functor, Concatenation( arg_before_pos, [ CokernelEpi( phi ) ], arg_behind_pos ) );
if alpha <> fail then
Error( "juhu 2" );
SetKernelEmb( mor, alpha );
fi;
fi;
fi;
fi;
if IsRightExactFunctor( Functor, pos ) = true then
if IsCovariantFunctor( Functor, pos ) = true then
if HasIsEpimorphism( phi ) and IsEpimorphism( phi ) then
Assert( 3, IsEpimorphism( mor ) );
SetIsEpimorphism( mor, true );
fi;
if HasCokernelEpi( phi ) then
alpha := GetFunctorObjCachedValue( Functor, Concatenation( arg_before_pos, [ CokernelEpi( phi ) ], arg_behind_pos ) );
if alpha <> fail then
SetCokernelEpi( mor, alpha );
fi;
fi;
fi;
if IsCovariantFunctor( Functor, pos ) = false then
if HasIsMonomorphism( phi ) and IsMonomorphism( phi ) then
Assert( 3, IsEpimorphism( mor ) );
SetIsEpimorphism( mor, true );
fi;
if HasKernelEmb( phi ) then
alpha := GetFunctorObjCachedValue( Functor, Concatenation( arg_before_pos, [ KernelEmb( phi ) ], arg_behind_pos ) );
if alpha <> fail then
SetCokernelEpi( mor, alpha );
fi;
fi;
fi;
fi;
UpdateObjectsByMorphism( mor );
return mor;
end );
##
InstallMethod( InstallFunctorOnObjects,
"for homalg functors",
[ IsHomalgFunctorRep ],
function( Functor )
local genesis, der_arg, functor_operation, number_of_arguments,
natural_transformation,
natural_transformation1, natural_transformation2,
natural_transformation3, natural_transformation4,
filter_obj, filter0, filter1_obj, filter2_obj, filter3_obj;
if HasGenesis( Functor ) then
genesis := Genesis( Functor );
if IsBound( genesis[3] ) then
der_arg := genesis[3];
fi;
fi;
functor_operation := OperationOfFunctor( Functor );
number_of_arguments := MultiplicityOfFunctor( Functor );
if number_of_arguments = 1 then
if not IsBound( Functor!.1[2] ) then
Functor!.1[2] := HOMALG.FunctorOn;
fi;
if not IsBound( Functor!.1[2][1] ) then
return fail;
fi;
filter_obj := Functor!.1[2][1];
if IsFilter( filter_obj ) then
if IsBound( Functor!.0 ) and IsList( Functor!.0 ) then
if Length( Functor!.0 ) = 1 then
filter0 := Functor!.0[1];
else
filter0 := IsList;
fi;
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter0, filter_obj ],
function( c, o )
local obj;
if IsStructureObject( o ) then
## I personally prefer left objects:
obj := AsLeftObject( o );
else
obj := o;
fi;
return FunctorObj( Functor, [ c, obj ] );
end );
else
if IsBound( Functor!.natural_transformation ) then
natural_transformation := ValueGlobal( Functor!.natural_transformation );
InstallOtherMethod( natural_transformation,
"for homalg objects",
[ filter_obj ],
function( o )
functor_operation( o ); ## this should set the attribute named "natural_transformation"
if not Tester( natural_transformation )( o ) then
Error( "the functor operation ", functor_operation,
" did not succeed to set the attribute ",
natural_transformation, "\n" );
fi;
return natural_transformation( o );
end );
fi;
if IsBound( Functor!.natural_transformation1 ) then
natural_transformation1 := ValueGlobal( Functor!.natural_transformation1 );
InstallOtherMethod( natural_transformation1,
"for homalg objects",
[ filter_obj ],
function( o )
functor_operation( o ); ## this should set the attribute named "natural_transformation"
if not Tester( natural_transformation1 )( o ) then
Error( "the functor operation ", functor_operation,
" did not succeed to set the attribute ",
natural_transformation1, "\n" );
fi;
return natural_transformation1( o );
end );
fi;
if IsBound( Functor!.natural_transformation2 ) then
natural_transformation2 := ValueGlobal( Functor!.natural_transformation2 );
InstallOtherMethod( natural_transformation2,
"for homalg objects",
[ filter_obj ],
function( o )
functor_operation( o ); ## this should set the attribute named "natural_transformation"
if not Tester( natural_transformation2 )( o ) then
Error( "the functor operation ", functor_operation,
" did not succeed to set the attribute ",
natural_transformation2, "\n" );
fi;
return natural_transformation2( o );
end );
fi;
if IsBound( Functor!.natural_transformation3 ) then
natural_transformation3 := ValueGlobal( Functor!.natural_transformation3 );
InstallOtherMethod( natural_transformation3,
"for homalg objects",
[ filter_obj ],
function( o )
functor_operation( o ); ## this should set the attribute named "natural_transformation"
if not Tester( natural_transformation3 )( o ) then
Error( "the functor operation ", functor_operation,
" did not succeed to set the attribute ",
natural_transformation3, "\n" );
fi;
return natural_transformation3( o );
end );
fi;
if IsBound( Functor!.natural_transformation4 ) then
natural_transformation4 := ValueGlobal( Functor!.natural_transformation4 );
InstallOtherMethod( natural_transformation4,
"for homalg objects",
[ filter_obj ],
function( o )
functor_operation( o ); ## this should set the attribute named "natural_transformation"
if not Tester( natural_transformation4 )( o ) then
Error( "the functor operation ", functor_operation,
" did not succeed to set the attribute ",
natural_transformation4, "\n" );
fi;
return natural_transformation4( o );
end );
fi;
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter_obj ],
function( o )
local obj;
if IsStructureObject( o ) then
## I personally prefer left objects:
obj := AsLeftObject( o );
else
obj := o;
fi;
return FunctorObj( Functor, [ obj ] );
end );
fi;
else
Error( "wrong syntax: ", filter_obj, "\n" );
fi;
elif number_of_arguments = 2 then
if not IsBound( Functor!.1[2] ) then
Functor!.1[2] := HOMALG.FunctorOn;
fi;
if not IsBound( Functor!.2[2] ) then
Functor!.2[2] := HOMALG.FunctorOn;
fi;
if not IsBound( Functor!.1[2][1] ) or not IsBound( Functor!.2[2][1] ) then
return fail;
fi;
filter1_obj := Functor!.1[2][1];
filter2_obj := Functor!.2[2][1];
if IsFilter( filter1_obj ) and IsFilter( filter2_obj ) then
if IsBound( Functor!.0 ) and IsList( Functor!.0 ) then
if Length( Functor!.0 ) = 1 then
filter0 := Functor!.0[1];
else
filter0 := IsList;
fi;
if IsDistinguishedFirstArgumentOfFunctor( Functor ) then
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter0, filter1_obj ],
function( c, o )
local R;
if IsStructureObject( o ) then
R := o;
else
R := StructureObject( o );
fi;
return functor_operation( c, o, R );
end );
InstallOtherMethod( functor_operation,
"for homalg objects",
[ IsInt, filter1_obj, IsString ],
function( c, o, s )
local R;
if IsStructureObject( o ) then
R := o;
else
R := StructureObject( o );
fi;
return functor_operation( c, o, R, s );
end );
if IsBound( der_arg ) and der_arg = 1 then
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter1_obj ],
function( o )
local R;
if IsStructureObject( o ) then
R := o;
else
R := StructureObject( o );
fi;
return functor_operation( o, R );
end );
fi;
fi;
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter0, filter1_obj, filter2_obj ],
function( c, o1, o2 )
local obj1, obj2;
if IsHomalgStaticObject( o1 ) and IsHomalgStaticObject( o2 ) then ## the most probable case
obj1 := o1;
obj2 := o2;
elif IsHomalgStaticObject( o1 ) and IsStructureObject( o2 ) then
obj1 := o1;
if IsHomalgLeftObjectOrMorphismOfLeftObjects( o1 ) then
obj2 := AsLeftObject( o2 );
else
obj2 := AsRightObject( o2 );
fi;
elif IsStructureObject( o1 ) and IsHomalgStaticObject( o2 ) then
obj2 := o2;
if IsHomalgLeftObjectOrMorphismOfLeftObjects( o2 ) then
obj1 := AsLeftObject( o1 );
else
obj1 := AsRightObject( o1 );
fi;
elif IsStructureObject( o1 ) and IsStructureObject( o2 ) then
if not IsIdenticalObj( o1, o2 ) then
Error( "the two rings are not identical\n" );
fi;
## I personally prefer left objects:
obj1 := AsLeftObject( o1 );
obj2 := obj1;
else
## the default:
obj1 := o1;
obj2 := o2;
fi;
return FunctorObj( Functor, [ c, obj1, obj2 ] );
end );
if IsBound( der_arg ) then
if IsCovariantFunctor( Functor, der_arg ) then
InstallOtherMethod( functor_operation,
"for homalg objects",
[ IsInt, filter1_obj, filter2_obj, IsString ],
function( n, o1, o2, s )
local H, C, j;
if s <> "a" then
TryNextMethod( );
fi;
H := functor_operation( 0, o1, o2 );
C := HomalgComplex( H );
for j in [ 1 .. n ] do
H := functor_operation( j, o1, o2 );
Add( C, H );
od;
return C;
end );
else
InstallOtherMethod( functor_operation,
"for homalg objects",
[ IsInt, filter1_obj, filter2_obj, IsString ],
function( n, o1, o2, s )
local H, C, j;
if s <> "a" then
TryNextMethod( );
fi;
H := functor_operation( 0, o1, o2 );
C := HomalgCocomplex( H );
for j in [ 1 .. n ] do
H := functor_operation( j, o1, o2 );
Add( C, H );
od;
return C;
end );
fi;
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter1_obj, filter2_obj ],
function( o1, o2 )
local n;
if der_arg = 1 then
n := LengthOfResolution( o1 );
else
n := LengthOfResolution( o2 );
fi;
return functor_operation( n, o1, o2, "a" );
end );
fi;
else
if IsDistinguishedFirstArgumentOfFunctor( Functor ) then
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter1_obj ],
function( o )
local R;
if IsStructureObject( o ) then
R := o;
else
R := StructureObject( o );
fi;
return functor_operation( o, R );
end );
fi;
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter1_obj, filter2_obj ],
function( o1, o2 )
local obj1, obj2;
if IsHomalgStaticObject( o1 ) and IsHomalgStaticObject( o2 ) then ## the most probable case
obj1 := o1;
obj2 := o2;
elif IsHomalgStaticObject( o1 ) and IsStructureObject( o2 ) then
obj1 := o1;
if IsHomalgLeftObjectOrMorphismOfLeftObjects( o1 ) then
obj2 := AsLeftObject( o2 );
else
obj2 := AsRightObject( o2 );
fi;
elif IsStructureObject( o1 ) and IsHomalgStaticObject( o2 ) then
obj2 := o2;
if IsHomalgLeftObjectOrMorphismOfLeftObjects( o2 ) then
obj1 := AsLeftObject( o1 );
else
obj1 := AsRightObject( o1 );
fi;
elif IsStructureObject( o1 ) and IsStructureObject( o2 ) then
if not IsIdenticalObj( o1, o2 ) then
Error( "the two rings are not identical\n" );
fi;
## I personally prefer left objects:
obj1 := AsLeftObject( o1 );
obj2 := obj1;
else
## the default:
obj1 := o1;
obj2 := o2;
fi;
return FunctorObj( Functor, [ obj1, obj2 ] );
end );
fi;
else
Error( "wrong syntax: ", filter1_obj, filter2_obj, "\n" );
fi;
elif number_of_arguments = 3 then
if not IsBound( Functor!.1[2] ) then
Functor!.1[2] := HOMALG.FunctorOn;
fi;
if not IsBound( Functor!.2[2] ) then
Functor!.2[2] := HOMALG.FunctorOn;
fi;
if not IsBound( Functor!.3[2] ) then
Functor!.3[2] := HOMALG.FunctorOn;
fi;
if not IsBound( Functor!.1[2][1] ) or
not IsBound( Functor!.2[2][1] ) or
not IsBound( Functor!.3[2][1] ) then
return fail;
fi;
filter1_obj := Functor!.1[2][1];
filter2_obj := Functor!.2[2][1];
filter3_obj := Functor!.3[2][1];
if IsFilter( filter1_obj ) and
IsFilter( filter2_obj ) and
IsFilter( filter3_obj ) then
if IsBound( Functor!.0 ) and IsList( Functor!.0 ) then
if Length( Functor!.0 ) = 1 then
filter0 := Functor!.0[1];
else
filter0 := IsList;
fi;
if IsDistinguishedFirstArgumentOfFunctor( Functor ) then
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter0, filter1_obj ],
function( c, o )
local R;
if IsStructureObject( o ) then
R := o;
else
R := StructureObject( o );
fi;
return functor_operation( c, o, R, R );
end );
InstallOtherMethod( functor_operation,
"for homalg objects",
[ IsInt, filter1_obj, IsString ],
function( c, o, s )
local R;
if IsStructureObject( o ) then
R := o;
else
R := StructureObject( o );
fi;
return functor_operation( c, o, R, R, s );
end );
fi;
InstallOtherMethod( functor_operation,
"for homalg objects",
[ filter0, filter1_obj, filter2_obj, filter3_obj ],
function( c, o1, o2, o3 )
local obj1, obj2, obj3;
if IsHomalgStaticObject( o1 ) and
IsHomalgStaticObject( o2 ) and
IsHomalgStaticObject( o3 ) then ## the most probable case
obj1 := o1;
obj2 := o2;
obj3 := o3;
elif IsHomalgStaticObject( o1 ) and IsStructureObject( o2 ) and IsStructureObject( o3 ) then
obj1 := o1;
if not IsIdenticalObj( o2, o3 ) then
Error( "the last two rings are not identical\n" );
fi;
if IsHomalgLeftObjectOrMorphismOfLeftObjects( o1 ) then
obj2 := AsLeftObject( o2 );
obj3 := AsLeftObject( o3 );
else
obj2 := AsRightObject( o2 );
obj3 := AsRightObject( o3 );
fi;
## FIXME: there are missing cases
elif ForAll( [ o1, o2, o3 ], IsStructureObject ) then
if not IsIdenticalObj( o1, o2 ) then
Error( "the first two rings are not identical\n" );
elif not IsIdenticalObj( o2, o3 ) then
Error( "the last two rings are not identical\n" );
fi;
## I personally prefer left objects:
obj1 := AsLeftObject( o1 );
obj2 := obj1;
obj3 := obj1;
else
## the default:
obj1 := o1;
obj2 := o2;
obj3 := o3;
fi;
return FunctorObj( Functor, [ c, obj1, obj2, obj3 ] );
end );
if IsBound( der_arg ) then
if IsCovariantFunctor( Functor, der_arg ) then
InstallOtherMethod( functor_operation,
"for homalg objects",
[ IsInt, filter1_obj, filter2_obj, filter3_obj, IsString ],
function( n, o1, o2, o3, s )
local H, C, j;
if s <> "a" then
TryNextMethod( );
fi;
H := functor_operation( 0, o1, o2, o3 );
C := HomalgComplex( H );
for j in [ 1 .. n ] do
H := functor_operation( j, o1, o2, o3 );
Add( C, H );
od;
return C;
end );
else
--> --------------------
--> maximum size reached
--> --------------------
[ Dauer der Verarbeitung: 0.49 Sekunden
(vorverarbeitet)
]
|