4

I have a symbolic expression in MATLAB with a == operator that I can use in solve(). What I want is to separate out the left hand side and the right hand side of the expression into two separate symbolic expressions.

For example:

expr = sym('[1-x^2==2*y; 1+x^2==x+y]');
side1 = lhs(expr);        % returns side1 = [1-x^2; 1+x^2];

of course my expressions are far more complicated, and it is always vector or matrix form.

Workaround 1 I can use the MuPAD built-in function lhs() but I wanted to know if it possible to do this using only MATLAB functions and I want to make it work for vectors of expressions and not just one value.

This is what I have so far that works as expected. Maybe the result filling can be vectorized somehow by using : but I have not manage to get it working.

function [ r ] = lhs( expr )
%LHS Returns the left hand side an expression
%   LHS(sym('[1-x^2==2*y'; 1+x^2==x+y]')) = [1-x^2; 1+x^2]

  cmd = @(e)['lhs(',char(e),')'];
  [m,n] = size(expr);
  r = sym(zeros(m,n));
  for i=1:m
      for j=1:n
          r(i,j) = evalin(symengine, cmd(expr(i,j)));
      end
  end  
end
John Alexiou
  • 28,472
  • 11
  • 77
  • 133

3 Answers3

4

Starting R2017a, use "lhs" and "rhs" as

syms x
expr = [1-x^2==2*y; 1+x^2==x+y];
lhsExpr = lhs(expr)
lhsExpr =
 1 - x^2
 x^2 + 1


rhsExpr = rhs(expr)
rhsExpr =
  2*y
 x + y
Karan Gill
  • 160
  • 5
  • Anything else on your Symbolic Math Toolbox wishlist? :) Functionality has been added every release. See the [release notes](https://www.mathworks.com/help/symbolic/release-notes.html). – Karan Gill Mar 13 '17 at 13:49
  • Thank you. I love the live worksheets but I am peeved that I cannot enter greek symbols or unicode for variables in order to display pretty. Image if you could input something like `'\gamma' = 1` or better yet `γ = 1` directly. In mupad you can enter `'γ'=1` for this – John Alexiou Mar 13 '17 at 15:30
  • 1
    You're correct. Unfortunately in MATLAB, variable names cannot have Greek letters. I don't see a fix for that but I'll pass your feedback on. It would definitely be nice to have. – Karan Gill Mar 13 '17 at 22:56
  • Sorry that this is the proper forum but there is another issue with Matlab (at least earlier versions). If I have a system of equations stored in a symbolic vector (example `expr = [ 2*x-y==1, x+3*y==2]`) and I want to solve it I should be able to just type `sol = solve(expr, [x,y])`. I think now I need to type `sol = solve(expr(1),x,expr(2),y)`. Maybe it should accept a cell of variables like `subs`, but have the quick form `solve(expr,vars)`. – John Alexiou Mar 13 '17 at 23:36
  • The command `S = solve(expr,[x y])` works with `S.x = 5/7` and `S.y = 3/7`. Is that what you expected? – Karan Gill Mar 16 '17 at 11:33
  • Yes, thank you. The help for solve in previous versions mentions `solve(eq1,eq2,eq3,var1,var2,var3)` type of arrangement. I checked with `2016b` and the functionality I mentioned is there verbatim `solve(eqns,vars)`. – John Alexiou Mar 16 '17 at 12:38
  • @ja72 No Greek letters for variable input, but as you probably know, you do get them in the output if your symbolic variables have names like `gamma` or `alpha_i`. – Christopher Creutzig Apr 04 '17 at 06:36
  • @ChristopherCreutzig I did not know that. That is great. I am a user of the old Derive program which converted `alpha` -> `α` and such. – John Alexiou Apr 04 '17 at 12:45
2
expr = sym('[1-x^2==2*y; 1+x^2==x+y]');
lr = children(expr);
lr{1}

ans =

[ 1 - x^2, 2*y]

Note that this is more robust than EitanT's string manipulation for a simple reason: Your left- and right-hand sides may contain equals-signs themselves.

Christopher Creutzig
  • 8,656
  • 35
  • 45
  • Maybe but I get `??? Undefined function or method 'children' for input arguments of type 'sym'.` – John Alexiou Mar 16 '13 at 21:22
  • I'd have to search when `sym/children` was introduced. Probably around 2012a or something like that? – Christopher Creutzig Mar 16 '13 at 21:24
  • Thanks for the hint. I will check at work and award question if it works. – John Alexiou Mar 16 '13 at 21:26
  • I can certainly argue about the robustness of my solution, but yours definitely deserves +1. – Eitan T Mar 18 '13 at 13:01
  • @EitanT: I mean no disrespect against you or your code – it's just that some symbolic expressions contain `=` signs in their outputs, and that means breaking in the textual representation requires a lot of work, which you rightly didn't include. Besides, when expressions get larger, a round-trip through MATLAB strings brings performance problems, in my experience. – Christopher Creutzig Mar 18 '13 at 13:06
  • IMHO, the only thing to be changed in my solution is the [><=]* part in the regular expression, which is fairly simple. But don't worry about it, no offense taken. Your solution is definitely better. – Eitan T Mar 18 '13 at 13:10
0

I'm thinking regular expressions, so here's my shot at this.

Let's say you have a symbolic expression expr, for instance:

expr = sym('[1-x^2==2*y; 1+x^2==x+y]')

Since it cannot be assumed to be a scalar expression, the first step would be splitting it into sub-elements, and converting each one to a string, like so:

C = arrayfun(@(n)char(expr(n)), 1:numel(expr), 'Uniform', false)

(I'm not using a simple char(expr) conversion because it adds the matrix([[...]]) syntax).

Now we use a regular expression search-and-replace to extract the LHS:

C = arrayfun(@(n)char(expr(n)), 1:numel(expr), 'Uniform', false)
C = regexprep(C, '([^><=]*)(?:[><=]*)(.*)', '$1')  %// $1 for lhs, $2 for rhs

And then concatenate the elements back into a string and convert it into a symbolic expression:

str = sprintf('%s,', C{:})
result = reshape(sym(str(1:end - 1)), size(expr))

Voila.


Here are copy-paste friendly lhs and rhs functions:

function y = lhs(x)
    C = arrayfun(@(n)char(x(n)), 1:numel(x), 'Uniform', false);
    C = regexprep(C, '([^><=]*)(?:[><=]*)(.*)', '$1');
    str = sprintf('%s,', C{:});
    y = reshape(sym(str(1:end - 1)), size(x));

function y = rhs(x)
    C = arrayfun(@(n)char(x(n)), 1:numel(x), 'Uniform', false);
    C = regexprep(C, '([^><=]*)(?:[><=]*)(.*)', '$2');
    str = sprintf('%s,', C{:});
    y = reshape(sym(str(1:end - 1)), size(x));

Note that the only difference between lhs and rhs are the replaced tokens in regexprep.
Using $1 extracts the left-hand side, and using $2 extracts the right-hand side.

Eitan T
  • 32,660
  • 14
  • 72
  • 109
  • The function does to have scope to `expr` so the `size(expr)` is not valid. I replaced it with `size(x)` and I got an error. `Dimensions must be real nonnegative`. – John Alexiou Jan 22 '13 at 19:51
  • Sorry, typing error. That should be `x` of course. I find it strange that you get an error for `size(x)`... what are you passing for `x`? – Eitan T Jan 22 '13 at 20:32
  • `x=[ xp_2 == xp_1 + qp_2*(rx*cos(th_1) - ry*sin(th_1)) - thp_1*(cos(th_1)*(n_2_y + q_2*ry) + sin(th_1)*(n_2_x + q_2*rx)) ; yp_2 == yp_1 + qp_2*(ry*cos(th_1) + rx*sin(th_1)) + thp_1*(cos(th_1)*(n_2_x + q_2*rx) - sin(th_1)*(n_2_y + q_2*ry)); 0 == 0]` and try `rhs(x)` – John Alexiou Jan 22 '13 at 20:47
  • Oh, apparently there was a mistake in the regular expression, so it occasionally failed. It's supposedly fixed now, please try it. – Eitan T Jan 24 '13 at 18:41
  • If I input a vector like I need I get an error: `Error using mupadmex Error in MuPAD command: Dimensions must be real nonnegative integers. Error in sym/reshape (line 35) ySym = mupadmex('symobj::reshape',x.s,args{:}); Error in lhs2 (line 8) r = reshape(sym(str(1:end - 1)), size(x));` – John Alexiou Jan 24 '13 at 19:35
  • The previous regular expression resulted failed to extract the RHS and returned an empty cell `C`, hence the dimension-related error with `reshape`. Did you try that with my fixed regexp pattern? It's working for me. – Eitan T Jan 24 '13 at 19:45
  • I am awarding the answer, because the problem is with MATLAB and not the response author. Thanks for your time. – John Alexiou Jan 24 '13 at 19:49
  • I mean that mathworks should have included the `rhs()` and `lhs()` functions when they sell you the symbolic toolbox. – John Alexiou Jan 25 '13 at 04:44