Outline
Manual selective substitution
TL;DR : Simplify[-2*a^2 + 2*b^2/(c^2 - d^2), A1 == -a^2 + b^2/(c^2 - d^2)]
Maybe see also the links at the end of this section
In this scenario, the user has identified subexpressions that they would like to substitute with variables.
Advantage:
Disadvantage:
In the example given in the comment below the answer by andre314,
b/(-a^2 + b^2/(c^2 - d^2))
can be replaced by
b/A1
using a replacement rule directly:
b/(-a^2 + b^2/(c^2 - d^2)) /. -a^2 + b^2/(c^2 - d^2) -> A1
However, this will not work when extra work is needed before making the substitution.
For example in :
-2*a^2 + 2*b^2/(c^2 - d^2)
one has to first tell Mathematica to factor out the 2 which is difficult as FactorTerms factors a -2 and the -1 is distributed across terms in the subexpression which then makes the substitution not obvious for Mathematica.
A quick fix here is to use Simplify with a side relation as shown here:
Simplify[-2*a^2 + 2*b^2/(c^2 - d^2), A1 == -a^2 + b^2/(c^2 - d^2)]
(* 2 A1 *)
Other methods are shown here:
Automatic detection and substitution of subexpressions
These are functions that automatically detect subexpressions and define variables for them.
Advantage:
- Simplifies a complicated expression without having to identify sub-expressions
Disadvantage:
- Less control over which subexpressions are replaced by variables
some related functions:
test:
$$ \frac{-2 a^2+\frac{2
b^2}{c^2-d^2}+4}{-2
a^2+\frac{2
b^2}{c^2-d^2}+5} $$
test=(4 + (-2*a^2 +
2*b^2/(c^2 - d^2)))/(5 + (-2*a^2 + 2*b^2/(c^2 - d^2)))
Experimental`OptimizeExpression[test]
out:
Experimental`OptimizedExpression[
Block[{Compile`$12, Compile`$13, Compile`$14, Compile`$15,
Compile`$16, Compile`$17, Compile`$18, Compile`$19, Compile`$30},
Compile`$12 = a^2; Compile`$13 = -2 Compile`$12; Compile`$14 = b^2;
Compile`$15 = c^2; Compile`$16 = d^2; Compile`$17 = -Compile`$16;
Compile`$18 = Compile`$15 + Compile`$17;
Compile`$19 = 1/Compile`$18;
Compile`$30 = 2 Compile`$14 Compile`$19; (
4 + Compile`$13 + Compile`$30)/(5 + Compile`$13 + Compile`$30)]]
ResourceFunction[
"SimplifyRepeatedSubexpressions"][test]
{(4 + $83 + $84)/( 5 + $83 + $84), {$83 -> (2 b^2)/(c^2 - d^2), $84 -> -2 a^2}}
res = ResourceFunction[
"RecursiveRewrite"][test];
Nest[ReplaceAll[Last@res], First[res], 3]
(4 + "c1" "c10" + "c11" "c17" "c3")/("c14" + "c18" + "c5")
ResourceFunction["RecursiveRewrite"] tends to consider everything as subexpressions whereas ResourceFunction["SimplifyRepeatedSubexpressions"] seems to focus on repeated subexpressions and ExperimentalOptimizedExpression recursively rewrites those subexpressions.
As I understand, ExperimentalOptimizedExpression is used internally by Compile. If the output from Experimental`OptimizedExpression is a bit hard to read, maybe you can use this answer to make it more readable. The answer there uses Compile directly instead but the manipulations are analogous in both cases.
Using https://stackoverflow.com/a/4208142/19955621
we can also filter out the subexpressions we want. The code counts the number of times a subexpression occurs which can allow us to focus more on certain subexpressions. We can also filter by complexity (for example by LeafCount) and remove subexpressions of subexpressions using the code below (to be used with the code of the link mentioned before):
Note: •=\[Bullet]
•SubexpressionQ[a_, b_] := MemberQ[Cases[a, _, All], b]
•FilterOutSubexpressions[x_] :=
Select[x,
s |-> Not@
AnyTrue[x[[All, 1]] //
DeleteCases[s[[1]]], •SubexpressionQ[#, s[[1]]] &]]
The code from that link wrapped into a function:
•SubExpressionCount[s_] :=
Module[{index,value,indices,values,items,indexQ,counts},
index[downvalue_, dict_] := (downvalue[[1]] /. HoldPattern[dict[x_]] -> x) // ReleaseHold;
value[downvalue_] := downvalue[[-1]];
indices[dict_] := Map[#[[1]] /. {HoldPattern[dict[x_]] -> x} &, DownValues[dict]] // ReleaseHold;
values[dict_] := Map[#[[-1]] &, DownValues[dict]];
items[dict_] := Map[{index[#, dict], value[#]} &, DownValues[dict]];
indexQ[dict_, index_] := If[MatchQ[dict[index], HoldPattern[dict[index]]], False, True];
Map[(counts[#]=If[indexQ[counts,#],counts[#]+1,1];#)&,s,Infinity];
items[counts]
]
Note: It is unclear to me whether the output of •SubExpressionCount can be different to Tally@Cases[#, _, All] & after ordering .
usage:
•SubExpressionCount[expr] //
Select[#[[2]] > 1 && LeafCount[#] > 10 &] //
ReverseSortBy[Last] // Column
$$
\begin{array}{l}
\left\{c^2-d^2,2\right\} \\
\left\{\frac{2 b^2}{c^2-d^2},2\right\} \\
\left\{\frac{1}{c^2-d^2},2\right\} \\
\end{array}
$$
If we filter out subexpressions that are subexpressions of subexpressions in the list then we obtain:
•SubExpressionCount[expr] //
Select[#[[2]] > 1 &&
LeafCount[#] > 10 &] // •FilterOutSubexpressions //
ReverseSortBy[Last]
{{(2 b^2)/(c^2 - d^2), 2}}
-a^2 + b^2/(c^2 - d^2)
For example: b/(-a^2 + b^2/(c^2 - d^2)) = b/A1.
Secondly, this subexpression was chosen just as an illustration. I think, this method might not work when it is hard (if possible) to express one of the variables through the others.
– user43283 Jan 05 '19 at 21:54