# Releasing multiple nested Hold in a single expression simultaneously

Trivia: I need to take a coefficient list of a long list of complicated expressions of the form $$\prod c_{ij} \left( p_{i,j}(x_1,x_2) \right)^n$$ where $$n$$ is positive integer, and $$i,j$$ label polynomial functions $$p_{ij}$$ in $$x_1,x_2$$ and integer constants $$c_{ij}$$.

What I do: To prevent hitting the memory limit, instead of substituting each $$p_{ij}$$, I substitute its coefficient list, and change the products and powers to  ListConvolve:

Replace[MList[[1]],
Power[b_, n_Integer]:>
Hold[Fold[
ListConvolve[#1,#2,{1,-1},0]&,
Table[b,n]
]],
{1}]
result1 = Replace[%,
Times[a0_Integer, b__]:>
a0 * Hold[Fold[
ListConvolve[#1,#2,{1,-1},0]&, {b}
]
],
{1}]

substitutionrules = Table[p[[i1,i2]] -> Coefflist[[i1,i2]]}, {i1,1, 7}, {i2,1,7}]


Where coefficient list Coefflist was already found. Then, I substitute these rules, and would like to release the Hold to bear the fruits of my labor. The following code is definitely incorrect:

result1 = result1//.substitutionrules;
result1 = FixedPoint[ReleaseHold, %];


Questions:

1. How to release the Hold correctly?

2. How to properly parallelize the last step and do not run out of memory?

3. Are there any simplifications or tricks I have missed?

Technical Details:

Length@MList
4137
Dimensions@substitutionrules
{7,7,50,50}
LeafCount@substitutionrules
127 394 421


P.S. Although I have been using Mathematica for quite some time, I have started coding on it only recently. Thus, I am sorry if my code is not easy to read, or I am asking about some common knowledge things.

UPDATE:

For the test purposes, assume

MList = {784032 Derivative[1, 1][p][x1, x2]^6 Derivative[2, 0][p][
x1, x2]^3};

substitutionrules = {Derivative[1, 1][p][x1,
x2] -> {{0, 2 c1, 2}, {0, 0, 0}, {0, 0, 0}},
Derivative[2, 0][p][x1, x2] -> {{0, 0, 0}, {0, -2, 0}, {0, 0, 0}}}


I have also deleted the overall ParallelDo command, since it was implemented wrongly.

UPDATE 2

The solution, suggessted by @MarcoB, unfortunately, does not work. Since the original post was said to be ambiguous, I will provide a clean example of what I want to achieve.

expr = 1024 f1 f2^2 f3^4;
substitutionrules = {
f1 -> CoefficientList[{(a x1 + b x2 + c x1^2)}, {x1, x2}],
f2 -> CoefficientList[{(b x1 + d x1 x2 + cx2^2)}, {x1, x2}],
f3 -> CoefficientList[{b x1 + a x2 + c x1 x2}, {x1, x2}]};


I want to turn all Times and Power into ListConvolve of the coefficient lists presented above:

Replace[expr,
Power[b_, n_Integer]:>
Hold[Fold[
ListConvolve[#1,#2,{1,-1},0]&,
Table[b,n]
]],
{1}];
expr1 = Replace[%,
Times[a0_Integer, b__]:>
a0 * Hold[Fold[
ListConvolve[#1,#2,{1,-1},0]&, {b}
]
],
{1}];


This procedure yields

1024 Hold[Fold[
ListConvolve[#, #2, {1, -1}, 0]& ,
{f1,
Hold[Fold[
ListConvolve[#, #2, {1, -1}, 0]& ,
Table[f2, 2]
]],
Hold[Fold[
ListConvolve[#, #2, {1, -1}, 0]& ,
Table[f3, 4]
]]
}
]]


Now, I would like to substitute the $$f_1, f_2, f_3$$, and, finally, ReleaseHold:

expr1=expr1//.substitutionrules;

Map[ReleaseHold,expr1,{4}];
Map[ReleaseHold,%,    {1}]


UPDATE 3

MapAll should not be used in this case, and indeed leads to mistakes. The reason is that, by the definition, this function applies to ALL subexpressions, parts of the list to be convolved and the slots #1 themselves included.

The compiler (as of version 11.2) simply cannot parse the result, and throws a general exception.

One of the right solutions is straightforward: by consequentially applying the Map at the desired levels.

There is also a technique of using patterns to consequentially replace the desired Hold[...] with its argument with the delayed rule :>.

• Your code is incomplete (missing definitions) so we cannot run it. So we can only give you our best guess. What happens if you simply do ReleaseHold@resultList? that should release all holds, as long as you don't have nested ones, i.e. Hold[.. Hold[...],..]. Commented May 14, 2020 at 18:07
• Yes, I have nested ones. I will update the file in a second with a runnable example Commented May 14, 2020 at 18:12
• Are you sure your code is copied correctly? For instance, inside your ParallelDo you have two Replace whose results go nowhere, and they are also separated only by a space, which indicates multiplication. Have you tried copying your post back into your notebook to check that it was correct? Commented May 14, 2020 at 18:18
• I am sorry for the mess. I have added ParallelDo on the go, when I was writing this post. I have not implemented in my notebook yet. Commented May 14, 2020 at 18:26
• So, I have updated my question. The whole point of my misunderstanding is how to release multiple nested Hold, starting from the deepest ones and then going to the ones at first level (i.e. in the inverse order) Commented May 14, 2020 at 19:39

Your code still does not contain all definitions, so we cannot execute it to obtain your expressions, but you distilled down the problem in comments to say that you want to "release multiple nested Hold, starting from the deepest ones and then going to the ones at first level (i.e. in the inverse order)".

Consider this minimal example then, which contains a nested Hold:

expr = Hold[Sin[Hold[Cos[x]]]];


It is true that a single application of ReleaseHold only removes the outermost one:

ReleaseHold@expr

(* Out: Sin[Hold[Cos[x]]] *)


Try MapAll instead (i.e. //@):

ReleaseHold //@ expr

(* Out: Sin[Cos[x]] *)


As you can see, this releases everything. According to the MapAll documentation, leaves of an expression are visited before roots, so that should proceed in the direction you wanted as well.

• Thank you. This is what I was looking for, essentially Commented May 14, 2020 at 20:29
• @Sergei Glad to help! Commented May 14, 2020 at 20:33
• Sorry, for late update. No, it does not work. See Upd 2 in my answer Commented May 14, 2020 at 21:38
• Also, see Upd 3 in my answer Commented May 14, 2020 at 22:01
• Also, see Upd 4 for the details why MapAll leads to an error in this case. Commented May 14, 2020 at 22:26
Replace[expr1, Hold[a___]:>a, 4]


The Replace goes from the deepest level to the roots.