Outline
One could consider a home cooked simplify.
The purpose of the code below is to eliminate any guidance from the user. That is, there is no hard coding or any way to incorporate insight from the user.
The main idea is to find the terms that occur the most in multiplications and use Collect
with those variables.
Usage example
Using simplify defined below:
expr // simplify
(G3 + G4 + G5 + G7 + G8) P2 + (G1 + G3 + G4 + G5 + G7 + G8) P5 + (G1 + G2 + G4 + G5 + G6 + G8) (P3 + P6)
In LaTeX :
$$(\text{P3}+\text{P6}) (\text{G1}+\text{G2}+\text{G4}+\text{G5}+\text{G6}+\text{G8})+\text{P5} (\text{G1}+\text{G3}+\text{G4}+\text{G5}+\text{G7}+\text{G8})+\text{P2} (\text{G3}+\text{G4}+\text{G5}+\text{G7}+\text{G8})$$
(expr // simplify) == desired
(*True*)
Code:
(code unpacked and mostly explained below)
Note: The code checks that the expression is a sum containing powers or multiplications and aborts otherwise.
Note: this version of the code uses ReplaceRepeated
and so it might end up in an infinite loop for some expressions.
I changed the code to use Bob Hanlon's replacement rule as my idea looks silly now. The beginning of the code that did not involve finding multiple occurrences of a factor is the same. The previous version of the code is given at the end of this answer
Clear[simplify];
simplify[expression_]:=
Module[{check,var,tocollect,
simplified},
(******************************)
(* Begin check (End check below) *)
(*
check that the expression is a
sum of products or powers
*)
check= Head[expression]===Plus &&
(List@@expression//AllTrue[#,MatchQ[_Power | _Times]]&);
If[check===False,
Print["simplify is not adapted to this structure"];
Abort[]
];
(* End check *)
(**********************************)
var=(expression//Variables);
tocollect={Count[expression,#*_],#}&/@var
//MaximalBy[First]
//Map[Last];
simplified=Collect[expression, tocollect, Simplify]
//. a_*b_+c_*b_:> (a+c)*b
]
Explanation
Find the variables:
var = (expr // Variables);
Find which variables occur the most in the multiplications:
{Count[expr, #*_], #} & /@ var // Sort
{{2, G2}, {2, G3}, {2, G6}, {2, G7}, {3, G1}, {4, G4}, {4, G5}, {4, G8}, {5, P2}, {6, P3}, {6, P5}, {6, P6}}
(included transposition to reduce displayed height )
$$\left(
\begin{array}{cccccccccccc}
2 & 2 & 2 & 2 & 3 & 4 & 4 & 4 & 5 & 6 & 6 & 6 \\
\text{G2} & \text{G3} & \text{G6} & \text{G7} & \text{G1} & \text{G4} & \text{G5} & \text{G8} & \text{P2} & \text{P3} & \text{P5} & \text{P6} \\
\end{array}
\right)$$
Collect the variables that occur the most in multiplications:
simplified = Collect[expr, {P6, P5, P3}, Simplify]
(G3 + G4 + G5 + G7 + G8) P2 + (G1 + G2 + G4 + G5 + G6 + G8) P3 + (G1 + G3 + G4 + G5 + G7 + G8) P5 + (G1 + G2 + G4 + G5 + G6 + G8) P6
$$ \text{P3} (\text{G1}+\text{G2}+\text{G4}+\text{G5}+\text{G6}+\text{G8})+\text{P6} (\text{G1}+\text{G2}+\text{G4}+\text{G5}+\text{G6}+\text{G8})+\text{P5} (\text{G1}+\text{G3}+\text{G4}+\text{G5}+\text{G7}+\text{G8})+\text{P2} (\text{G3}+\text{G4}+\text{G5}+\text{G7}+\text{G8}) $$
factor common factors with //. a_*b_+c_*b_:> (a+c)*b
(using Bob Hanlon's method instead of mines in the previous code )
Comparing with Simplify on random expressions
List of variables:
vars = Array[m, 30];
Test expression:
expression = RandomChoice[vars, 20] . RandomChoice[vars, 20];
Comparison between Simplify
and simplify on this example:
simplified1 = expression // Simplify;
simplified2 = expression // simplify ;
simplified1 // LeafCount
simplified2 // LeafCount
Simplify : 94
simplify : 82
Check:
simplified1 == simplified2 // Simplify
(* True *)
Statistics :
expressiontable =
Table[RandomChoice[vars, 20] . RandomChoice[vars, 20], 20];
simps1 = simplify /@ expressiontable ;
simps2 = Simplify /@ expressiontable;
simps1 == simps2 // Simplify
(* True *)
LeafCount /@ simps1 // Mean // N
LeafCount /@ simps2 // Mean // N
Average complexity using simplify: 76.3
Average complexity using Simplify: 94.
Previous version of the code
Clear[simplify];
simplify[expression_]:=
Module[{check,var,to⎵collect,
simplified,duplicated,x,renaming⎵rule,
renaming⎵rule⎵inversed,new⎵variables},
check= Head[expression]===Plus &&
(List@@expression//AllTrue[#,MatchQ[_Times]]&);
If[check===False,
Print["simplify is not adapted to this structure"];
Abort[]
];
var=(expression//Variables);
to⎵collect={Count[expression,#*_],#}&/@var
//MaximalBy[First]
//Map[Last];
simplified=Collect[expression, to⎵collect, Simplify];
(* Find subexpressions that occur
multiple times *)
duplicated=simplified
//Level[#,{2}]&
//Gather
//Select[Length@#>1&]
//Map[DeleteDuplicates]
//Flatten;
Collect[simplified,
duplicated,
Simplify]
]
The major change difference with the newer version is that it explicitly collects sub expressions that occur more than once.
Explanation:
One could maybe use
Experimental`OptimizeExpression
(see for example Common subexpression from two expressions )
to find common sub expressions in the expression above but instead I consider a more simple approach for this kind of structure:
simplified // Level[#, {2}] & // Tally
{{G3 + G4 + G5 + G7 + G8, 1}, {P2, 1}, {G1 + G2 + G4 + G5 + G6 + G8, 2}, {P3, 1}, {G1 + G3 + G4 + G5 + G7 + G8, 1}, {P5, 1}, {P6, 1}}
$$\left(
\begin{array}{cc}
\text{G3}+\text{G4}+\text{G5}+\text{G7}+\text{G8} & 1 \\
\text{P2} & 1 \\
\text{G1}+\text{G2}+\text{G4}+\text{G5}+\text{G6}+\text{G8} & 2 \\
\text{P3} & 1 \\
\text{G1}+\text{G3}+\text{G4}+\text{G5}+\text{G7}+\text{G8} & 1 \\
\text{P5} & 1 \\
\text{P6} & 1 \\
\end{array}
\right)$$
G1 + G2 + G4 + G5 + G6 + G8 appears twice we can collect that term:
Collect[simplified , G1 + G2 + G4 + G5 + G6 + G8]
$$(\text{P3}+\text{P6}) (\text{G1}+\text{G2}+\text{G4}+\text{G5}+\text{G6}+\text{G8})+\text{P5} (\text{G1}+\text{G3}+\text{G4}+\text{G5}+\text{G7}+\text{G8})+\text{P2} (\text{G3}+\text{G4}+\text{G5}+\text{G7}+\text{G8})$$
Collect
, a direct approach could beCollect[expr /. P3 -> -P6 + s, {P5, P2, s}] /. s -> (P3 + P6)
$\endgroup$V:= indets(ex)[]: codegen[optimize](unapply(ex, [V]), tryhard)(V);
but I do not know how to reproduce this in Mathematica as it uses special Maple package.idents
finds the variables in expression. can reproduceindets
but not the optimize command. $\endgroup$Collect[expr,{P2,P3,P5,P6}]//Collect[#,#[[2,1]]]&
$\endgroup$Collect[expr,{P2,P3,P5,P6}]//Collect[#,#[[1]]&/@List@@#]&
gives aLeafCount
of 25 $\endgroup$