simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

simplifyCore.js (7693B)


      1 import { isAccessorNode, isArrayNode, isConstantNode, isFunctionNode, isIndexNode, isObjectNode, isOperatorNode } from '../../utils/is.js';
      2 import { createUtil } from './simplify/util.js';
      3 import { factory } from '../../utils/factory.js';
      4 var name = 'simplifyCore';
      5 var dependencies = ['equal', 'isZero', 'add', 'subtract', 'multiply', 'divide', 'pow', 'AccessorNode', 'ArrayNode', 'ConstantNode', 'FunctionNode', 'IndexNode', 'ObjectNode', 'OperatorNode', 'ParenthesisNode', 'SymbolNode'];
      6 export var createSimplifyCore = /* #__PURE__ */factory(name, dependencies, _ref => {
      7   var {
      8     equal,
      9     isZero,
     10     add,
     11     subtract,
     12     multiply,
     13     divide,
     14     pow,
     15     AccessorNode,
     16     ArrayNode,
     17     ConstantNode,
     18     FunctionNode,
     19     IndexNode,
     20     ObjectNode,
     21     OperatorNode,
     22     ParenthesisNode,
     23     SymbolNode
     24   } = _ref;
     25   var node0 = new ConstantNode(0);
     26   var node1 = new ConstantNode(1);
     27   var {
     28     hasProperty,
     29     isCommutative
     30   } = createUtil({
     31     FunctionNode,
     32     OperatorNode,
     33     SymbolNode
     34   });
     35   /**
     36    * simplifyCore() performs single pass simplification suitable for
     37    * applications requiring ultimate performance. In contrast, simplify()
     38    * extends simplifyCore() with additional passes to provide deeper
     39    * simplification.
     40    *
     41    * Syntax:
     42    *
     43    *     simplifyCore(expr)
     44    *
     45    * Examples:
     46    *
     47    *     const f = math.parse('2 * 1 * x ^ (2 - 1)')
     48    *     math.simpifyCore(f)                          // Node {2 * x}
     49    *     math.simplify('2 * 1 * x ^ (2 - 1)', [math.simplifyCore]) // Node {2 * x}
     50    *
     51    * See also:
     52    *
     53    *     simplify, resolve, derivative
     54    *
     55    * @param {Node} node
     56    *     The expression to be simplified
     57    * @param {Object} options
     58    *     Simplification options, as per simplify()
     59    * @return {Node} Returns expression with basic simplifications applied
     60    */
     61 
     62   function simplifyCore(node, options) {
     63     var context = options ? options.context : undefined;
     64 
     65     if (hasProperty(node, 'trivial', context)) {
     66       // This node does nothing if it has only one argument, so if so,
     67       // return that argument simplified
     68       if (isFunctionNode(node) && node.args.length === 1) {
     69         return simplifyCore(node.args[0], options);
     70       } // For other node types, we try the generic methods
     71 
     72 
     73       var simpChild = false;
     74       var childCount = 0;
     75       node.forEach(c => {
     76         ++childCount;
     77 
     78         if (childCount === 1) {
     79           simpChild = simplifyCore(c, options);
     80         }
     81       });
     82 
     83       if (childCount === 1) {
     84         return simpChild;
     85       }
     86     }
     87 
     88     if (isOperatorNode(node) && node.isUnary()) {
     89       var a0 = simplifyCore(node.args[0], options);
     90 
     91       if (node.op === '-') {
     92         // unary minus
     93         if (isOperatorNode(a0)) {
     94           if (a0.isUnary() && a0.op === '-') {
     95             return a0.args[0];
     96           } else if (a0.isBinary() && a0.fn === 'subtract') {
     97             return new OperatorNode('-', 'subtract', [a0.args[1], a0.args[0]]);
     98           }
     99         }
    100 
    101         return new OperatorNode(node.op, node.fn, [a0]);
    102       }
    103     } else if (isOperatorNode(node) && node.isBinary()) {
    104       var _a = simplifyCore(node.args[0], options);
    105 
    106       var a1 = simplifyCore(node.args[1], options);
    107 
    108       if (node.op === '+') {
    109         if (isConstantNode(_a)) {
    110           if (isZero(_a.value)) {
    111             return a1;
    112           } else if (isConstantNode(a1)) {
    113             return new ConstantNode(add(_a.value, a1.value));
    114           }
    115         }
    116 
    117         if (isConstantNode(a1) && isZero(a1.value)) {
    118           return _a;
    119         }
    120 
    121         if (isOperatorNode(a1) && a1.isUnary() && a1.op === '-') {
    122           return new OperatorNode('-', 'subtract', [_a, a1.args[0]]);
    123         }
    124 
    125         return new OperatorNode(node.op, node.fn, a1 ? [_a, a1] : [_a]);
    126       } else if (node.op === '-') {
    127         if (isConstantNode(_a) && a1) {
    128           if (isConstantNode(a1)) {
    129             return new ConstantNode(subtract(_a.value, a1.value));
    130           } else if (isZero(_a.value)) {
    131             return new OperatorNode('-', 'unaryMinus', [a1]);
    132           }
    133         } // if (node.fn === "subtract" && node.args.length === 2) {
    134 
    135 
    136         if (node.fn === 'subtract') {
    137           if (isConstantNode(a1) && isZero(a1.value)) {
    138             return _a;
    139           }
    140 
    141           if (isOperatorNode(a1) && a1.isUnary() && a1.op === '-') {
    142             return simplifyCore(new OperatorNode('+', 'add', [_a, a1.args[0]]), options);
    143           }
    144 
    145           return new OperatorNode(node.op, node.fn, [_a, a1]);
    146         }
    147       } else if (node.op === '*') {
    148         if (isConstantNode(_a)) {
    149           if (isZero(_a.value)) {
    150             return node0;
    151           } else if (equal(_a.value, 1)) {
    152             return a1;
    153           } else if (isConstantNode(a1)) {
    154             return new ConstantNode(multiply(_a.value, a1.value));
    155           }
    156         }
    157 
    158         if (isConstantNode(a1)) {
    159           if (isZero(a1.value)) {
    160             return node0;
    161           } else if (equal(a1.value, 1)) {
    162             return _a;
    163           } else if (isOperatorNode(_a) && _a.isBinary() && _a.op === node.op && isCommutative(node, context)) {
    164             var a00 = _a.args[0];
    165 
    166             if (isConstantNode(a00)) {
    167               var a00a1 = new ConstantNode(multiply(a00.value, a1.value));
    168               return new OperatorNode(node.op, node.fn, [a00a1, _a.args[1]], node.implicit); // constants on left
    169             }
    170           }
    171 
    172           if (isCommutative(node, context)) {
    173             return new OperatorNode(node.op, node.fn, [a1, _a], node.implicit); // constants on left
    174           } else {
    175             return new OperatorNode(node.op, node.fn, [_a, a1], node.implicit);
    176           }
    177         }
    178 
    179         return new OperatorNode(node.op, node.fn, [_a, a1], node.implicit);
    180       } else if (node.op === '/') {
    181         if (isConstantNode(_a)) {
    182           if (isZero(_a.value)) {
    183             return node0;
    184           } else if (isConstantNode(a1) && (equal(a1.value, 1) || equal(a1.value, 2) || equal(a1.value, 4))) {
    185             return new ConstantNode(divide(_a.value, a1.value));
    186           }
    187         }
    188 
    189         return new OperatorNode(node.op, node.fn, [_a, a1]);
    190       } else if (node.op === '^') {
    191         if (isConstantNode(a1)) {
    192           if (isZero(a1.value)) {
    193             return node1;
    194           } else if (equal(a1.value, 1)) {
    195             return _a;
    196           } else {
    197             if (isConstantNode(_a)) {
    198               // fold constant
    199               return new ConstantNode(pow(_a.value, a1.value));
    200             } else if (isOperatorNode(_a) && _a.isBinary() && _a.op === '^') {
    201               var a01 = _a.args[1];
    202 
    203               if (isConstantNode(a01)) {
    204                 return new OperatorNode(node.op, node.fn, [_a.args[0], new ConstantNode(multiply(a01.value, a1.value))]);
    205               }
    206             }
    207           }
    208         }
    209       }
    210 
    211       return new OperatorNode(node.op, node.fn, [_a, a1]);
    212     } else if (isFunctionNode(node)) {
    213       return new FunctionNode(simplifyCore(node.fn), node.args.map(n => simplifyCore(n, options)));
    214     } else if (isArrayNode(node)) {
    215       return new ArrayNode(node.items.map(n => simplifyCore(n, options)));
    216     } else if (isAccessorNode(node)) {
    217       return new AccessorNode(simplifyCore(node.object, options), simplifyCore(node.index, options));
    218     } else if (isIndexNode(node)) {
    219       return new IndexNode(node.dimensions.map(n => simplifyCore(n, options)));
    220     } else if (isObjectNode(node)) {
    221       var newProps = {};
    222 
    223       for (var prop in node.properties) {
    224         newProps[prop] = simplifyCore(node.properties[prop], options);
    225       }
    226 
    227       return new ObjectNode(newProps);
    228     } else {// cannot simplify
    229     }
    230 
    231     return node;
    232   }
    233 
    234   return simplifyCore;
    235 });