GithubHelp home page GithubHelp logo

cortex-js / compute-engine Goto Github PK

View Code? Open in Web Editor NEW
339.0 10.0 40.0 13.8 MB

An engine for symbolic manipulation and numeric evaluation of math formulas expressed with MathJSON

Home Page: https://cortexjs.io

License: MIT License

JavaScript 1.65% Shell 0.49% TypeScript 95.51% HTML 2.04% CSS 0.31%
math json javascript typescript format cas math-json compute-engine latex symbolic-computation technical-computing

compute-engine's Introduction

math live

Cortex Compute Engine

Symbolic manipulation and numeric evaluation of MathJSON expressions

MathJSON is a lightweight mathematical notation interchange format based on JSON.

The Cortex Compute Engine can parse LaTeX to MathJSON, serialize MathJSON to LaTeX, format, simplify and evaluate MathJSON expressions.

Reference documentation and guides at cortexjs.io/compute-engine.

Using Compute Engine

$ npm install --save @cortex-js/compute-engine
import { parse, evaluate } from "@cortex-js/compute-engine";

const expr = parse("2^{11}-1 \\in \\P");

console.log(expr);
// ➔ ["Element", ["Subtract", ["Power", 2, 11] , 1], "PrimeNumber"]

console.log(evaluate(expr));
// ➔ "False"

More

Related Projects

MathJSON
A lightweight mathematical notation interchange format
MathLive (on GitHub)
A Web Component for math input.
Cortex (on GitHub)
A programming language for scientific computing

Support the Project

License

This project is licensed under the MIT License.

compute-engine's People

Contributors

arnog avatar dependabot[bot] avatar holgerengels avatar phcreery avatar seancheey avatar sritchie avatar stefnotch avatar volesen avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

compute-engine's Issues

"Integrate" doesn't work

This code :

const expr = ce.parse('x^2').json
ce.box(['Integrate', expr, ['Element', 'x', ['Interval', 0, 1]]]).latex
console.log(calculintegrale)

returns
\mathrm{Integrate}(x^{2}, x\in )

Canonical rules for fraction numerators

Hi all! I'm super interested in MathJSON, and I'm trying to set up MathLive/Compute in a personal project.

I was working on comparing linear equations with simplify/evaluate/canonical and found that I couldn't get a consistent representation for (-7/4)x+10

const answerbox="\\frac{-7x}{4}+10"

const answer = [  "Add", [ "Multiply", [ "Divide", -7, 4 ], "x" ], 10]

  match(
    ce.evaluate(ce.parse(answerbox)),
    ce.evaluate(answer)
  )
);

Is this a case where I need to use additional rewrite rules to coerce this into a unique 'canonical' form for comparison, or is this within the scope of the canonical function?

image
image

Substituting a variable with 0 evaluates the equation without the 0 substitution

@cortex-js/compute-engine: v0.8.0 from npm
vue: v3.2.40
Minimal Stackblitz demo: https://stackblitz.com/edit/vue-xsgxaq?file=src/App.vue

What's happening

Substituting any variable with 0 returns an evaluated equation with the variable substituted with 0 untouched.

What should happen

The variable should be treated as 0 and the equation should evaluate as normal

Steps to Reproduce

  1. Create a new ComputeEngine
  2. Parse a latex equation with the created CE. (take a+b+c as example)
  3. Substitute the variables with any values but make sure one of them is 0
  4. Evaluate and check the output

Quick Demonstration

Equation: abc
Variables: a = 5, b = 0, c = 10
Expected Output: 0 (since 5x0x5)
Actual Output: 50b

API to get a list of all free variables for an expression

I have a use-case with ce.defaultDomain = null where I am trying to parse a latex string and get a list of all symbols which are free-variables

This has been my partial attempt by following the documentation but I feel it is not quite right

import {ComputeEngine} from '@cortex-js/compute-engine';

const ce = new ComputeEngine();
ce.defaultDomain = null;

const expr = ce.parse('x + 1');

// Following that the root expression is a function ops will contain the symbols and values. 
// Might have to traverse recursively all BoxedExpressions in the ops. 
const symbols = expr.ops?.filter((e) => e.head === 'Symbol');

// How do I know if the symbol is a free variable 🤔 ? 
... 

Although it seems obvious that there should be a way to find it out easily, but I might be wrong. Feel free to let me know if it is indeed an absurd request 😅. Thanks!

`preserveLatex` doesn't preserve LaTeX in `3x+y`

The preserveLaTeX option works most of the time, but it misses preserving the LaTeX at a level sometimes. For example, in the expression 3x+y, you get:

{
  latex: '3x+y',
  fn: [
    'Add',
    [ 'Multiply', { num: '3', latex: '3' }, { latex: 'x', sym: 'x' } ], // This is short form! We'd expect long form with latex.
    { latex: 'y', sym: 'y' }
  ]
}

// instead of

{
  latex: '3x+y',
  fn: [
    'Add',
    {
      latex: '3x'
      fn: [ 'Multiply', { num: '3', latex: '3' }, { latex: 'x', sym: 'x' } ]},
    }
    { latex: 'y', sym: 'y' }
  ]
}

Quick Repro:

node - << 'EOF'
    const util = require('util')
    const CE = require("@cortex-js/compute-engine")
    const myParse = new CE.LatexSyntax({preserveLatex: true})
    console.log(util.inspect(myParse.parse('3x+y'), {depth: null}))
EOF

This prints out:

Crash when serializing division with LaTeX

Version: 0.4.4

Steps:

  1. Initialize Compute Engine
  2. Set jsonSerializationOptions.metadata = ['latex']
  3. Parse 1/x
  4. Try to print the MathJson for the resulting BoxedExpression.

Here's a quick node session replicating the problem:

> const ComputeEngine = require("@cortex-js/compute-engine").ComputeEngine
undefined
> let myCe = new ComputeEngine()
undefined
> myCe.parse("1/x").json
[ 'Divide', 1, 'x' ]
> myCe.jsonSerializationOptions.metadata = ['latex']
[ 'latex' ]
> myCe.parse("1/x").json
Uncaught TypeError: Converting circular structure to JSON
    --> starting at object with constructor 'global'
    --- property 'global' closes the circle
    at JSON.stringify (<anonymous>)
    at /Users/bengold/brilliant/node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:2:69692
    at Ne.serialize (/Users/bengold/brilliant/node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:2:69710)
    at ke.serialize (/Users/bengold/brilliant/node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:2:74791)
    at zt.serialize (/Users/bengold/brilliant/node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:2:267194)
    at vi (/Users/bengold/brilliant/node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:2:140335)
    at Oi.get json [as json] (/Users/bengold/brilliant/node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:2:163316)

Substituting a variable value replaces unrelated variables too

@cortex-js/compute-engine: v0.8.0 from npm
vue: v3.2.40
Minimal Stackblitz demo: https://stackblitz.com/edit/vue-5kqpdv?file=src/App.vue

What's happening

Substituting any variable, say V, replaces V in unrelated variables such as V_1, V_N

What should happen

Only the substituted variable should be affected

Steps to Reproduce

  1. Create a new ComputeEngine
  2. Parse an equation which has multiple variables with one part same. (ex: V-V_a)
  3. Substitute the variable V with a value.
  4. Evaluate and check the output

Quick Demonstration

Equation: V-V_a
Variables: V: 6, V_a: 4
Expected Output: 2
Actual Output: Evaluation: 2-2_{a}

Error after simplify

Hi, here is my code :

const engine = new ComputeEngine()
expression = '2+\\frac{2+x}{2}'
expression2 = engine.parse(expression).simplify().latex

After printing expression = expression2 the result is :
image

Fails to turn into correct MathJSON

I went through some mathematics notes and found the following interesting equations. I tested them on the compute engine demo page https://cortexjs.io/compute-engine/demo/

(Information for myself: Page 1 of ANA_UE_SS_21_Mo_1.pdf , meaning there are a few more testcases)

  • \begin{equation*} N(\varepsilon)\coloneq\lceil\frac{4}{\varepsilon^2}\rceil \end{equation*}
    image
    turns into "" syntax-error

  • \begin{equation*} x_{1,2}=1,2 \end{equation*}
    image
    turns into ["Equal", ["Subscript", "x", {num: "12"}], {num: "12"}]. Notice how the comma is missing

  • \begin{equation*} \{1,2\} \end{equation*}
    image
    turns into ["Multiply", "\\{", {num: "12"}, "\\}"]. I assume this happens because sets haven't been implemented yet

  • \begin{equation*} [1,2] \end{equation*}
    image
    turns into "" syntax-error. I assume this happens because vectors/matrices and the like haven't been implemented yet.



  • \begin{equation*} \frac{2}{\sqrt{n}}\Leftrightarrow n>\frac{5}{n^2} \end{equation*}
    image
    turns into the following, notice how \\Leftrightarrow doesn't have a special meaning and its precedence is off
[
  "Greater",
  [
    "Multiply",
    ["Divide", 
      {num: "2"}, ["Sqrt", "n"]],
    "\\Leftrightarrow",
    "n"
  ],
  ["Divide", 
    {num: "5"}, ["Power", "n", 2]]
]


  • \begin{equation*} |a_n|\le\frac{2}{\sqrt{n}}\Rightarrow a_n\to0=0 \end{equation*}
    image
    turns into the following, notice how \\Rightarrow doesn't have a special meaning (implies) and its precedence is off.
[
  "LessEqual",
  ["Abs", ["Subscript", "a", "n"]],
  [
    "Equal",
    [
      "To",
      [
        "Multiply",
        ["Divide", 
          {num: "2"}, ["Sqrt", "n"]],
        "\\Rightarrow",
        ["Subscript", "a", "n"]
      ],
      
      {num: "0"}
    ],
    
    {num: "0"}
  ]
]
  • \begin{equation*} 3\equiv5\mod7 \end{equation*} or also \begin{equation*} 3\equiv 5 (\mod 7) \end{equation*}
    image
    This is a congruence relation. I assume this is simply something that hasn't been implemented yet. Hopefully it's a good test case for round-tripping

  • \begin{equation*} a={\displaystyle \lim_{n\to\infin}a_n} \end{equation*}
    image
    turns into

["Equal", "a", "Missing"]
syntax-error
  • \begin{equation*} \forall x\in\C^2:|x|<0 \end{equation*}
    image
    turns into the following
[
  "Element",
  ["Multiply", "\\forall", "x"],
  ["Power", "ComplexNumber", 2]
]
syntax-error
  • \begin{equation*} \forall n\colon a_n\le c_n\le b_n\implies\lim_{n\to\infin}c_n=a \end{equation*}
    image
    turns into the following, note how a few operators aren't parsed and how the precedences for other operators are slightly off. I think the correct order of operations in this case would start at the :, then the => and then the normal rules. Another interesting tidbit here are the two <= signs. While a comparison would usually return a boolean value (true/false), here the comparisons are more like a range.
    The expression is a part of the squeeze theorem.
[
  "LessEqual",
  [
    "Multiply",
    "\\forall",
    "n",
    "\\colon",
    ["Subscript", "a", "n"]
  ],
  [
    "LessEqual",
    ["Subscript", "c", "n"],
    [
      "Equal",
      [
        "Multiply",
        ["Subscript", "b", "n"],
        "\\implies",
        ["Subscript", "\\lim", ["To", "n", "\\infin"]],
        ["Subscript", "c", "n"]
      ],
      "a"
    ]
  ]
]

Parser doesn't insert `Missing` where expected

I've run into two more cases where I'd expect the parser to give an error by inserting Missing, akin to #28:

\\frac{x}{}=y parses as [ 'Multiply', [ 'Divide', 'x', '=' ], 'y' ]

\\sqrt{}=y parses as [ 'Multiply', [ 'Sqrt', '=' ], 'y' ]

Note that in both cases, if you have \placeholder inside of the empty brackets, it parses correctly:

\\frac{x}{\\placeholder}=y  parses as [ 'Equal', [ 'Divide', 'x', 'Missing' ], 'y' ]

\\sqrt{\\placeholder}=y parses as [ 'Equal', [ 'Sqrt', 'Missing' ], 'y' ]

Malformed addition/subtraction don't get parsed as errors

If you're missing an argument, addition and subtraction parse inconsistently compared to multiplication and division.

parse('x * ') => [ 'Multiply', 'x', 'Missing' ]
parse('x + ') => 'x'                                       // should be [ 'Add', 'x', 'Missing' ]

parse('x / ') => [ 'Divide', 'x', 'Missing' ]
parse('x - ') => 'x'                                       // should be [ 'Subtract', 'x', 'Missing' ]

I'd be curious to know a bit more about the error-handling philosophy for parse. MathLive throws math-error events when it can't parse the latex (including the above four cases). Compute Engine seems to not throw any errors at all in parse. Is that intentional?

Error subexpressions not found with getSubexpression (or errors getter)

In the following example I would expect the errors getter to not return an empty value

parsed = computeEngine.parse("1+++");
parsed.isValid
=> true

evaluated = parsed.evaluate()
evaluated.isValid
=> false


evaluated.errors
=> []

JSON.stringify(evaluated.toJSON())
=> `["Add",["Increment",1],["Error","'missing'",["Latex","'+'"]]]`

Distinguish between variable names a_b and indexing a_1

Sometimes, there are variables with a subscript. For example, when one has a triangle and a square and wants to calculate their areas: A_tri and A_squ. In this case, they're clearly two different variables.

However, in a different context, one has matrices. To index the first row/column (depending on convention), one would write a_1. To index the second one, it would be a_2. In that case, they're referring to the same variable.

The same thing applies to

  • function f(x) = 3x^2 + 2x, then f' means "the first derivative of f"
  • but it's also possible to define a variable a'

Is there a general way of distinguishing between those two options?
As in, something along the lines of ["Symbol/Variable/Function/..", ["Subscript", "a", "t"]] vs ["Subscript", "a", "1"]. The former would mean "this entire thing is the variable name" while the latter would mean "the subscript probably has a special meaning"

`replace()` is mis-matching patterns

replace doesn't seem to handle wildcards as documented. Here are two cases where it fails, in slightly different ways:

1 Rewrite "ax + bx" as "(a+b)*x"

const rewriteRule = [
  ['Add', ['Multiply', '_const1', '_var'], ['Multiply', '_const2', '_var']],
  ['Multiply', ['Add', '_const1', '_const2'], '_var']
];

c.replace([myRule], parse('2x+3x'));
// returns [ 'Multiply', [ 'Add', { num: '2' }, { num: '3' } ], 'x' ]
// Good!

c.replace([myRule], parse('2x+x*3');
// returns [ 'Multiply', 3, [ 'Add', 'x', 2 ] ]
// Not good....

It seems like it's not making sure _var is matching the same value.

2 Not handling commutativity:

const rewriteRule = [['Add', '_a', '_a', '_b'], ['Multiply, '_a', '_b']];  // Not valid math, just wanted to test commutativity.

c.replace([commutativity], ['Add', 1, 1, 'x']);
// returns ['Multiply', 1, 'x']
// Good!

c.replace([commutativity], ['Add', 1, 'x', 1]);
// returns ['Add', 1, 'x', 1]
// Not good...

On version 0.4.2.

Evaluation of Piecewise expressions (cases) incorect

Evaluation and numeric approximation gives incorrect results.

version: 0.8.0:

let expr = ce.parse("\begin{cases} 0 & n = 0\\ 1 & n = 1\\ n \geq 2 & n^2+1 \end{cases}")
expr = expr.subs({n: 1})

console.log(expr.N().latex)
// > `\begin{cases}\\1&1=1\\2\le1&1+1\end{cases}`

Expected result: 1

`0.5.0` doesn't include LatexSyntax

I've installed the latest version of compute-engine (0.5.0) and am trying to call LatexSyntax.getDictionary; however, it seems that the exported dist/compute-engine.min.js doesn't actually include LatexSyntax's code. The types seem to indicate that a line like this should work:

import { ComputeEngine, LatexSyntax } from '@cortex-js/compute-engine';

However, this fails when running in the browser. You can also reproduce this by running

$ node
> const ce = require('./dist/compute-engine.min.js')
undefined
> ce
{ ComputeEngine: [class (anonymous)], version: '0.5.0' }
>

Is there another way to import LatexSyntax, now that it was dropped from math-json.min.js?

Can not parse "\sum"

I'm trying to parse a summation equation such as

Screenshot 2022-09-22 125520

and get an error as below;

Screenshot 2022-09-22 130549

Most of the summation cases got similar errors. It recognizes exponentiation instead of summation.

Thanks!

Comma with no space not parsing as a separator

I came across what appears to be a bug in the parser related commas:

image

I would expect 1,2 (without a space) to still read as a Sequence or as multiple arguments, rather than collapsing into a single number.

(Perhaps there's a good reason for this I don't know about 😄)

Version Info:
Cortex: "@cortex-js/compute-engine": "0.4.2"
Node: v14.17.3

Spaces aren't preserved by `preserveLatex`

Version 0.44

Steps to reproduce

  1. Set jsonSerializationOptions.metadata = ['latex']
  2. Parse an expression with extra spaces (e.g. parse('1 + x'))

What I'd expect
I'd expect the latex field to exactly match the input, including all of the spaces in the original expression; ie:
{fn: ["Add", {num: "1", latex: "1"}, "x"], latex: "1 + x"}

What happens instead
Multiple contiguous spaces are collapsed into one:
{fn: ["Add", {num: "1", latex: "1"}, "x"], latex: "1 + x"}

Correct parsing of chains of < and <=

Parsing a chain of comparison operators, such as 1 <= x < 10 is a quite natural thing for humans to do. However, how to best represent this doesn't seem quite as clear cut to me. Which of the <= and < signs comes first? Or should we somehow encode that they're at the same level? Or..?

For the record, this is what the compute-engine currently does. It's a bit inconsistent.

image

Incorrectly parses input with `latexOptions.decimalMarker` option set to `{,}`

It doesn't seem like ComputeEngine properly parses latex input with latexOptions.decimalMarker set to "{,}"

const ce = new ComputeEngine()
ce.latexOptions = { decimalMarker: "{,}" }

const { json } = ce.parse("12{,}3")
// json -> ["Error",12,"'syntax-error'",["LatexForm","'{,}3'"]]

// Now it also breaks when parsing input with `.` as separator
const { json } = ce.parse("12.3")
// json -> ["Error",12,"'syntax-error'",["LatexForm","'.3'"]]

Parsing `""` gives strange LaTeX

Parsing the empty string gives incorrect LaTeX in the parsed JSON; instead of getting latex: "", you get latex: '\\mathrm{Nothing}'.

This was tested on compute-engine 0.6.0.

const ce = new ComputeEngine();
ce.jsonSerializationOptions.metadata = ['latex'];
ce.parse("").json
> { sym: 'Nothing', latex: '\\mathrm{Nothing}' }

Power precedence/associativity

Power towers
I'm not sure if the powers currently get interpreted correctly. The following 'power tower' should, as far as I know, get parsed from the top to the bottom. So those two are equivalent:

image

However, typing in 1^2^3 results in the following: ["Power",["Power",{"num":"1"},2],3]

Negative number to the power

Futhermore, as I've been taught it, if one writes -1^3, they mean -(1^3). MathJson also doesn't seem to reflect on that ["Power",{"num":"-1"},3]
image

Fails to bring into canoical form

The following test cases fail to be turned into a canoical form. Some of them are pretty silly, others are somewhat sensible and should probably work. To reproduce any of them, select the text and paste it into https://cortexjs.io/compute-engine/demo/

  • \displaystyle \left(\sin^{-1}\mleft(x\mright)\right)^{\prime}
    image
    turns into "" syntax-error, probably because of the \displaystyle

  • \begin{equation*} 1^{\sin(x)} \end{equation*}
    image
    turns into null

  • \begin{equation*} 3\text{hello}6 \end{equation*}
    image
    turns into [ "Multiply", 3, 6, ["\\text", ["Multiply", "ExponentialE", "h", "l", "l", "o"]]]

  • \color{red}3
    image
    turns into ["\\textcolor", ["Multiply", "ExponentialE", "d", "r"], 3]

  • \begin{equation*} \ln(3) \end{equation*}
    image
    turns into ["Multiply", 3, "\\ln"]

  • \begin{equation*} f:[a,b]\to\R \end{equation*}
    image
    turns into "f" syntax-error

  • \begin{equation*} \lim_{n\to\infin}3 \end{equation*}
    image
    turns into ["Multiply", 3, ["Subscript", "\\lim", ["To", "n", "\\infin"]]]

  • \begin{cases} 3 & x < 5 \\ 7 & else \end{cases}
    image
    turns into [ "Piecewise", [ "list", ["list", 3, ["Less", "x", ["Multiply", 5, 7, "\\\\"]]] ]]

Tested with https://cortexjs.io/compute-engine/demo/

"Expand" doesn't work

This code :

const expr = ce.parse('4x(3x+2)-5(5x-4)').json
const calculexpand = ce.box(["Expand", expr]).evaluate().latex
console.log(calculexpand)

returns this :
20-25x+4x(2+3x)

Parser doesn't give error when encountering unknown characters

When encountering most special characters, the parser acts as if it hasn't run into any problem at all:

All of the below simply parse as 'x':
x#y
x&y
x$y
x@y
x?y
... and probably others I can't think of.

It'd be great if the parser returned an error like, "Unrecognized symbol: ?" that we could work with to display a better error to the user.

`\complement` doesn't parse correctly

The parser doesn't seem to handle Set complement correctly:

CleanShot 2021-10-18 at 16 44 49

This appears to be A^\complement, which the latex parser interprets as:
[ 'Power', 'A', '\complement']

The other approach we considered was simply A\complement -- but the parser interprets that as:
['Complement', 'A', 'Missing']

parsing `D` gives `Maximum call stack size exceeded` error

👋 Hello! Just bumped into an issue

Parsing 'D' causes an infinite loop somewhere -- Here's a minimal example:

> const ls = require('@cortex-js/compute-engine');
> const latexSyntax = new ls.LatexSyntax();
> latexSyntax.parse('d');
'd'

> latexSyntax.parse('D');
Uncaught RangeError: Maximum call stack size exceeded
    at l (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:1:3816)
    at u (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:1:4004)
    at ni.latexAhead (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:7:68001)
    at ni.lookAhead (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:7:68251)
    at ni.peekDefinition (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:7:68621)
    at ni.matchDefinition (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:7:72131)
    at ni.matchSymbol (/Users/aaronstrick/work/brilliant/node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:7:72311)
    at ni.matchPrimary (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:7:78836)
    at ni.matchExpression (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:7:79320)
    at ni.matchExpression (.../node_modules/@cortex-js/compute-engine/dist/compute-engine.min.js:7:79556)

Occurred on
Node version: v14.17.3
@cortex-js/compute-engine version: 0.4.2

[FEATURE] Support to Units

I want extend the capabilities of Cortexjs Compute Engine to be able to properly handle measurement units, similar to how this project does: hurmet.app/examples.

That is, I want to evaluate expressions taking into account the units.

What is the most feasible way to achieve this?

Support to parsing incomplete Latex expressions

Parsing incomplete (mathematically) expressions seems to leave some parts outs, eg:

parse("2 * 2 = ")

=> [ 'Multiply', { num: '2' }, { num: '2' } ]

parse("2 * 2 = 4")
=> [ 'Equal', [ 'Multiply', { num: '2' }, { num: '2' } ], { num: '4' } ]

This has the implication that chained parse / serialize calls would alter the initial input...

Will this ever work with in-complete expressions?

Typescript Types not available + requiring '@cortex-js/math-json' does not work with Jest + typescript

Hey @arnog, what would be the recommended way to use math-json with Typescript?

Currently my editor complains...

Also: Jest with Typescript does not like the js files in the dist folder:

 FAIL  src/util/mathjson.test.ts
  ● Test suite failed to run

    Jest encountered an unexpected token

    This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.

    By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".

    Here's what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/en/ecmascript-modules for how to enable it.
     • To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
     • If you need a custom transformation specify a "transform" option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.

    You'll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/en/configuration.html

    Details:

    /Users/roman/Desktop/work/playground/node_modules/@cortex-js/math-json/dist/math-json.js:7745
    export { ComputeEngine, LatexSyntax, evaluate, format, parse, parseCortex, serialize, serializeCortex };
    ^^^^^^

    SyntaxError: Unexpected token 'export'

Test-file:

import { expect } from '@jest/globals'
import { parse, serialize } from '@cortex-js/math-json'


describe('something', () => {
  it('should ', () => {
    console.log(parse('\\frac{\\pi}{2}'))
  })
})

parse and serialize

`import {
ErrorListener,
ErrorCode,
DictionaryCategory,
Expression,
} from '../../dist/types/public';
import { tokenize } from '../core/tokenizer';
import {
DEFAULT_LATEX_DICTIONARY,
IndexedLatexDictionary,
indexLatexDictionary,
} from './definitions';
import { Scanner } from './parse';
import {
LatexNumberOptions,
ParseLatexOptions,
SerializeLatexOptions,
LatexDictionaryEntry,
LatexDictionary,
LatexString,
} from './public';
import { Serializer } from './serializer';
import {
DEFAULT_LATEX_NUMBER_OPTIONS,
DEFAULT_PARSE_LATEX_OPTIONS,
DEFAULT_SERIALIZE_LATEX_OPTIONS,
} from './utils';

export class LatexSyntax {
onError?: ErrorListener;

options: Required &
Required &
Required;
private dictionary: IndexedLatexDictionary;

constructor(
options?: LatexNumberOptions &
ParseLatexOptions &
SerializeLatexOptions & {
dictionary?: readonly LatexDictionaryEntry[];
onError?: ErrorListener;
}
) {
const onError = (err) => {
if (typeof window !== 'undefined') {
if (!err.before || !err.after) {
console.warn(err.code + (err.arg ? ': ' + err.arg : ''));
} else {
console.warn(
err.code +
(err.arg ? ': ' + err.arg : '') +
'\n' +
'%c' +
'| ' +
err.before +
'%c' +
err.after +
'\n' +
'%c' +
'| ' +
String(' ').repeat(err.before.length) +
'▲',
'font-weight: bold',
'font-weight: normal; color: rgba(160, 160, 160)',
'font-weight: bold; color: hsl(4deg, 90%, 50%)'
);
}
}
return;
};
this.onError = options?.onError ?? onError;
const opts = { ...(options ?? {}) };
delete opts.dictionary;
delete opts.onError;
this.options = {
...DEFAULT_LATEX_NUMBER_OPTIONS,
...DEFAULT_SERIALIZE_LATEX_OPTIONS,
...DEFAULT_PARSE_LATEX_OPTIONS,
...opts,
};
this.dictionary = indexLatexDictionary(
options?.dictionary ?? LatexSyntax.getDictionary(),
this.onError
);
}

static getDictionary(
domain: DictionaryCategory | 'all' = 'all'
): Readonly {
if (domain === 'all') {
let result: Readonly = [];
for (const domain of Object.keys(DEFAULT_LATEX_DICTIONARY)) {
result = [...result, ...DEFAULT_LATEX_DICTIONARY[domain]];
}
return result;
}

return [...DEFAULT_LATEX_DICTIONARY[domain]];

}

parse(latex: LatexString): Expression {
const scanner = new Scanner(
tokenize(latex, []),
this.options,
this.dictionary,
this.onError
);

const result = scanner.matchExpression();

if (!scanner.atEnd) {
  // eslint-disable-next-line no-unused-expressions
  this.onError?.({ code: 'syntax-error' });
}

return result ?? '';

}
serialize(expr: Expression): LatexString {
const serializer = new Serializer(
this.options,
this.dictionary,
this.onError
);
return serializer.serialize(expr);
}
}

export function parse(
latex: LatexString,
options?: LatexNumberOptions &
ParseLatexOptions & {
dictionary?: Readonly;
onError?: ErrorListener;
}
): Expression {
const syntax = new LatexSyntax(options);
return syntax.parse(latex);
}

/**

  • Serialize a MathJSON expression as a Latex string.

*/
export function serialize(
expr: Expression,
options?: LatexNumberOptions &
SerializeLatexOptions & {
dictionary?: Readonly;
onError?: ErrorListener;
}
): LatexString {
const syntax = new LatexSyntax(options);
return syntax.serialize(expr);
}
`

and

index.js:159 ./src/app/home/home.page.ts 34:20-29 "export 'serialize' was not found in 'math-json/math-json.js'

class LatexSyntax is exported

but parse function and serialize function is not

Arbitrary unicode characters

I'd love it if the compute-engine could handle arbitrary Unicode characters and parse them as variable names.
A silly example would be
\begin{equation*} 🍕+3 \end{equation*}

A more reasonable example would be variable names in a different language, I suppose.

Parsing variable names (chars without spaces between) results in a `Multiply` element with a list

parse("Pts_{total} =")  // ["Multiply","P","t",["Subscript","s",["Multiply","t","o","t","a","l"]]]

This is how it looks like, it's basically a variable name for total points.
Screenshot 2021-04-10 at 23 14 04

I understand the reason to wrap it in Multiply, yet I'm not sure how to deal with it... Wrapping it in \text{} changes formatting, keeping it like it is now obscures the AST... If you don't have a suggestion / solution, feel free to close this issue, it seems to be a somewhat fundamental ambiguity in the semantic interpretation of LaTEX.

Parsing certain expressions causes uncaught exceptions

It seems that parsing certain expressions causes the uncaught exception the name "..." cannot be used as a symbol name. The two examples I've found are:

\\frac{x}{} \\text{ cm} 
-- Fails with "The name " cm" cannot be used as a symbol name.
\\frac{x}{ } \\text{ cm} 
-- Succeeds
\\frac{x}{} \\text{cm} 
-- Succeeds

and

\\sqrt(x)
-- Fails with "The name "(" cannot be used as a symbol name"
-- This should probably be \\sqrt{x}, but I'm not sure that crashing is ideal behavior here.

As a broader question, it seems like there are two mechanisms for throwing errors from parse:

  1. Throwing an exception
  2. Returning MathJson representing the error ["Error", ....]

Is there a meaningful difference between these?

\mathrm and a variable with sub-script causes error in evaluation

@cortex-js/compute-engine: v0.8.0 from npm
vue: v3.2.40
Minimal Stackblitz demo: https://stackblitz.com/edit/vue-agjdaa?file=src/App.vue

What's happening

Substituting any variable with 0 returns an evaluated equation with the variable substituted with 0 untouched.

What should happen

The variable should be treated as 0 and the equation should evaluate as normal

Steps to Reproduce

  1. Create a new ComputeEngine
  2. Parse a latex equation which has both a \mathrm and variable with subscript(ex: F_i) with the created CE. (take \pi-a-\mathrm{AD}-F_o as example)
  3. Substitute the variables with any values.
  4. Evaluate and check the output

Quick Demonstration

Equation: \pi-a-\mathrm{AD}-F_o
Variables: AD: 1, F_o: 2, Pi: 3.14, a: 1
Expected Output: Evaluated output
Actual Output: 1.14-\mathrm{F_{o}}

logarithm not working

Banal logarithm of 8 to base 2. Not calculated.
Example:
"\log_28"
Parse: [ 'Multiply', [ 'Subscript', '\log', 2 ], { num: '8' } ]
Result: [ 'Multiply', 8, 'Subscript' ]

Grouping and parenthesis

Currently having issues trying to serialize parenthesis grouped expressions. It looks like we might neeed a way to clean mathlive latex into math json expected strings. (Converting \\mleft( into (, etc...)

"x\\mleft(x-4\\mright)"

Is serialized to

[ "Subtract", [ "Multiply", "x", "\\mleft", [ "Parentheses" ], "x" ], [ "Multiply", { "num": "4" }, "\\mright" ] ]

`\cdot y` parses incorrectly

There are a few different signs that can be used for multiplication: *, \cdot, and \times. They are inconsistent in how they parse when an argument is missing.

x * y       => [ 'Multiply', 'x', 'y' ]       👍
x \cdot y   => [ 'Multiply', 'x', 'y' ]       👍
x \times y  => [ 'Multiply', 'x', 'y' ]       👍

-- Right side missing
x *         => [ 'Multiply', 'x', 'Missing' ] 👍
x \cdot     => [ 'Multiply', 'x', 'Missing' ] 👍
x \times    => [ 'Multiply', 'x', 'Missing' ] 👍

-- Left side missing
  * y       => [ 'Multiply', 'Missing', 'y' ] 👍
  \cdot y   => [ 'Multiply', '\\cdot', 'y' ]  ❌ 
  \times y  => [ 'Multiply', '\\times', 'y' ] ❌ 

"5d+5" parses as "5e5"

Perhaps this is some strange scientific notation I'm unfamiliar with, but the latex of 5d+5 parses as { num: '5e5' } instead of [ 'Add', [ 'Multiply', { num: '5' }, 'd' ], { num: '5' } ].

To repro, call:

new LatexParser().parse("5d+5")

Text should not be parsed

You're probably aware of this already, but text shouldn't get parsed.

image

So, something like the following

\begin{equation*} e^{i\pi\text{nope!?\lparen sum}} \end{equation*}

will get serialized as

 ["Power","ExponentialE",["Multiply","ImaginaryUnit","Pi",["\\text",["Multiply","n","o","p",["Factorial","ExponentialE"]]]]] 

Notice how it ends up being a multiplication and everything after the ? is missing. While text isn't usually part of a rigorous mathematical formula, it does occasionally get used to describe something. Some examples would be:

image
-- Wikipedia

image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.