Welcome to Cadabra Q&A, where you can ask questions and receive answers from other members of the community.
+1 vote

Hello!

As far as I understood, the zoom() command was introduced to produce manipulations on a subset of the full sum. It might be useful to extend its functionality in two ways.

First, if you have a long expression, you would probably want to examine it by parts. In present form zoom() does not suits this goal. To illustrate this, consider the following code:

test := H = a + b a;

Then by using zoom() in different ways I get:

zoom(test, $ a $); 
H = a + ...
unzoom(test),
zoom(test, $ a Q?? $);
H = ba + ... 

The second result is what one needs for the manipulations of the expressions. However, none of them suits for examining all terms including a in a simple way. Or is there an implemented mechanism for getting all terms including a?

Second, having an analogue of zoom for creating new object might be useful. Schematically, something like

H2 := zoom(H1,$ a Q?? $),

will allow to add H2 to other objects, etc. I did not find a way to implement this in the present version of Cadabra.

The things I've described can be implemented by introducing additional steps (like using substitute and setting some of the parameters to zero), but 1) it would not always work and 2) it is nice when it is possible to make complicated things by running only one command.

Kasper, thank you very much for creating and improving Cadabra! It is very useful.

in Feature requests by (220 points)
edited by

2 Answers

+1 vote
 
Best answer

The fact that zoom(smth,$ a Q?? $) does not match a single term a is intended behaviour, but of course that does not help you. This should have been possible by using multiple patterns in the same zoom, but that does not work at the moment. I have opened an issue at https://github.com/kpeeters/cadabra2/issues/161.

by (82.5k points)
selected by
0 votes

Regarding the first part of your question, you could try

test := H = a + b a + b c;
factor_out(test, $a$);
zoom(test, $ a Q?? $);

For the second, you could try (notice that I'm not using an equation in my expression):

test := a + b a + b c;
factor_out(test, $a$)
take_match(_, $a Q??$);

this separates the the part to be assigned to a new variable

H2 := @(test);

and now you can return test to its original expression

replace_match(test);

Update (2019-Sep-06)

Thanks to @kasper answer to a related question, I provide an updated solution to @sapor question (se comments below.

The solution is to assing a Weight property to the derivative,. However, the assignation of this property for the derivative is define through the parameter self of the WeightInheritproperty. Thus, consider the following

(x, y)::Coordinate.
(i, j)::Indices(values={x, y}, position=fixed).
\partial{#}::Derivative.
h_{i j}::Depends(x, y, \partial{#}).
\delta{#}::KroneckerDelta.
\partial{#}::WeightInherit(label=order, self=1, type=multiplicative);

test := A \delta_{i j} + h_{i j} 
+ \partial_{x}{h_{i j}} 
+ B \partial_{y}{h_{i j}} 
+ C \partial_{x}{ \partial_{y}{ h_{i j} } } 
+ \partial_{x}{ \partial_{x}{ h_{i j} } };

Now, we can take the particular order witht he keep_weight algorithm

term0 := @(test).
keep_weight(_, $order=0$);
term1 := @(test).
keep_weight(_, $order=1$);
term2 := @(test).
keep_weight(_, $order=2$);

*NOTE:** The solution above does not work properly if the derivative is declared as PartialDerivative

by (15.0k points)
edited by

Thank you for the answer!

The example you provided does work in this simplest case. However, I was keeping in mind more complicated problems. Namely, the extraction of terms with derivatives. Let me consider the following example:

{x, y}::Coordinate:
{i, j}::Indices(values={x, y}, position=fixed):
{\partial{#}}::PartialDerivative:
H::Depends(x, y):
test := H + 2 \partial_{x}{H} + H \partial_{y}{H};

Is there a way to extract all terms with derivatives in this case? I can use

factor_out(test, $ \partial_{i?}{H} $);

to "factor out" all derivatives. But it does not help to extract all term with derivatives due to the problem described in my question. Is there a simple solution?

Regarding the second part of my question, the solution you suggested works. Thank you!

Hi Ivan. I've tried a whole bunch of different things, but it seems that the sort of manipulation you would like is not (yet) possible. However, I'd suggest to manipulate each derivatives individually. In case you would like to execute a whole set of (similar) instructions , it is possible to define a python function that goes through it, e.g.

def do_something(obj,order):
    substitute(obj,g_pert)
    substitute(obj,g_pertt)
    product_rule(obj)
    distribute(obj)
    unwrap(obj)
    keep_weight(obj, order)
    sort_product(obj)
    collect_factors(obj)
    eliminate_kronecker(obj)
    canonicalise(obj)
    eliminate_metric(obj)
    eliminate_metric(obj)
    eliminate_metric(obj)
    rename_dummies(obj)
    return obj

I found a way to achieve what I wanted (was related to perturbation theory in a modification of GR), so this was not a problem. Thank you for suggesting other possible solutions.

Yet it seems strange (=unexpected) to me that the command

zoom(smth,$ a Q?? $);

does not return all occurrences of a. Is this the intended behavior? May be using regular expressions can help, but I didn't succeed on that route. In any case, it would be nice have a function returning all occurrences of a given object.

Regards, Ivan.

Could you share your solution? I was about to propose you to use the Weight property to isolate the terms with derivatives... but at the moment it seems out of place!

Regards.

Sure. In my case I needed to extract terms with zero, one and two derivatives. Simplifying, this goes as follows. Initialization:

(x, y)::Coordinate:
(i, j)::Indices(values={x, y}, position=fixed):
{\partial{#}}::PartialDerivative:
h_{i j}::Depends(x, y):
\delta{#}::KroneckerDelta:

A (random) test tensor with various derivatives:

test := T_{i j} = A \delta_{i j} + h_{i j} + \partial_{x}{h_{i j}} + 
B \partial_{y}{h_{i j}} + C \partial_{x y}{h_{i j}} + \partial_{x x}{h_{i j}};

Then I simply extract terms with different numbers of derivatives by using the substitute command:

der0 := D0_{i j} = T_{i j}:
substitute(der0, test), 
substitute(der0, $ \partial_{?i ?j}{h_{k? l?}} -> 0, \partial_{?i}{h_{k? l?}} -> 0 $); 
der1    := D1_{i j} = T_{i j} - D0_{i j}:
substitute(der1, test),
substitute(der1, der0),
substitute(der1, $ \partial_{?i ?j}{h_{k? l?}} -> 0 $);
der2    := D2_{i j} = T_{i j} - D0_{i j} - D1_{i j}:
substitute(der2, test),
substitute(der2, der0),
substitute(der2, der1);

Straightforward and highly inefficient, but it works. At least, in my case.

One of the problems in improving the code was that if I perform

substitute(test, $ h_{i? j?} = 0 $);

it will set all of h's derivatives to zero as well. Hence the need to extract der1 and der2 in such a terrible manner. I didn't find a way to overcome such behavior of substitute command. Isn't this a little problem as well?

Providing a `Weight` to a `Derivative`
...