17

I can't believe this hasn't been asked before but I can't find anything.

Is there a way to convince Simplify or FullSimplify to extract common factors from matrices as it does from sums?

  • Exhibit A:

    {a/(2 c), b/(2 c), d/(2 c), e/(2 c)} // FullSimplify // FullSimplify
    

    gives

    {a/(2 c), b/(2 c), d/(2 c), e/(2 c)}
    

    In reality, I have a 2x2 matrix, but the result is effectively the same. A solution to my problem should ideally not depend on the dimensions/layout of the (potentially nested) list.

  • Exhibit B:

    a/(2 c) + b/(2 c) + d/(2 c) + e/(2 c) // FullSimplify
    

    gives

    (a + b + d + e)/(2 c)
    

I did see this related question, but I'm just asking about rearranging, I don't actually need access to the polynomial GDC in a separate variable or anything, so I was wondering if this was possible with Simplify or FullSimplify somehow.

Martin Ender
  • 8,774
  • 1
  • 34
  • 60
  • {a,b,c} x is not a "stable form", it would immediately evaluate to {a x, b x, c x}. The question is interesting, I'm just mentioning that it's not possible to keep the expression in this form. You'd have to store {a,b,c} and x separately. – Szabolcs Jan 04 '15 at 17:48
  • @Szabolcs Could you link me to some background reading about "stable forms"? – Martin Ender Jan 04 '15 at 17:49
  • I just made that term up, you won't find much by searching for it. All I meant to say is that it evaluates further immediately. A similar example would be x+x, which evaluates immediately to 2x, or Sin[Pi] which evaluates to 0. – Szabolcs Jan 04 '15 at 17:51
  • @Szabolcs I see. I'd still be interested, what's going on under the hood. But I guess that means my only option is indeed to flatten the input and feed it to PolynomialGDC. Feel free to close as duplicate. – Martin Ender Jan 04 '15 at 17:52
  • You can actually keep it in that form using Hold[{a, b} x] or HoldForm[{a,b} x], but held expressions aren't very suitable for algebraic manipulation. HoldForm is useful though for just displaying the expression in certain form that is easier for us humans to parse. – Szabolcs Jan 04 '15 at 17:55
  • I don't know much about the internals of Simplify and FullSimplify, but I vaguely recall that they make their decisions about what forms are "simplest" based on some sort of cost function, and that there have been previous questions asking about how to manipulate the cost function used by Simplify in order to tailor the output to a particular form that was considered 'optimal' by the question-asker. Is there any way that cost-function modification could be applied here, or is that not possible? – DumpsterDoofus Jan 04 '15 at 18:09
  • @DumpsterDoofus You can manipulate both the ComplexityFunction (which wouldn't be necessary) and the TransformationFunctions (which would), but because the result the OP was looking for auto-evaluates, it wouldn't be of any help. Theoretically you could include a transformation function that applies Hold after extracting the common factor, but since Simplify won't be able to further transform held expressions, and since the factor extraction would need to be done manually anyway, it makes little sense to integrate this into Simplify. – Szabolcs Jan 04 '15 at 18:21
  • Perhaps leaving an inactive expression might be useful? facMat[m_] := m /. mat : IgnoringInactive[{{f_ _ ..} ..}?MatrixQ] :> Inactivate[f (mat /. IgnoringInactive[f i_] :> i), Times] – Rojo Jan 05 '15 at 11:48
  • A good application of such a solution would be to vectorize expressions so that they could be computed much more quickly. – remo Apr 13 '17 at 14:25

3 Answers3

7

A toy approach is as follows:

 FactorizeCommonFactor[tensor_?TensorQ] := 
  Block[{GCDListv, cfactor, tenfact, tenfactmf, facten},
   cfactor := PolynomialGCD @@ Flatten@tensor;
   tenfact := (1/cfactor)*tensor;
   tenfactmf := MatrixForm[tenfact];
   Piecewise[{{Return[{"Common Factor" -> cfactor, tenfact, 
        Inactivate[cfactor*tenfactmf]}], 
      cfactor =!= 1}, {Print[
       "The tensor cannot be expressed as a multiple of another
tensor"], cfactor === 1}}];
   ];

Test:

enter image description here

E. Chan-López
  • 23,117
  • 3
  • 21
  • 44
5

I am not seriously proposing this as a solution but it was fun to play with.

Block[{Plus},
  Simplify[
    Plus @@ {a/(2 c), b/(2 c), d/(2 c), e/(2 c)}
  ] /. Plus -> Defer@*List
]
{a, b, d, e}/Sqrt[2]

I do not recommend this in practice as Plus is a very low level operator and it is not clear what Block actually accomplishes.

  1. As noted elsewhere(1)(2) heads like Plus and Equal have rules that fire on numbers and packed arrays even while Blocked. One cannot be sure that Simplify does not also have specific handling of Plus that Block does not affect therefore this seem unreliable.

  2. Plus is such a basic function that it is probably used in a lot of internal code that we do not wish to affect, and Block is an indiscriminate tool.

Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • @Martin Sorry, I am increasingly forgetful, and recently I've been busy, so I do this a lot I'm afraid. :-/ – Mr.Wizard Apr 14 '17 at 06:35
  • This post is from many years ago, is there something more about the extraction of common factors in vectors or matrices? – E. Chan-López Dec 03 '21 at 03:26
4

arrayFactorList[array] factors an array à la FactorList, which can then be put in the form Inactive[Times][scalarCommonFactor, array] (see examples):

arrayFactorList[array_?ArrayQ] :=
 FactorList@Total[
    MapIndexed[# \[FormalCapitalA][#2] &, array, {ArrayDepth[array]}],
     ArrayDepth[array]] /. \[FormalCapitalA][i_] :> 
   SparseArray[{i -> 1}, Dimensions[array]]

Example 1:

arrayFactorList[{a/(2 c), b/(2 c), d/(2 c), e/(2 c)}]
GroupBy[Power @@@ %, Head[#] === SparseArray &] //
   Map@Apply@Times //
  Apply@Inactive@Times //
 Normal

Mathematica graphics

Example 2:

arrayFactorList[{{1/Sqrt[2]}, {0}, {0}, {1/Sqrt[2]}}]
GroupBy[Power @@@ %, Head[#] === SparseArray &] //
   Map@Apply@Times //
  Apply@Inactive@Times //
 Normal

Mathematica graphics

Michael E2
  • 235,386
  • 17
  • 334
  • 747