% Copyright (c) 1991 Marcel Roelofs, University of Twente, Enschede, % The Netherlands. % % $Header: supervf.web,v 0.94 92/02/26 17:29:57 roelofs Exp $ % \input specification \def\Version$#1Revision: #2 ${Version #2} \def\title{SUPER VECTORFIELD} \font\titlefont=cmcsc10 scaled\magstep3 \font\ttitlefont=cmtt10 scaled\magstep4 \def\topofcontents{\null\vfill \centerline{\titlefont The {\ttitlefont SUPER VECTORFIELD} package for REDUCE} \vskip15pt\centerline{\Version$Revision: 0.94 $} \vskip15pt\centerline{\sc Marcel Roelofs}\vfill} \def\enditem{\medskip\noindent\ignorespaces} % single control sequences are used by WEB \def\BZ{{\bf Z}} \def\BN{{\bf N}} \def\BR{{\bf R}} \def\CinfU{C^\infty(U)} \def\dd#1#2{{\displaystyle{\partial #1\over\partial #2}}} @*=Super vectorfields in REDUCE. In this \.{WEB} file we shall implement the action of $\BZ_2$ graded vectorfields on $\BZ_2$ graded functions. The package is partially based on a former package by Gragert and Kersten, which also implemented $\BZ_2$ graded forms and operators like exterior differentiation, Lie derivatives, etc. Since our methods nowadays mainly consist of using vectorfields, there is no direct need for an implementation of these operators. \medskip \noindent The ``banner line'' defined here is intended for indentification purposes on loading. It should be changed whenever this file is modified. System dependent changes, however, should be made in a separate change file. @d banner="Super vectorfield package for REDUCE 3.4, $Revision: 0.94 $" @ We define the following macros for clarity. @d change_to_symbolic_mode =symbolic @d change_to_algebraic_mode =algebraic @d stop_with_error(string_1,expr_1,string_2,expr_2) = @/ msgpri(string_1,expr_1,string_2,expr_2,t) @; @d message(string_1,expr_1,string_2,expr_2) = @/ msgpri(string_1,expr_1,string_2,expr_2,nil) @; @d operator_name_of=car @d arguments_of=cdr @d first_argument_of=cadr @d second_argument_of=caddr @d first_element_of=car @d rest_of=cdr @d skip_list=cdr %Skip the |'list| in front of an algebraic list% @ The following macros are intended as common programming idioms. @d incr(x) = (x:=x+1)@; @d decr(x) = (x:=x-1)@; @ A new REDUCE switch can be introduced using the following code. @d initialize_global(global_name,value)=@/ global '(global_name)$@/ global_name:=value @d new_switch(switch_name,value)=@/ initialize_global(!* @& switch_name,value)$@/ flag('(switch_name),'switch) @ We do all initializations in the beginning of the package. @u change_to_symbolic_mode$@/ write banner$terpri()$@/ @ change_to_algebraic_mode$ @ We shall start with a (very) short description of the local picture of a graded manifold and vectorfields on these graded manifolds. For a more detailed description we refer to B. Kostant, Lecture Notes in Mathematics 570 (1977). The local picture of a {\it graded manifold} is $U\subset\BR^m$ open together with the {\it graded commutative algebra} $ \CinfU\otimes\Lambda(n) $ where $\Lambda(n)$ is the antisymmetric (exterior) algebra on $n$ elements $s_1,\dots,s_n$, with $\BZ_2$-degree $\vert s_i\vert=1$ and $s_i s_j=-s_j s_i$. A particular element $f\in\CinfU\otimes\Lambda(n)$ is represented by $ f=\sum_\mu f_\mu s_\mu $ where $$\mu\in M_n=\{\mu=(\mu_1,\dots,\mu_k) \mid \mu_i\in\BN,1\leq\mu_1<\mu_2<\cdots< \mu_k\leq n\},$$ $s_\mu=s_{\mu_1}s_{\mu_2}\cdots s_{\mu_k}$ and $f_\mu\in\CinfU$. {\it Graded vectorfields} on a graded manifold $(U,\CinfU\otimes\Lambda(n))$ are introduced as graded derivations of the algebra $\CinfU\otimes\Lambda(n)$. It can be shown that they constitute a left $\CinfU\otimes\Lambda(n)$-module. Locally a graded vectorfield $V$ is represented as $$ V=\sum_{i=1}^m f_i\dd{}{x_i} + \sum_{j=1}^n g_j\dd{}{s_j} $$ with $f_i,g_j\in\CinfU\otimes\Lambda(n)$ and $x_i$ $(i=1,\dots,m)$ a local coordinate system on $U$. The derivations $\dd{}{x_i}$ are even, while the derivation $\dd{}{s_j}$ are odd; they satisfy the relations $$ \dd{x_i}{x_k}=\delta_{ik},\qquad \dd{s_j}{x_k}=0,\qquad \dd{x_i}{x_\ell}=0,\qquad \dd{s_j}{s_\ell}=\delta_{j\ell}. $$ @ In REDUCE we shall represent the elements $s_\mu\in\Lambda(n)$ by EXT($\mu_1,\dots,\mu_k$). Thus elements of $\CinfU\otimes\Lambda(n)$ can be implemented in REDUCE as ordinary algebraic expressions. @= algebraic operator ext$ @*1 Initializing vectorfields. In order to introduce graded vectorfields, we need to know the local coordinates $x_i$ on $U$, as well as the components of $\dd{}{x_i}$ and $\dd{}{s_j}$. In this file we want to implement vectorfields as algebraic operators with a simplification procedure which takes care of the action on a function. It is our purpose to keep the local coordinates and the components local to one vectorfield at a time. The following procedure initializes a super vectorfield. The macro |make_oplist| is taken from the TOOLS package; it transforms algebraic and lisp lists and identifiers into the appropriate lisp lists. We will not give all components of the vectorfield here: it is much easier to give them separately, as we shall see in the sequel. @d make_oplist(op_list)=@/if null op_list then op_list else if atom op_list then list op_list else if car op_list='list then cdr op_list else op_list @; @u lisp operator super_vectorfield; lisp procedure super_vectorfield(operator_name,even_dimension, odd_dimension,variables); begin if not idp operator_name then @/ stop_with_error("SUPER_VECTORFIELD:",operator_name,"is not an identifier",nil); if not fixp even_dimension or even_dimension<0 or not fixp odd_dimension or odd_dimension<0 then@/ rederr("SUPER_VECTORFIELD: improper dimensions"); put(operator_name,'simpfn,'super_der_simp); flag(list(operator_name),'full);@/ put(operator_name,'even_dimension,even_dimension);@/ put(operator_name,'odd_dimension,odd_dimension);@/ put(operator_name,'variables,make_oplist(variables)); end$ @*1 Implementation of exterior multiplication. Before we can implement the action of a graded vectorfield on a graded function we need to have a function that computes the (exterior) multiplication of two elements of $\Lambda(n)$. If we have two elements EXT($i_1,\dots,i_n$) and EXT($j_1,\dots,j_m$) then the product will be 0 or an expression of the form $\pm{}$EXT(\dots). In order to find this result we need to merge the lists $(i_1,\dots,i_n)$ and $(j_1,\dots,j_m)$ into one ordered list, taking into account the signs that occur due to the switching of all pairs of elements of the lists. In fact, since it is needed for cohomology computations by van den Hijligenberg and Post, we shall implement an even more general procedure: given two {\it ordered} lists $(i_1,\dots,i_m)$ and $(j_1,\dots,j_m)$, return the list which results from merging the two lists into one ordered lists, together with a sign due to the switching of indices. The elements of the list need, however, not only be positive integers anymore, but may also be negative integers, with the proviso that switching two negative integers does {\it not} cause a sign. The algorithm is rather simple: given two lists |x1| and |x2| we construct the merged list |x2| as follows (the notation |cx1| is an abbreviation for |car x1|, and the same for all other lists): \medskip \item{1.} reverse |x1| (|x1| is now ordered reversely) and move all the elements of |x2|, with which the first element of |x1| (i.e.\ the highest element) has to be interchanged for merging both lists, in reverse order on the list |lx2|. Keep track if the number of elements of |lx2| is odd or even with help of the boolean |oddskip|. \item{2.} if either |x1| or |lx2| is empty return the appropriate result. \item{3.} if |cx1=clx2| then we can return |nil| if both are positive, due to the anticommutativity. \item{4.} if |cx1>clx2| put |cx1| in front of |x2| and adjust the sign according to |oddskip| only if |cx1| is positive: if |cx1| is negative, so are all elements of |lx2| and thus no sign need to be added. Continue with 2. \item{5.} if |cx1<=clx2| put |clx2| in front of |x2| and adjust |oddskip|. Continue with 2. \enditem Since it is used quite frequently, we shall implement this procedure using labels in order to prevent overhead caused by (recursive) function calls. @u lisp procedure merge_lists(x1,x2); begin scalar cx1,cx2,lx2,clx2,oddskip,sign; @; b: @; end$ @ The implementation of step 1. @= sign:=1; x1:=reverse x1; if x1 then cx1:=car x1 @+else goto b; a: if x2 then cx2:=car x2 @+else goto b; if cx1= if null x1 then @+return sign . nconc(reversip lx2,x2); if null lx2 then @+return sign . nconc(reversip x1,x2); clx2:=car lx2; if cx1=clx2 and cx1>0 then @+return nil; if cx1>clx2 then goto b1; @; b1: @ @ The implementation of step 5. @= x2:=clx2 . x2;@/ lx2:=cdr lx2;@/ oddskip:=not oddskip;@/ goto b @ And finally step 4. @=@/ x2:=cx1 . x2;@/ x1:=cdr x1; if oddskip and cx1>0 then sign:=-sign; cx1:=car x1;@/ goto b @ It's a piece of cake now the write a procedure for the multiplication of two ``EXT'' kernels. By definition |ext()| is equal to 1. @d sign_of=car @d arg_list_of=cdr @u lisp procedure ext_mult(x1,x2); (if null x then nil ./ 1 else @+if null arg_list_of x then 1 ./ 1 else (((!*a2k('ext . arg_list_of x) .^ 1) .* sign_of x) .+ nil) ./ 1)@/ where x=merge_lists(arguments_of x1,arguments_of x2)$ @*=The simplification procedure for vectorfields. The only thing left now is to implement the action of a vectorfield on a function by means of the simplification procedure |super_der_simp|. If $V$ is a vectorfield we shall assume that the components of $\dd{}{x_i}$ and $\dd{}{s_j}$ are given by $V(0,i)$ and $V(1,j)$, respectively. Since we want to be able to look at the value of the components, we have to make the following distinction: if a vectorfield has just one argument it is the action on a function, otherwise we just have to return the value of the kernel. @u lisp procedure super_der_simp u; if length u=2 then @ else simpiden u$ @ The action is not very complicated: collect all the even and odd components of the vectorfield and apply the vectorfield to the numerator and denominator of the function, using the quotient rule. Notice that we don't want denominators of any function to contain odd variables, since such an expression can always be rewritten to a finite expression without odd variables in the denominator. @= begin scalar derivation_name,variables,even_components,odd_components,@| splitted_numr,splitted_denr; derivation_name:=reval operator_name_of u;@/ variables:=get(derivation_name,'variables);@/ u:=simp!* first_argument_of u; @; return subtrsq(@| quotsq(addsq(even_action(even_components,splitted_numr),@| odd_action(odd_components,splitted_numr)), denr u ./ 1),@| quotsq(multsq(numr u ./ 1, even_action(even_components,splitted_denr)),@| multf(denr u,denr u) ./ 1)); end @*1 Getting the vectorfield components. Finding all linear kernels of an algebraic operator and their coefficients in a standard form is performed by the procedure |split_form| of the TOOLS package, which acts on standard forms. Since it is more convenient for the components of the vectorfield to have the coefficients returned by |split_form| as standard quotients instead of standard forms, the following procedure applies |split_form| to the numerator of a standard quotient and takes care of the necessary conversion of the coefficients to standard quotients. In order to allow simple processing of the lists the independent part must be preceded by |ext()|. @d independent_part_of=car @d kc_list_of=cdr @d kernel_of=car @d coefficient_of=cdr @u lisp procedure split_ext(sq,op_list); begin scalar denr_sq,splitted_form; denr_sq:=denr sq; splitted_form:=split_form(numr sq,op_list); return (list('ext) . cancel(independent_part_of splitted_form ./ denr_sq)) . for each kc_pair in kc_list_of splitted_form collect@/ (kernel_of kc_pair . cancel(coefficient_of kc_pair ./ denr_sq)) end$ @ For a proper action of |even_action| and |odd_action| all components need to be decomposed into ``EXT'' kernels and their coefficients. Since the action is most conveniently performed recursively on standard forms, the numerator and denominator are decomposed at standard form level. @= splitted_numr:=split_form(numr u,'(ext));@/ splitted_numr:= (list('ext) . independent_part_of splitted_numr) . kc_list_of splitted_numr;@/ splitted_denr:=split_form(denr u,'(ext));@/ splitted_denr:= (list('ext) . independent_part_of splitted_denr) . kc_list_of splitted_denr;@/ even_components:=for i:=1:get(derivation_name,'even_dimension) collect@/ (nth(variables,i) . split_ext(component,'(ext)))@| where component=simp!* list(derivation_name,0,i);@/ odd_components:=for i:=1:get(derivation_name,'odd_dimension) collect@/ (i . split_ext(component,'(ext)))@| where component=simp!* list(derivation_name,1,i) @*1 Action of the even components. The action of the even part of a vectorfield on a function is fairly simple at top level: just add the actions on all kernel-coefficient pairs. @u lisp procedure even_action(components,splitted_form); begin scalar action; action:=nil ./ 1; for each kc_pair in splitted_form do@/ action:=addsq(action, even_action_sf(components,coefficient_of kc_pair,kernel_of kc_pair,1)); return action; end$ @ The action on a standard form is the sum of the actions on all terms. If the last term is a domain element we don't have to take it into consideration. @u lisp procedure even_action_sf(components,sf,ext_kernel,fac); begin scalar action; action:=nil ./ 1; while not domainp sf do <>; return action; end$ @ For the action on the leading term we use the derivation property: the action on the leading power has to be added to the action on the leading coefficient. The last argument of |even_action_sf| is the product of all leading powers which have already been treated and with which the result has to be multiplied. For reasons of efficiency it is more convenient to have the factor as in standard quotient in |even_action_pow|. @d term_pow=car @d term_coeff=cdr @u lisp procedure even_action_term(components,term,ext_kernel,fac); addsq(even_action_pow(components,term_pow term, ext_kernel,!*f2q multf(fac,term_coeff term)),@| even_action_sf(components,term_coeff term, ext_kernel,multf(fac,!*p2f term_pow term)))$ @ Finally we have to implement the action on leading powers. For this we have to find all dependencies of the main variable on local coordinates occuring in the vectorfield, and act accordingly. @u lisp procedure even_action_pow(components,pow,ext_kernel,fac); begin scalar kernel,n,component,derivative,action,active_components; kernel:=car pow; n:=cdr pow; %|pow=kernel^n|% @; @; @; end$ @ We can check if |kernel| is one of the local coordinates by a simple |assoc| on |components|. @= if (component:=assoc(kernel,components)) then return <> @ The procedure |component_action| takes care of returning the sum of all products of the |kc_pairs| in |component| with |ext_kernel| and |derivative|. Recall that super vectorfields have a left $\CinfU\otimes\Lambda(n)$ module structure. This means that we have to take care that the arguments in the |ext_mult| call have to be in the right order: components of the vectorfield left and the |ext_kernel|'s from the function right. Of course, if the product of the two ``EXT'' kernels is zero, there is no need to consider the summand. @d combined_product(x,y,z)=@/multsq(multsq(x,y),z) @u lisp procedure component_action(component,ext_kernel,coefficient); begin scalar action; action:=nil ./ 1; for each kc_pair in kc_list_of component do@/ (if numr ext_product then@/ action:=addsq(action, combined_product(ext_product,even_coefficient,coefficient)))@| where ext_product=ext_mult(kernel_of kc_pair,ext_kernel),@| even_coefficient=coefficient_of kc_pair; return action; end$ @ If a kernel is not one of the local coordinates, it may still depend on them, in which case we can still differentiate it w.r.t. such a coordinate. The following procedure tries finds all active components in |kernel| as completely as possible. @d get_dependencies_of(kernel)= ((if depl_entry then cdr depl_entry) where depl_entry=assoc(kernel,depl!*)) @u lisp procedure find_active_components(kernel,components,components_found); begin components_found:=@| update_components(kernel . get_dependencies_of(kernel), components,components_found)$ if not atom kernel then for each element in kernel do@/ components_found:=find_active_components(element,components,components_found); return components_found; end$ @ The procedure |update_components| takes care that |components_found| contains all active components just once. @u lisp procedure update_components(dependencies,components,components_found); begin scalar component; for each kernel in dependencies do if (component:=assoc(kernel,components)) and not assoc(kernel,components_found) then@/ components_found:=component . components_found; return components_found; end$ @ @=@/ active_components:=find_active_components(kernel,components,nil) @ Once we know all active components we can simply apply |diffp| to compute the derivatives of |pow| and |component_action| to compute the action of the different components. Recall that the final result has to be multiplied with |fac|. @= action:=nil ./ 1; for each component in active_components do <>; return multsq(action,fac) @*1 Action of the odd components. The action of the odd components is much simpler than the action of the even components since the dependencies are clear at once: the only dependency on odd variables are the indices of the ``EXT'' kernels. Odd differentiations can cause an additional sign: $$ \dd{}{s_{i_j}}s_{i_1}\dots,s_{i_j},\dots,s_{i_n}= (-1)^{j-1}s_{i_1}\dots,\widehat{s_{i_j}},\dots,s_{i_n} $$ Additional signs are governed by the boolean |sign|. After the deletion of one index we have to apply |!*a2k| in order to get a unique kernel. @u lisp procedure odd_action(components,splitted_form); begin scalar action,sign,derivative,kernel,coefficient,component; action:=nil ./ 1; for each kc_pair in splitted_form do <> >>; return action; end$ @*=Multiplication of graded expressions. Since it is useful in practical problems, we shall finally implement a procedure |super_product| for multiplying two graded expressions. Using some of the above procedures this is not difficult at all. @u lisp operator super_product; lisp procedure super_product(x,y); begin scalar splitted_x,splitted_y,product; splitted_x:=split_ext(simp x,'(ext)); splitted_y:=split_ext(simp y,'(ext));@/ product:=nil ./ 1; for each term_x in splitted_x do for each term_y in splitted_y do@/ product:=addsq(product,@| combined_product(coefficient_of term_x,coefficient_of term_y,@| ext_mult(kernel_of term_x,kernel_of term_y))); return mk!*sq subs2 product; end$ @ The end of a REDUCE input file must be marked with |end|. @u end; @*=Index. This section contains a cross reference index of all identifiers, together with the numbers of the mdules in which they are used. Underlined entries correspond to module numbers where the identifier was declared.