Convert matlab symbol to array of products

228 views Asked by At

Can I convert a symbol that is a product of products into an array of products?

I tried to do something like this:

syms A B C D;
D = A*B*C;
factor(D);

but it doesn't factor it out (mostly because that isn't what factor is designed to do).

ans =
A*B*C

I need it to work if A B or C is replaced with any arbitrarily complicated parenthesized function, and it would be nice to do it without knowing what variables are in the function.

For example (all variables are symbolic):

D = x*(x-1)*(cos(z) + n);
factoring_function(D);

should be: [x, x-1, (cos(z) + n)]

It seems like a string parsing problem, but I'm not confident that I can convert back to symbolic variables afterwards (also, string parsing in matlab sounds really tedious).

Thank you!

2

There are 2 answers

1
Luis Mendo On BEST ANSWER

Use regexp on the string to split based on *:

>> str = 'x*(x-1)*(cos(z) + n)';
>> factors_str = regexp(str, '\*', 'split')
factors_str = 
    'x'    '(x-1)'    '(cos(z) + n)'

The result factor_str is a cell array of strings. To convert to a cell array of sym objects, use

N = numel(factors_str);
factors = cell(1,N); %// each cell will hold a sym factor
for n = 1:N
    factors{n} = sym(factors_str{n});
end
0
Chet On

I ended up writing the code to do this in python using sympy. I think I'm going to port the matlab code over to python because it is a more preferred language for me. I'm not claiming this is fast, but it serves my purposes.

# Factors a sum of products function that is first order with respect to all symbolic variables
# into a reduced form using products of sums whenever possible.
# @params orig_exp     A symbolic expression to be simplified
# @params depth        Used to control indenting for printing
# @params verbose      Whether to print or not
def factored(orig_exp, depth = 0, verbose = False):
  # Prevents sympy from doing any additional factoring
  exp = expand(orig_exp)
  if verbose: tabs = '\t'*depth
  terms = []

  # Break up the added terms
  while(exp != 0):
    my_atoms = symvar(exp)
    if verbose: 
      print tabs,"The expression is",exp
      print tabs,my_atoms, len(my_atoms)

    # There is nothing to sort out, only one term left
    if len(my_atoms) <= 1:
      terms.append((exp, 1))
      break

    (c,v) = collect_terms(exp, my_atoms[0])
    # Makes sure it doesn't factor anything extra out
    exp = expand(c[1])
    if verbose: 
      print tabs, "Collecting", my_atoms[0], "terms."
      print tabs,'Seperated terms with ',v[0], ',  (',c[0],')'

    # Factor the leftovers and recombine
    c[0] = factored(c[0], depth + 1)
    terms.append((v[0], c[0]))


  # Combines trivial terms whenever possible
  i=0
  def termParser(thing): return str(thing[1])
  terms = sorted(terms, key = termParser) 

  while i<len(terms)-1:
    if equals(terms[i][1], terms[i+1][1]):
      terms[i] = (terms[i][0]+terms[i+1][0], terms[i][1])
      del terms[i+1]
    else:
      i += 1

  recombine = sum([terms[i][0]*terms[i][1] for i in range(len(terms))])
  return simplify(recombine, ratio = 1)