Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transforming expressions into polynomials #920

Closed
paulobuchsbaum opened this issue Aug 12, 2017 · 5 comments
Closed

Transforming expressions into polynomials #920

paulobuchsbaum opened this issue Aug 12, 2017 · 5 comments

Comments

@paulobuchsbaum
Copy link
Contributor

paulobuchsbaum commented Aug 12, 2017

Algebrite is an alternative javascript package for math.

In Algebrite there are a set of functions very useful to convert a expression in a polynomial equation, that can be solved by some numeric or algebric method, some specific to polynomial form, like the reputed Jenkins-Traub method.

expand - deletes all parentheses from an expression, except, of course, function parentheses.

Example:

var a= require('algebrite'); 
s='(2x^2-5)*(3x^2-5x+2)';
u=a.expand(s);   //  6 x^4 - 10 x^3 - 11 x^2 + 25 x - 10

rationalize, numerator, denominator - the first function rationalizes a expression converting in a numerator and denominator, both get without divide operators. Related functions are numerator that returns the upper part and denominator that returns the lower part of an expression.

var a= require('algebrite');  
s =  "3x/(x^2+2)  -  2/(x-1)  - 4";
t=a.rationalize(s);   //  (-4 x^3 + 5 x^2 - 11 x + 4) / ((x - 1) (x^2 + 2))
t=a.numerator(s); // -4 x^3 + 5 x^2 - 11 x + 4
t=Algebrite.denominator(s);  // x^3 - x^2 + 2 x - 2

The numerator function can be used to reduces an general expression to a direct polynomial equation form. So one can solve the resulting equation for any method.

However, mathjs is much sturdier than algebrite.

For instance,

s = "(2x-5)*(3x-3)*(x+4) / (x^2-5) + (x-5)*(2x-3)*(2x+5) / (x^2+3x-2) + 2x - 3";

It's enough to use numerator or any other function to let algebrite nuts. The same does not happens in mathjs.

It would be great if these functions (expand, rationalize, numerator, denominator) existed in mathjs

@josdejong
Copy link
Owner

Thanks for your suggestions, that sounds like useful additions.

If anyone is interested in implementing one of the proposed functions please drop a message here.

@paulobuchsbaum
Copy link
Contributor Author

paulobuchsbaum commented Aug 15, 2017

@josdejong, by using simplify, I guess that I've got something very close to proposed expand and rationalize function.

It makes sense?

var m=requires("mathjs");

var defRules = m.simplify.rules;  // default rules
defRules.splice(15,4);  // Delete the rules that don't match with expand and rationalize. 
defRules.splice(12,2); 
defRules.splice(4,3);  
defRules.splice(2,1);  

// new rules
var rules =[ { l:'(n1/n2)/n3', r:'n1/(n2*n3)' } ,
                { l:'n1/(n2/n3)', r:'(n1*n3)/n2'} ,
                { l:'(n1+n2)*(n3+n4)', r:'(n1*n3 + n1*n4 + n2*n3 + n2*n4)' } ,
                { l:'(n1-n2)*(n3-n4)', r:'(n1*n3 - n1*n4 - n2*n3 + n2*n4)' } ,
		{ l:'(n1+n2)*(n3-n4)', r:'(n1*n3 - n1*n4 + n2*n3 - n2*n4)' } ,
		{ l:'(n1-n2)*(n3+n4)', r:'(n1*n3 + n1*n4 - n2*n3 - n2*n4)' } ,
                { l:'(-n1+n2)*(n3+n4)', r:'(-n1*n3 - n1*n4 + n2*n3 + n2*n4)' } ,
                { l:'(-n1-n2)*(n3-n4)', r:'(-n1*n3 + n1*n4 - n2*n3 + n2*n4)' } ,
		{ l:'(-n1+n2)*(n3-n4)', r:'(-n1*n3 + n1*n4 + n2*n3 - n2*n4)' } ,
		{ l:'(-n1-n2)*(n3+n4)', r:'(-n1*n3 - n1*n4 - n2*n3 - n2*n4)' } ,
		{ l:'(n1+n2)*n3', r:'(n1*n3 + n2*n3)' },
 	        { l:'(n1-n2)*n3', r:'(n1*n3 - n2*n3)' },
 		{ l:'(-n1+n2)*n3', r:'(-n1*n3 + n2*n3)' },
 	        { l:'(-n1-n2)*n3', r:'(-n1*n3 - n2*n3)' },
 		{ l:'(-n1+n2)*-n3', r:'(n1*n3 - n2*n3)' },
 	        { l:'(-n1-n2)*-n3', r:'(n1*n3 + n2*n3)' },
 		{ l:'(n1+n2)*-n3', r:'-(n1*n3 - n2*n3)' },
	        { l:'(n1-n2)*-n3', r:'-(n1*n3 + n2*n3)' },
	        { l: 'c1*n + c2*n', r:'(c1+c2)*n'} ,
 		{ l: 'c1*n + -c2*n', r:'(c1-c2)*n'} ,
 		{ l: 'c1*n - c2*n', r:'(c1-c2)*n'} ,
		{ l: '-c1*n + c2*n', r:'(c2-c1)*n'} ,
 		{ l: '-c1*n + -c2*n', r:'(-c1-c2)*n'} ,
                { l: '-c1*n - c2*n', r:'(-c1-c2)*n'} ,
        	{ l: '(n1^n2)^n3', r:'n1^(n2*n3)'} ,
                { l: 'n1-v*-c', r:'n1+c*v'} ,
                { l: 'n1+-c*v', r:'n1-c*v'} ,
		{ l: 'v*c', r:'c*v'} ,
        	{ l: 'v*-c', r:'-c*v'} ,
		{ l: 'c*-v', r:'-c*v'} ,
		{ l: 'c+v', r:'v+c'} ,
  		{ l: '-c+v', r:'v-c'} ,
 		{ l: 'n1-(n2+n3)', r:'n1+(-n2-n3)'}, 
 		{ l: 'n1-(n2-n3)', r:'n1+(-n2+n3)'}, 
 	        { l:'n1/n2 + n3/n4', r:'(n1*n4 + n3*n2)/(n2*n4)' } ,
                { l:'n1/n2 + n3', r:'(n1 + n2*n3)/n2' },
	        { l:'n1/n2 - n3/n4', r:'(n1*n4 - n3*n2)/(n2*n4)' } ,
                { l:'n1/n2 - n3', r:'(n1 - n2*n3)/n2' }			 ];
newRules = defRules.concat(rules);  // append new rules

var expr='3x/(x^2+2)  -  2/(x-1)  - 4';
console.log(m.simplify(expr,newRules).toString());

So the expression:

3x/(x^2+2) - 2/(x-1) - 4

results in:

(3 * x ^ 2 - 3 * x - 4 * x ^ 3 - 8 * x + -2 * x ^ 2 - 4 + 4 * x ^ 2 + 8) / (x ^3 - x ^ 2 + 2 * x - 2)

The root node is the operator divide (/), the whole expression is equivalent to rationalize, the left branch is the numerator and the right branch is denominator. The only minor question is that the expression don't group terms with the same exponent and don't solve every following redundant operators (+ -), because they don't are adjascent in the expression tree.

To solve 3x/(x^2+2) - 2/(x-1) - 4=0 is similar to solve the equation with the numerator part -4x^3 + 5x^2 -11x + 4=0 (when one simplify the above expression), provided that the denominator is not 0.

@josdejong
Copy link
Owner

He, that looks good, basically just a different set of rules with simplify. That's neat.

@paulobuchsbaum
Copy link
Contributor Author

@josdejong, thanks.

I've added 2 additional rules in order to handle with more complex polinomial expressions:

newRules = [  ...
{ l:'(n1/n2)/n3', r:'n1/(n2*n3)' } ,
{ l:'n1/(n2/n3)', r:'(n1*n3)/n2'} ....]

So

var expr = '2x/( (2x-1) / (3x+2) ) - 5x/ ( (3x+4) / (2x^2-5) ) + 3';   
console.log(m.simplify(expr,newRules).toString());

Results in

(36 * x ^ 2 + 18 * x ^ 3 + 16 * x + -20 * x ^ 4 - 25 * x + 18 * x ^ 2 - 9 * x +
24 * x - 12) / (6 * x ^ 2 - 3 * x + 8 * x - 4)

With few lines one can separate the numerator and denominator:

var hasDivide = (node.type==='OperatorNode'  &&  node.op==="/");  // Has divide operator
var nume, deno;
if (hasDivide)
   nume = node.args[0]
   deno = node.args[1];
} else  {
   nume = node; 
   deno = m.parser('1);
}

@josdejong
Copy link
Owner

sounds good! Can you work this out in a pull request as a new function with tests and docs?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants