Is it possible to treat a square root as it is, e.g. โ5, without converting it to a number?
Specifically, I'd like to do the following:
sqrt(5) //=> โ5 object
sqrt(2) + sqrt(3) //=> โ2 + โ3
sqrt(2) + sqrt(2) //=> 2 * โ2
sqrt(3 * 3) //=> 3
I have read some examples and, like in the custom_datatatype.js example, I thought I could add a custom data type for โ. But I couldn't figure out how to return the value as it is of add, sub, etc.
If anyone knows how to do it right, please let me know!
Finally, thank you for such a wonderful library.
@yasuhito thanks for your question. When you run for example math.parse('sqrt(2) + sqrt(3)'), you get back an expression tree, and you can perform operations on this tree like simplification, derivative, or custom transformations, and formatting to a string (or Latex output). Is that what you mean?
Docs:
https://mathjs.org/docs/expressions/parsing.html#parse
https://mathjs.org/docs/expressions/expression_trees.html
@josdejong thanks for the detailed information!
I pasted the code I wrote below to clarify what I want to achieve.
(I mainly used sqrt.js as a reference.)
My intention is that math.evaluate('sqrt(25)') will return 5 (same behavior as math.js), and math.evaluate('sqrt(5)') will return "โ5".
However, my implementation returns a string "โ5", which causes an error in further calculations.
I think what should be returned here is data of a type like "expression" or something.
If you have a way to do it correctly in mathjs, or sample code, it would be very helpful.
const sqrt = factory('sqrt', ['config', 'typed', 'Complex'], ({ config, typed, Complex }) => {
return typed('sqrt', {
number: _sqrtNumber,
Complex: function (x) {
return x.sqrt()
},
BigNumber: function (x) {
if (!x.isNegative() || config.predictable) {
const result = x.sqrt()
if (Number.isInteger(result)) {
return result
} else {
return `โ${x}`
}
} else {
return _sqrtNumber(x.toNumber())
}
},
})
function _sqrtNumber (x) {
if (isNaN(x)) {
return NaN
} else if (x >= 0 || config.predictable) {
const result = Math.sqrt(x)
if (Number.isInteger(result)) {
return result
} else {
return `โ${x}`
}
} else {
return new Complex(x, 0).sqrt()
}
}
})
this.math = create(evaluateDependencies)
this.math.import({ sqrt }, { override: true })
I'm not sure if it is a good idea to sometimes return a numeric result like 5 and sometimes a string like "โ5" when _evaluating_. If you want to do this in a real, symbolic way, you should use a solution based on manipulating a parsed expression tree.
What you can do is add a new rule to the math.simplify function. This rule can check whether a node in the expression is the function sqrt. If so, it can evaluate it, and when the result is an integer, return a new ConstantNode holding the integer result, and otherwise leave the node untouched.
You can extend the built-in rules as described in the documentation: https://mathjs.org/docs/reference/functions/simplify.html (existing rules are exposed via math.simplify.rules). Does that make sense?
@josdejong Thank you very much!!
I have added the function simplifyNode to math.simplify.rules as follows. As you told me, with this function โ25 becomes 5 and โ5 remains unchanged.
simplifyNode(node) {
if (isFunctionNode(node) && node.name == 'sqrt') {
const result = node.compile().evaluate()
if (Number.isInteger(result)) {
return new this.math.ConstantNode(result)
}
}
return node
}
// ...
this.math.simplify.rules.unshift(
this.simplifyNode.bind(this)
)
Unfortunately, the simplifyConstant later in math.simplify.rules seems to convert โ5 to a number (2.23606797749979).
Let me describe the lacking background information that explains why I am trying to do this.
I'm trying to implement a quantum computer simulator, running in the browser, using math.js. In a quantum simulation, every computation is a product of a matrix and a vector. The vector, by the way, is the tensor product of the state of the qubits, and the matrix represents the operations (gates) on the qubits.
My implementation doesn't use eval(expr) or simplify(expr) (i.e., doesn't go through the string), but instead finds the product by constructing matrices and vectors directly like this:
// Apply a H gate to a qubit state
// the H gate
const H = matrix([
[divide(1, sqrt(2)), divide(1, sqrt(2))],
[divide(1, sqrt(2)), divide(-1, sqrt(2))],
])
// psi: qubit state vector
return multiply(H, this.psi)
The result of such a calculation often returns a value like 1/โ2. My original motivation was that I wanted to get the same "1/โ2" as the result of hand calculations, not an immediate value of 0.7071067811865475.
Would math.simplify.rules still work if I don't use eval(expr) or simplify(expr)? If you have a better way to achieve what I want to doo, I'd appreciate it if you could let me know. Thank you in advance ๐
@yasuhito you could try another lib like https://nerdamer.com/demo or my lib while it is not supported here.
@yasuhito you could try another lib like https://nerdamer.com/demo or my lib while it is not supported here.
Thanks for the link Viktor, I hadn't heard about Nerdamer before, it looks great! I've linked to it from the downloads page.
@yasuhito The reason is that there is a built-in rule which evaluates functions containing constants, also for sqrt. This is the simplifyConstant rule: simplify.js#L300. So, what you need is create your own version of this function, which first checks the special sqrt rule, and otherwise fallback to the old behavior. Here a full example:
const { simplify, isFunctionNode, isInteger, ConstantNode } = require('mathjs')
const rules = simplify.rules.map(rule => {
if (typeof rule === 'function' && rule.name === 'simplifyConstant') {
// replace the built-in simplifyConstant with our own extended version
const simplifyConstant = rule
return function customSimplifyConstant(node) {
if (isFunctionNode(node) && node.name === 'sqrt') {
const result = node.compile().evaluate({})
if (isInteger(result)) {
// return the integer result (for example `sqrt(25)`
return new ConstantNode(result)
} else {
// do not replace sqrt functions not resulting in an integer result, like `sqrt(5)`
return node
}
}
else {
// fall back on the original behavior
return simplifyConstant(node, {})
}
}
}
else {
// leave all other built-in rules untouched
return rule
}
})
function simplifyAndPrint(expression) {
console.log(`${expression}=${simplify(expression, rules)}`)
}
simplifyAndPrint('2 + 5') // 7
simplifyAndPrint('sqrt(25)') // 5
simplifyAndPrint('sqrt(5)') // sqrt(5)
You're fully flexible in which rules you do or do not want to apply with the function simplify ๐
@josdejong Thank you, sir! The code you gave me did indeed make the examples such as โ5 work correctly. math.js' API is very elegant.
Finally, could you tell me one more thing about the simplify() function over matrices? I tried to parse() and simplify() the following formula m, but it gave me the following error:
const m = "multiply(matrix([[1 / sqrt(2), 1 / sqrt(2)], [1 / sqrt(2), -1 / sqrt(2)]]), multiply(identity(2), matrix([[1], [0]])))"
const f = math.parse(m)
math.simplify(f, myRules) // => Error: Unimplemented node type in simplifyConstant: ArrayNode
Is there any way to get around this error? The formula m is supposed to return a vector (1/โ2, 1/โ2), so for me I want to get each element symbolically. I did a search and #1913 seems to be relevant.
That's good to hear ๐
About simplifying expressions with matrices: that is an open feature request, see #1913. Help is welcome :)