9
$\begingroup$

I am trying to write a function to convert an expression of the form

$$\alpha x_1^{n_1} x_2^{n_2} ... x_N^{n_N}$$

(with some coefficient $\alpha\in\mathbb{R}$) into a list of indices and powers

$$\{\{1,n_1\},\{2,n_2\},...,\{N,n_N\}\}.$$

for expressions of varying length (number of products $x_i^{n_i}$).

My natural choice for this was the Cases function with pattern matching. Something along the lines of, for a three-term product,

pattern = α_ x{i_}^ni_. x{j_}^nj_. x{k_}^nk_.
Cases[expr, pattern -> {{i,ni},{j,nj},{k,nk}}],

where I write x{i_} to denote Subscript[x,i_].

My question is how to match a product of arbitrary length. Is there a way to write a pattern to match

$$\alpha \prod_j x_{i_j}^{n_{i_j}}$$

for an arbitrary $j$ and $i_j\in\mathbb{Z}$ not predefined.

Alternatively, if we assume the maximum $j$ is known, is there a way I can write a single pattern (along the lines of pattern above) such that each term is optional? I.e. modify pattern to also match 2-term and 1-term products.

$\endgroup$
11
  • $\begingroup$ I should comment that although this question is primarily about pattern matching, I am not tied to this approach if it is the 'wrong' way to attack problems like this in Mathematica $\endgroup$ Jul 22, 2021 at 11:31
  • $\begingroup$ Can you provide the Mathematica code corresponding to the expression you have? Is it written as a Product, as a Times, as a Dot, or what? Your first equation is Times, your pattern has Dots, but you ask about a Product $\endgroup$
    – bRost03
    Jul 22, 2021 at 14:46
  • $\begingroup$ list =Array[x, 5]; mono = a * x[1]^q1 x[2]^q2 x[3]^q3 x[4]^1 x[5]^0 ; Cases[mono *Apply[Times,list]^m // PowerExpand, x_^n_ :> {x, n}] /. m -> 0 $\endgroup$
    – I.M.
    Jul 22, 2021 at 15:09
  • $\begingroup$ @bRost03, yes, I accept the way I had written the pattern was confusing - especially as in a rush I had used latex-style x_i to denote a subscript. The dots in the pattern should read x^in_. - not for a Dot but for an optional part of the pattern to include x^1 terms. I've corrected the pattern now. $\endgroup$ Jul 22, 2021 at 15:47
  • $\begingroup$ I use product in the mathematical sense as interchangeable with times, not because the expression used a Product function. I believe they will use Times. $\endgroup$ Jul 22, 2021 at 15:48

2 Answers 2

6
$\begingroup$

Update: "to apply the Times/Power -> List replacement only to powers of $x_i$"

expr2 = -2 a^2 expr

enter image description here

expr2 /. m -> 5 /. Times[a___, p : (Power[_x, _] ..)] :> 
  {Times @ a, Sequence @@ ({p} /. Power -> List /. x -> Identity)}
{-2 a^2 α, {1, n[1]}, {2, n[2]}, {3, n[3]}, {4, n[4]}, {5, n[5]}}

Original answer:

expr = α Product[Power[x[i], n[i]], {i, 1, m}]

enter image description here

With numeric m you can simply replace Times and Power with List:

Rest[expr /. m -> 5 /. Times | Power -> List] /. x -> Identity
 {{1, n[1]}, {2, n[2]}, {3, n[3]}, {4, n[4]}, {5, n[5]}}

Also

Block[{Power = List, Times = List}, 
   Transpose @ First @ Rest[expr /. m -> 5]] /. x -> Identity
 {{1, n[1]}, {2, n[2]}, {3, n[3]}, {4, n[4]}, {5, n[5]}}

and

First @ Cases[expr /. m -> 5, 
   Times[α, pat : __Power] :> ReplaceAll[x -> Identity] @* List @@@ {pat}, All]
{{1, n[1]}, {2, n[2]}, {3, n[3]}, {4, n[4]}, {5, n[5]}}
$\endgroup$
2
  • $\begingroup$ @CaileanWilkinson, please see the update. $\endgroup$
    – kglr
    Jul 22, 2021 at 16:24
  • $\begingroup$ Thanks! Sorry, I removed the comment asking for that when I realised I could just 'correct' for it by piecing the coefficient back together afterwards. Didn't realise you'd already seen it / started on an answer. Seems like a nicer approach to just not break up the coefficient in the first place as per your update. Thanks again! $\endgroup$ Jul 22, 2021 at 16:34
3
$\begingroup$

If we can assume the variables are always in the form $x[i]$ then we have

f = With[{v = #}, {First[#], Exponent[v, #]} & /@ Cases[v, x[_], ∞]] &;

EDIT: for variables in the form $x_i$ just use f = With[{v = #}, {# /. Subscript[x, a__] -> a, Exponent[v, #]} & /@ Cases[v, Subscript[x,_],∞]] &;

This handles the initial coefficient more robustly than the other answer. The idea is the Cases identifies the variables and the Map gives back the format you like. So something like

test=8a^2 x[1]^q1 x[2]^q2 x[3]^q3 x[4]
f@test

gives

{{1,q1},{2,q2},{3,q3},{4,1}}

Note that the coefficient is filtered automatically and it works for an exponent of 1.

If we cannot assume the variables are always in the form $x_i$ and, like in a now deleted comment, the coefficient may be something like $8a^2$, then it's obviously impossible since we can't know if $a$ belongs to the coefficient or the variables. That aside, we can do something like

f[exp_, cof_] := Block[{exp2, vars, ind},
  exp2 = (exp/cof) /. Times -> List;
  vars = exp2 /. Times -> List /. Power[a_, b_] -> a;
  MapIndexed[(ind = First[#2]; {ind, Exponent[#1, vars[[ind]]]}) &, exp2]]

which takes in the coefficient as an argument. Again

test=8a^2 x[1]^q1 x[2]^q2 x[3]^q3 x[4]
f@test

gives

{{1,q1},{2,q2},{3,q3},{4,1}}

as desired.

If you don't know the coefficient apriori, you can put in 1 or simply use

f[exp_] := Block[{Times=List, vars, ind},
  vars = exp /. Power[a_, b_] -> a;
  MapIndexed[(ind = First[#2]; {ind, Exponent[#1, vars[[ind]]]}) &, exp]]

then filter the coefficient later like you said you can do in the comments.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.