btm-expressions
Version:
BTM (bowtie-math) is a math object model to enable parsing of mathematical expressions into a tree structure that can be manipulated, evaluated, and compared.
215 lines (193 loc) • 10.2 kB
JavaScript
/*!
* menv JavaScript Library v@VERSION
* https://github.com/dbrianwalton/menv
*
* Copyright D. Brian Walton
* Released under the MIT license (https://opensource.org/licenses/MIT)
*
* Date: @DATE
*/
/* *********************************************************************************
** Dealing with identities and reductions.
********************************************************************************* */
import {MENV, exprType, exprValue } from "./BTM_root.js";
class Identity {
constructor(refExpr, eqExpr, description, isValid, idNum) {
this.refExpr = refExpr;
this.eqExpr = eqExpr;
this.description = description;
this.isValid = isValid;
this.isActive = true;
this.idNum = idNum;
}
}
class Match {
constructor(testRule, bindings) {
// Find unbound variables.
var allVars = testRule.eqExpr.dependencies(),
missVars = [];
for (var j in allVars) {
if (typeof bindings[allVars[j]] == 'undefined') {
missVars.push(allVars[j]);
}
}
for (var j in missVars) {
bindings[missVars[j]] = "input"+(+j+1)+"";
}
this.subExpr = testRule.eqExpr.compose(bindings);
this.subTeX = this.subExpr.toTeX();
this.subStr = this.subExpr.toString();
this.name = testRule.description;
if (this.subExpr.type == exprType.binop && this.subExpr.valueType == exprValue.bool) {
this.equation = testRule.refExpr.toTeX() + " \\iff " + testRule.eqExpr.toTeX();
} else {
this.equation = testRule.refExpr.toTeX() + "=" + testRule.eqExpr.toTeX();
}
this.bindings = bindings;
this.numInputs = missVars.length;
this.ruleID = testRule.idNum;
}
}
export function newRule(menv, reductionList, equation, description, isValid, useOneWay, constraints) {
var exprFormulas = equation.split('==');
if (exprFormulas.length != 2) {
console.log("Invalid equation in identity list: " + equation);
} else {
for (var refID=0; refID <= 1; refID++) {
if (refID == 1 && typeof useOneWay != 'undefined' && useOneWay) {
break;
}
var identity;
var refExpr = menv.parse(exprFormulas[refID],"formula");
var eqExpr = menv.parse(exprFormulas[1-refID],"formula");
var numVars = refExpr.dependencies().length;
var allRefExpr = [exprFormulas[refID]];
// this is a big slow down, so just make sure each rule is written in multiple ways.
// var allRefExpr = refExpr.allStringEquivs();
var uniqueExpr = [];
for (var i in allRefExpr) {
var nextExpr = menv.parse(allRefExpr[i],"formula");
var isNew = true;
for (var j in uniqueExpr) {
var bindings = uniqueExpr[j].match(nextExpr, {});
if (bindings !== null) {
isNew = false;
}
}
if (isNew) {
var ruleID = reductionList.length+1;
identity = new Identity(nextExpr, eqExpr, description, isValid, ruleID);
reductionList.push(identity);
uniqueExpr.push(nextExpr);
}
}
}
}
}
// Disable a rule in the list.
export function disableRule(menv, reductionList, equation) {
// Match only on refExpr.
var exprFormulas = equation.split('==');
var refExpr, eqExpr;
if (exprFormulas.length > 2) {
console.log("Invalid equation in identity list: " + equation);
return;
} else {
refExpr = menv.parse(exprFormulas[0],"formula");
}
for (var i in reductionList) {
var testRule = reductionList[i];
var bindings = testRule.refExpr.match(refExpr, {})
if (bindings !== null) {
reductionList[i].isActive = false;
}
}
}
/* *******************
** Given a list of reduction rules and a given expression,
** test each reduction rule to see if it matches the structure.
** Create an array of new objects with bindings stating what
** substitutions are necessary to make the matches.
******************* */
export function findMatchRules(reductionList, testExpr, doValidate) {
var matchList = [];
var i, testRule;
for (i in reductionList) {
testRule = reductionList[i];
var bindings = testRule.refExpr.match(testExpr, {})
if (testRule.isActive && bindings !== null) {
var match = new Match(testRule, bindings);
matchList.push(match);
}
}
return(matchList);
}
export function defaultReductions(menv) {
var reduceRules = new Array();
newRule(menv, reduceRules, '0+x==x', 'Additive Identity', true, true);
newRule(menv, reduceRules, 'x+0==x', 'Additive Identity', true, true);
newRule(menv, reduceRules, '0-x==-x', 'Additive Inverse', true, true);
newRule(menv, reduceRules, 'x-0==x', 'Additive Identity', true, true);
newRule(menv, reduceRules, 'x+-(0)==x', 'Additive Identity', true, true);
newRule(menv, reduceRules, '0*x==0', 'Multiply by Zero', true, true);
newRule(menv, reduceRules, 'x*0==0', 'Multiply by Zero', true, true);
newRule(menv, reduceRules, '1*x==x', 'Multiplicative Identity', true, true);
newRule(menv, reduceRules, 'x*1==x', 'Multiplicative Identity', true, true);
newRule(menv, reduceRules, '0/x==0', 'Multiply by Zero', true, true);
newRule(menv, reduceRules, 'x/1==x', 'Divide by One', true, true);
newRule(menv, reduceRules, 'x^1==x', 'First Power', true, true);
newRule(menv, reduceRules, 'x^0==1', 'Zero Power', true, true);
newRule(menv, reduceRules, 'x^(-a)==1/(x^a)', 'Negative Power', true, true);
newRule(menv, reduceRules, '1^x==1', 'One to a Power', true, true);
newRule(menv, reduceRules, '-1*x==-x', 'Multiplicative Identity', true, true);
newRule(menv, reduceRules, 'x*-1==-x', 'Multiplicative Identity', true, true);
newRule(menv, reduceRules, 'x-x==0', 'Additive Inverses Cancel', true, true);
newRule(menv, reduceRules, 'x+-x==0', 'Additive Inverses Cancel', true, true);
newRule(menv, reduceRules, '-x+x==0', 'Additive Inverses Cancel', true, true);
newRule(menv, reduceRules, 'x+(-y)==x-y', "Subtraction", true, true);
newRule(menv, reduceRules, '(-x)+(-y)==-(x+y)', "Factor Negation from Addition", true, true);
newRule(menv, reduceRules, 'x-(-y)==x+y', "Additive Inverse's Inverse", true, true);
newRule(menv, reduceRules, 'x*(-y)==(-x)*y', "Factor Negation from Multiplication", true, true);
newRule(menv, reduceRules, '(-x)/y==-(x/y)', "Factor Negation from Multiplication", true, true);
newRule(menv, reduceRules, 'x/(-y)==-(x/y)', "Factor Negation from Multiplication", true, true);
newRule(menv, reduceRules, '-(-x)==x', "Additive Inverse's Inverse", true, true);
newRule(menv, reduceRules, '/(/x)==x', "Multiplicative Inverse's Inverse", true, true);
return(reduceRules);
}
export function defaultSumReductions(menv) {
var sumReductions = new Array();
newRule(menv, sumReductions, 'a+0==a', 'Simplify Addition by Zero', true, true);
newRule(menv, sumReductions, '0+a==a', 'Simplify Addition by Zero', true, true);
newRule(menv, sumReductions, 'a-a==0', 'Cancel Additive Inverses', true, true);
newRule(menv, sumReductions, 'a+-a==0', 'Cancel Additive Inverses', true, true);
newRule(menv, sumReductions, '-a+a==0', 'Cancel Additive Inverses', true, true);
newRule(menv, sumReductions, 'a*b+-a*b==0', 'Cancel Additive Inverses', true, true);
newRule(menv, sumReductions, '-a*b+a*b==0', 'Cancel Additive Inverses', true, true);
newRule(menv, sumReductions, 'a*(b+c)==a*b+a*c', 'Expand Products by Distributing', true, true);
newRule(menv, sumReductions, '(a+b)*c==a*c+b*c', 'Expand Products by Distributing', true, true);
newRule(menv, sumReductions, 'a*(b-c)==a*b-a*c', 'Expand Products by Distributing', true, true);
newRule(menv, sumReductions, '(a-b)*c==a*c-b*c', 'Expand Products by Distributing', true, true);
return(sumReductions);
}
export function defaultProductReductions(menv) {
var productReductions = new Array();
newRule(menv, productReductions, '0*a==0', 'Simplify Multiplication by Zero', true, true);
newRule(menv, productReductions, 'a*0==0', 'Simplify Multiplication by Zero', true, true);
newRule(menv, productReductions, '1*a==a', 'Simplify Multiplication by One', true, true);
newRule(menv, productReductions, 'a*1==a', 'Simplify Multiplication by One', true, true);
newRule(menv, productReductions, 'a/a==1', 'Cancel Multiplicative Inverses', true, true);
newRule(menv, productReductions, 'a*/a==1', 'Cancel Multiplicative Inverses', true, true);
newRule(menv, productReductions, '/a*a==1', 'Cancel Multiplicative Inverses', true, true);
newRule(menv, productReductions, '(a*b)/(a*c)==b/c', 'Cancel Common Factors', true,true);
newRule(menv, productReductions, 'a^m/a^n==a^(m-n)', 'Cancel Common Factors', true,true);
newRule(menv, productReductions, '(a^m*b)/(a^n*c)==(a^(m-n)*b)/c', 'Cancel Common Factors', true,true);
newRule(menv, productReductions, 'a*a==a^2', 'Write Products of Common Terms as Powers', true, true);
newRule(menv, productReductions, 'a*a^n==a^(n+1)', 'Write Products of Common Terms as Powers', true, true);
newRule(menv, productReductions, 'a^n*a==a^(n+1)', 'Write Products of Common Terms as Powers', true, true);
newRule(menv, productReductions, 'a^m*a^n==a^(m+n)', 'Write Products of Common Terms as Powers', true, true);
newRule(menv, productReductions, '(a^-m*b)/c==b/(a^m*c)', 'Rewrite Using Positive Powers', true,true);
newRule(menv, productReductions, '(b*a^-m)/c==b/(a^m*c)', 'Rewrite Using Positive Powers', true,true);
newRule(menv, productReductions, 'b/(a^-m*c)==(a^m*b)/c', 'Rewrite Using Positive Powers', true,true);
newRule(menv, productReductions, 'b/(c*a^-m)==(a^m*b)/c', 'Rewrite Using Positive Powers', true,true);
return (productReductions);
}