MST rendering: support infix division, use arcsin instead sin^-1 form for inverse trigonometric functions

This commit is contained in:
Iaroslav Postovalov 2021-05-03 04:14:19 +07:00
parent eca1ad48f5
commit 591b408729
No known key found for this signature in database
GPG Key ID: 46E15E4A31B3BCD7
13 changed files with 363 additions and 78 deletions

View File

@ -91,7 +91,7 @@ KMath is a modular library. Different modules provide different features with di
* ### [kmath-ast](kmath-ast) * ### [kmath-ast](kmath-ast)
> >
> >
> **Maturity**: PROTOTYPE > **Maturity**: EXPERIMENTAL
> >
> **Features:** > **Features:**
> - [expression-language](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser > - [expression-language](kmath-ast/src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser
@ -154,9 +154,9 @@ performance calculations to code generation.
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
> >
> **Features:** > **Features:**
> - [ejml-vector](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : The Point implementation using SimpleMatrix. > - [ejml-vector](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlVector.kt) : Point implementations.
> - [ejml-matrix](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : The Matrix implementation using SimpleMatrix. > - [ejml-matrix](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlMatrix.kt) : Matrix implementation.
> - [ejml-linear-space](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : The LinearSpace implementation using SimpleMatrix. > - [ejml-linear-space](kmath-ejml/src/main/kotlin/space/kscience/kmath/ejml/EjmlLinearSpace.kt) : LinearSpace implementations.
<hr/> <hr/>
@ -200,6 +200,12 @@ One can still use generic algebras though.
> **Maturity**: PROTOTYPE > **Maturity**: PROTOTYPE
<hr/> <hr/>
* ### [kmath-jupyter](kmath-jupyter)
>
>
> **Maturity**: PROTOTYPE
<hr/>
* ### [kmath-kotlingrad](kmath-kotlingrad) * ### [kmath-kotlingrad](kmath-kotlingrad)
> >
> >

View File

@ -1,6 +1,6 @@
# Module kmath-ast # Module kmath-ast
Abstract syntax tree expression representation and related optimizations. Performance and visualization extensions to MST API.
- [expression-language](src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser - [expression-language](src/commonMain/kotlin/space/kscience/kmath/ast/parser.kt) : Expression language and its parser
- [mst-jvm-codegen](src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler - [mst-jvm-codegen](src/jvmMain/kotlin/space/kscience/kmath/asm/asm.kt) : Dynamic MST to JVM bytecode compiler
@ -39,12 +39,16 @@ dependencies {
### On JVM ### On JVM
`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds `kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a
a special implementation of `Expression<T>` with implemented `invoke` function. special implementation of `Expression<T>` with implemented `invoke` function.
For example, the following builder: For example, the following builder:
```kotlin ```kotlin
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.asm.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
``` ```
@ -54,6 +58,7 @@ MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
package space.kscience.kmath.asm.generated; package space.kscience.kmath.asm.generated;
import java.util.Map; import java.util.Map;
import kotlin.jvm.functions.Function2; import kotlin.jvm.functions.Function2;
import space.kscience.kmath.asm.internal.MapIntrinsics; import space.kscience.kmath.asm.internal.MapIntrinsics;
import space.kscience.kmath.expressions.Expression; import space.kscience.kmath.expressions.Expression;
@ -63,7 +68,7 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
private final Object[] constants; private final Object[] constants;
public final Double invoke(Map<Symbol, ? extends Double> arguments) { public final Double invoke(Map<Symbol, ? extends Double> arguments) {
return (Double)((Function2)this.constants[0]).invoke((Double)MapIntrinsics.getOrFail(arguments, "x"), 2); return (Double) ((Function2) this.constants[0]).invoke((Double) MapIntrinsics.getOrFail(arguments, "x"), 2);
} }
public AsmCompiledExpression_45045_0(Object[] constants) { public AsmCompiledExpression_45045_0(Object[] constants) {
@ -75,8 +80,8 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
#### Known issues #### Known issues
- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid - The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid class
class loading overhead. loading overhead.
- This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders. - This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders.
### On JS ### On JS
@ -84,6 +89,10 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
A similar feature is also available on JS. A similar feature is also available on JS.
```kotlin ```kotlin
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.estree.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
``` ```
@ -91,13 +100,16 @@ The code above returns expression implemented with such a JS function:
```js ```js
var executable = function (constants, arguments) { var executable = function (constants, arguments) {
return constants[1](constants[0](arguments, "x"), 2); return constants[1](constants[0](arguments, "x"), 2);
}; };
``` ```
JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation. Currently, only expressions inside `DoubleField` and `IntRing` are supported. JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation.
Currently, only expressions inside `DoubleField` and `IntRing` are supported.
```kotlin ```kotlin
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.wasm.* import space.kscience.kmath.wasm.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
@ -128,7 +140,9 @@ Example usage:
```kotlin ```kotlin
import space.kscience.kmath.ast.* import space.kscience.kmath.ast.*
import space.kscience.kmath.ast.rendering.* import space.kscience.kmath.ast.rendering.*
import space.kscience.kmath.misc.*
@OptIn(UnstableKMathAPI::class)
public fun main() { public fun main() {
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath() val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath()
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst) val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
@ -144,13 +158,68 @@ public fun main() {
Result LaTeX: Result LaTeX:
![](http://chart.googleapis.com/chart?cht=tx&chl=e%5E%7B%5Csqrt%7Bx%7D%7D-%5Cfrac%7B%5Cfrac%7B%5Coperatorname%7Bsin%7D%5E%7B-1%7D%5C,%5Cleft(2%5C,x%5Cright)%7D%7B2%5Ctimes10%5E%7B10%7D%2Bx%5E%7B3%7D%7D%7D%7B-12%7D) ![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{-12})
Result MathML (embedding MathML is not allowed by GitHub Markdown): Result MathML (embedding MathML is not allowed by GitHub Markdown):
<details>
```html ```html
<mrow><msup><mrow><mi>e</mi></mrow><mrow><msqrt><mi>x</mi></msqrt></mrow></msup><mo>-</mo><mfrac><mrow><mfrac><mrow><msup><mrow><mo>sin</mo></mrow><mrow><mo>-</mo><mn>1</mn></mrow></msup><mspace width="0.167em"></mspace><mfenced open="(" close=")" separators=""><mn>2</mn><mspace width="0.167em"></mspace><mi>x</mi></mfenced></mrow><mrow><mn>2</mn><mo>&times;</mo><msup><mrow><mn>10</mn></mrow><mrow><mn>10</mn></mrow></msup><mo>+</mo><msup><mrow><mi>x</mi></mrow><mrow><mn>3</mn></mrow></msup></mrow></mfrac></mrow><mrow><mo>-</mo><mn>12</mn></mrow></mfrac></mrow> <math xmlns="https://www.w3.org/1998/Math/MathML">
<mrow>
<mo>exp</mo>
<mspace width="0.167em"></mspace>
<mfenced open="(" close=")" separators="">
<msqrt>
<mi>x</mi>
</msqrt>
</mfenced>
<mo>-</mo>
<mfrac>
<mrow>
<mfrac>
<mrow>
<mo>arcsin</mo>
<mspace width="0.167em"></mspace>
<mfenced open="(" close=")" separators="">
<mn>2</mn>
<mspace width="0.167em"></mspace>
<mi>x</mi>
</mfenced>
</mrow>
<mrow>
<mn>2</mn>
<mo>&times;</mo>
<msup>
<mrow>
<mn>10</mn>
</mrow>
<mrow>
<mn>10</mn>
</mrow>
</msup>
<mo>+</mo>
<msup>
<mrow>
<mi>x</mi>
</mrow>
<mrow>
<mn>3</mn>
</mrow>
</msup>
</mrow>
</mfrac>
</mrow>
<mrow>
<mo>-</mo>
<mn>12</mn>
</mrow>
</mfrac>
</mrow>
</math>
``` ```
</details>
It is also possible to create custom algorithms of render, and even add support of other markup languages It is also possible to create custom algorithms of render, and even add support of other markup languages
(see API reference). (see API reference).

View File

@ -1,6 +1,6 @@
# Module kmath-ast # Module kmath-ast
Abstract syntax tree expression representation and related optimizations. Performance and visualization extensions to MST API.
${features} ${features}
@ -10,12 +10,16 @@ ${artifact}
### On JVM ### On JVM
`kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds `kmath-ast` JVM module supports runtime code generation to eliminate overhead of tree traversal. Code generator builds a
a special implementation of `Expression<T>` with implemented `invoke` function. special implementation of `Expression<T>` with implemented `invoke` function.
For example, the following builder: For example, the following builder:
```kotlin ```kotlin
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.asm.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
``` ```
@ -25,6 +29,7 @@ MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
package space.kscience.kmath.asm.generated; package space.kscience.kmath.asm.generated;
import java.util.Map; import java.util.Map;
import kotlin.jvm.functions.Function2; import kotlin.jvm.functions.Function2;
import space.kscience.kmath.asm.internal.MapIntrinsics; import space.kscience.kmath.asm.internal.MapIntrinsics;
import space.kscience.kmath.expressions.Expression; import space.kscience.kmath.expressions.Expression;
@ -34,7 +39,7 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
private final Object[] constants; private final Object[] constants;
public final Double invoke(Map<Symbol, ? extends Double> arguments) { public final Double invoke(Map<Symbol, ? extends Double> arguments) {
return (Double)((Function2)this.constants[0]).invoke((Double)MapIntrinsics.getOrFail(arguments, "x"), 2); return (Double) ((Function2) this.constants[0]).invoke((Double) MapIntrinsics.getOrFail(arguments, "x"), 2);
} }
public AsmCompiledExpression_45045_0(Object[] constants) { public AsmCompiledExpression_45045_0(Object[] constants) {
@ -46,8 +51,8 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
#### Known issues #### Known issues
- The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid - The same classes may be generated and loaded twice, so it is recommended to cache compiled expressions to avoid class
class loading overhead. loading overhead.
- This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders. - This API is not supported by non-dynamic JVM implementations (like TeaVM and GraalVM) because of using class loaders.
### On JS ### On JS
@ -55,6 +60,10 @@ public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
A similar feature is also available on JS. A similar feature is also available on JS.
```kotlin ```kotlin
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.estree.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
``` ```
@ -62,13 +71,16 @@ The code above returns expression implemented with such a JS function:
```js ```js
var executable = function (constants, arguments) { var executable = function (constants, arguments) {
return constants[1](constants[0](arguments, "x"), 2); return constants[1](constants[0](arguments, "x"), 2);
}; };
``` ```
JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation. Currently, only expressions inside `DoubleField` and `IntRing` are supported. JS also supports very experimental expression optimization with [WebAssembly](https://webassembly.org/) IR generation.
Currently, only expressions inside `DoubleField` and `IntRing` are supported.
```kotlin ```kotlin
import space.kscience.kmath.expressions.*
import space.kscience.kmath.operations.*
import space.kscience.kmath.wasm.* import space.kscience.kmath.wasm.*
MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField) MstField { bindSymbol("x") + 2 }.compileToExpression(DoubleField)
@ -99,9 +111,11 @@ Example usage:
```kotlin ```kotlin
import space.kscience.kmath.ast.* import space.kscience.kmath.ast.*
import space.kscience.kmath.ast.rendering.* import space.kscience.kmath.ast.rendering.*
import space.kscience.kmath.misc.*
@OptIn(UnstableKMathAPI::class)
public fun main() { public fun main() {
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath() val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(12)+x^(2/3)".parseMath()
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst) val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax) val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax)
println("LaTeX:") println("LaTeX:")
@ -115,13 +129,78 @@ public fun main() {
Result LaTeX: Result LaTeX:
![](http://chart.googleapis.com/chart?cht=tx&chl=e%5E%7B%5Csqrt%7Bx%7D%7D-%5Cfrac%7B%5Cfrac%7B%5Coperatorname%7Bsin%7D%5E%7B-1%7D%5C,%5Cleft(2%5C,x%5Cright)%7D%7B2%5Ctimes10%5E%7B10%7D%2Bx%5E%7B3%7D%7D%7D%7B-12%7D) ![](https://latex.codecogs.com/gif.latex?%5Coperatorname{exp}%5C,%5Cleft(%5Csqrt{x}%5Cright)-%5Cfrac{%5Cfrac{%5Coperatorname{arcsin}%5C,%5Cleft(2%5C,x%5Cright)}{2%5Ctimes10^{10}%2Bx^{3}}}{12}+x^{2/3})
Result MathML (embedding MathML is not allowed by GitHub Markdown): Result MathML (can be used with MathJax or other renderers):
<details>
```html ```html
<mrow><msup><mrow><mi>e</mi></mrow><mrow><msqrt><mi>x</mi></msqrt></mrow></msup><mo>-</mo><mfrac><mrow><mfrac><mrow><msup><mrow><mo>sin</mo></mrow><mrow><mo>-</mo><mn>1</mn></mrow></msup><mspace width="0.167em"></mspace><mfenced open="(" close=")" separators=""><mn>2</mn><mspace width="0.167em"></mspace><mi>x</mi></mfenced></mrow><mrow><mn>2</mn><mo>&times;</mo><msup><mrow><mn>10</mn></mrow><mrow><mn>10</mn></mrow></msup><mo>+</mo><msup><mrow><mi>x</mi></mrow><mrow><mn>3</mn></mrow></msup></mrow></mfrac></mrow><mrow><mo>-</mo><mn>12</mn></mrow></mfrac></mrow> <math xmlns="https://www.w3.org/1998/Math/MathML">
<mrow>
<mo>exp</mo>
<mspace width="0.167em"></mspace>
<mfenced open="(" close=")" separators="">
<msqrt>
<mi>x</mi>
</msqrt>
</mfenced>
<mo>-</mo>
<mfrac>
<mrow>
<mfrac>
<mrow>
<mo>arcsin</mo>
<mspace width="0.167em"></mspace>
<mfenced open="(" close=")" separators="">
<mn>2</mn>
<mspace width="0.167em"></mspace>
<mi>x</mi>
</mfenced>
</mrow>
<mrow>
<mn>2</mn>
<mo>&times;</mo>
<msup>
<mrow>
<mn>10</mn>
</mrow>
<mrow>
<mn>10</mn>
</mrow>
</msup>
<mo>+</mo>
<msup>
<mrow>
<mi>x</mi>
</mrow>
<mrow>
<mn>3</mn>
</mrow>
</msup>
</mrow>
</mfrac>
</mrow>
<mrow>
<mn>12</mn>
</mrow>
</mfrac>
<mo>+</mo>
<msup>
<mrow>
<mi>x</mi>
</mrow>
<mrow>
<mn>2</mn>
<mo>/</mo>
<mn>3</mn>
</mrow>
</msup>
</mrow>
</math>
``` ```
</details>
It is also possible to create custom algorithms of render, and even add support of other markup languages It is also possible to create custom algorithms of render, and even add support of other markup languages
(see API reference). (see API reference).

View File

@ -118,7 +118,11 @@ public object LatexSyntaxRenderer : SyntaxRenderer {
render(node.right) render(node.right)
} }
is FractionSyntax -> { is FractionSyntax -> if (node.infix) {
render(node.left)
append('/')
render(node.right)
} else {
append("\\frac{") append("\\frac{")
render(node.left) render(node.left)
append("}{") append("}{")

View File

@ -133,14 +133,13 @@ public object MathMLSyntaxRenderer : SyntaxRenderer {
render(node.right) render(node.right)
} }
is FractionSyntax -> tag("mfrac") { is FractionSyntax -> if (node.infix) {
tag("mrow") { render(node.left)
render(node.left) tag("mo") { append('/') }
} render(node.right)
} else tag("mfrac") {
tag("mrow") { tag("mrow") { render(node.left) }
render(node.right) tag("mrow") { render(node.right) }
}
} }
is RadicalWithIndexSyntax -> tag("mroot") { is RadicalWithIndexSyntax -> tag("mroot") {

View File

@ -89,6 +89,7 @@ public open class FeaturedMathRendererWithPostProcess(
SquareRoot.Default, SquareRoot.Default,
Exponent.Default, Exponent.Default,
InverseTrigonometricOperations.Default, InverseTrigonometricOperations.Default,
InverseHyperbolicOperations.Default,
// Fallback option for unknown operations - printing them as operator // Fallback option for unknown operations - printing them as operator
BinaryOperator.Default, BinaryOperator.Default,
@ -105,6 +106,7 @@ public open class FeaturedMathRendererWithPostProcess(
), ),
listOf( listOf(
BetterExponent, BetterExponent,
BetterFraction,
SimplifyParentheses.Default, SimplifyParentheses.Default,
BetterMultiplication, BetterMultiplication,
), ),

View File

@ -102,7 +102,7 @@ public data class SymbolSyntax(public var string: String) : TerminalSyntax()
public data class OperatorNameSyntax(public var name: String) : TerminalSyntax() public data class OperatorNameSyntax(public var name: String) : TerminalSyntax()
/** /**
* Represents a usage of special symbols. * Represents a usage of special symbols (e.g., *&infin;*).
* *
* @property kind The kind of symbol. * @property kind The kind of symbol.
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
@ -143,7 +143,7 @@ public data class OperandSyntax(
} }
/** /**
* Represents unary, prefix operator syntax (like f x). * Represents unary, prefix operator syntax (like *f(x)*).
* *
* @property prefix The prefix. * @property prefix The prefix.
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
@ -160,7 +160,7 @@ public data class UnaryOperatorSyntax(
} }
/** /**
* Represents prefix, unary plus operator. * Represents prefix, unary plus operator (*+x*).
* *
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@ -175,7 +175,7 @@ public data class UnaryPlusSyntax(
} }
/** /**
* Represents prefix, unary minus operator. * Represents prefix, unary minus operator (*-x*).
* *
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@ -190,7 +190,7 @@ public data class UnaryMinusSyntax(
} }
/** /**
* Represents radical with a node inside it. * Represents radical with a node inside it (*&radic;x*).
* *
* @property operand The radicand. * @property operand The radicand.
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
@ -225,7 +225,7 @@ public data class ExponentSyntax(
} }
/** /**
* Represents a syntax node with superscript (usually, for exponentiation). * Represents a syntax node with superscript (*x<sup>2</sup>*).
* *
* @property left The node. * @property left The node.
* @property right The superscript. * @property right The superscript.
@ -244,7 +244,7 @@ public data class SuperscriptSyntax(
} }
/** /**
* Represents a syntax node with subscript. * Represents a syntax node with subscript (*x<sub>i</sup>*).
* *
* @property left The node. * @property left The node.
* @property right The subscript. * @property right The subscript.
@ -263,7 +263,7 @@ public data class SubscriptSyntax(
} }
/** /**
* Represents binary, prefix operator syntax (like f(a, b)). * Represents binary, prefix operator syntax (like *f(a, b)*).
* *
* @property prefix The prefix. * @property prefix The prefix.
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
@ -282,7 +282,7 @@ public data class BinaryOperatorSyntax(
} }
/** /**
* Represents binary, infix addition. * Represents binary, infix addition (*42 + 42*).
* *
* @param left The augend. * @param left The augend.
* @param right The addend. * @param right The addend.
@ -301,7 +301,7 @@ public data class BinaryPlusSyntax(
} }
/** /**
* Represents binary, infix subtraction. * Represents binary, infix subtraction (*42 - 42*).
* *
* @param left The minuend. * @param left The minuend.
* @param right The subtrahend. * @param right The subtrahend.
@ -324,13 +324,15 @@ public data class BinaryMinusSyntax(
* *
* @property left The numerator. * @property left The numerator.
* @property right The denominator. * @property right The denominator.
* @property infix Whether infix (*1 / 2*) or normal (*&frac12;*) fraction should be made.
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public data class FractionSyntax( public data class FractionSyntax(
public override val operation: String, public override val operation: String,
public override val left: MathSyntax, public override val left: OperandSyntax,
public override val right: MathSyntax, public override val right: OperandSyntax,
public var infix: Boolean,
) : BinarySyntax() { ) : BinarySyntax() {
init { init {
left.parent = this left.parent = this
@ -339,7 +341,7 @@ public data class FractionSyntax(
} }
/** /**
* Represents radical syntax with index. * Represents radical syntax with index (*<sup>3</sup>&radic;x*).
* *
* @property left The index. * @property left The index.
* @property right The radicand. * @property right The radicand.
@ -358,11 +360,11 @@ public data class RadicalWithIndexSyntax(
} }
/** /**
* Represents binary, infix multiplication in the form of coefficient (2 x) or with operator (x&times;2). * Represents binary, infix multiplication in the form of coefficient (*2 x*) or with operator (*x &times; 2*).
* *
* @property left The multiplicand. * @property left The multiplicand.
* @property right The multiplier. * @property right The multiplier.
* @property times whether the times (&times;) symbol should be used. * @property times Whether the times (&times;) symbol should be used.
* @author Iaroslav Postovalov * @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI

View File

@ -54,6 +54,7 @@ else
* *('-'? (DIGIT+ ('.' DIGIT+)? ('E' '-'? DIGIT+)? | 'Infinity')) | 'NaN'*. * *('-'? (DIGIT+ ('.' DIGIT+)? ('E' '-'? DIGIT+)? | 'Infinity')) | 'NaN'*.
* *
* @property types The suitable types. * @property types The suitable types.
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : RenderFeature { public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : RenderFeature {
@ -113,6 +114,7 @@ public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : Rend
* Special printing for numeric types which are printed in form of *'-'? DIGIT+*. * Special printing for numeric types which are printed in form of *'-'? DIGIT+*.
* *
* @property types The suitable types. * @property types The suitable types.
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : RenderFeature { public class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : RenderFeature {
@ -135,6 +137,7 @@ public class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : Re
* Special printing for symbols meaning Pi. * Special printing for symbols meaning Pi.
* *
* @property symbols The allowed symbols. * @property symbols The allowed symbols.
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class PrettyPrintPi(public val symbols: Set<String>) : RenderFeature { public class PrettyPrintPi(public val symbols: Set<String>) : RenderFeature {
@ -157,6 +160,7 @@ public class PrettyPrintPi(public val symbols: Set<String>) : RenderFeature {
* not [MST.Unary]. * not [MST.Unary].
* *
* @param operations the allowed operations. If `null`, any operation is accepted. * @param operations the allowed operations. If `null`, any operation is accepted.
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public abstract class Unary(public val operations: Collection<String>?) : RenderFeature { public abstract class Unary(public val operations: Collection<String>?) : RenderFeature {
@ -177,6 +181,7 @@ public abstract class Unary(public val operations: Collection<String>?) : Render
* not [MST.Binary]. * not [MST.Binary].
* *
* @property operations the allowed operations. If `null`, any operation is accepted. * @property operations the allowed operations. If `null`, any operation is accepted.
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public abstract class Binary(public val operations: Collection<String>?) : RenderFeature { public abstract class Binary(public val operations: Collection<String>?) : RenderFeature {
@ -193,6 +198,8 @@ public abstract class Binary(public val operations: Collection<String>?) : Rende
/** /**
* Handles binary nodes by producing [BinaryPlusSyntax]. * Handles binary nodes by producing [BinaryPlusSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class BinaryPlus(operations: Collection<String>?) : Binary(operations) { public class BinaryPlus(operations: Collection<String>?) : Binary(operations) {
@ -213,6 +220,8 @@ public class BinaryPlus(operations: Collection<String>?) : Binary(operations) {
/** /**
* Handles binary nodes by producing [BinaryMinusSyntax]. * Handles binary nodes by producing [BinaryMinusSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class BinaryMinus(operations: Collection<String>?) : Binary(operations) { public class BinaryMinus(operations: Collection<String>?) : Binary(operations) {
@ -233,6 +242,8 @@ public class BinaryMinus(operations: Collection<String>?) : Binary(operations) {
/** /**
* Handles unary nodes by producing [UnaryPlusSyntax]. * Handles unary nodes by producing [UnaryPlusSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class UnaryPlus(operations: Collection<String>?) : Unary(operations) { public class UnaryPlus(operations: Collection<String>?) : Unary(operations) {
@ -251,6 +262,8 @@ public class UnaryPlus(operations: Collection<String>?) : Unary(operations) {
/** /**
* Handles binary nodes by producing [UnaryMinusSyntax]. * Handles binary nodes by producing [UnaryMinusSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class UnaryMinus(operations: Collection<String>?) : Unary(operations) { public class UnaryMinus(operations: Collection<String>?) : Unary(operations) {
@ -269,13 +282,16 @@ public class UnaryMinus(operations: Collection<String>?) : Unary(operations) {
/** /**
* Handles binary nodes by producing [FractionSyntax]. * Handles binary nodes by producing [FractionSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class Fraction(operations: Collection<String>?) : Binary(operations) { public class Fraction(operations: Collection<String>?) : Binary(operations) {
public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): FractionSyntax = FractionSyntax( public override fun renderBinary(parent: FeaturedMathRenderer, node: MST.Binary): FractionSyntax = FractionSyntax(
operation = node.operation, operation = node.operation,
left = parent.render(node.left), left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
right = parent.render(node.right), right = OperandSyntax(operand = parent.render(node.right), parentheses = true),
infix = true,
) )
public companion object { public companion object {
@ -288,6 +304,8 @@ public class Fraction(operations: Collection<String>?) : Binary(operations) {
/** /**
* Handles binary nodes by producing [BinaryOperatorSyntax]. * Handles binary nodes by producing [BinaryOperatorSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class BinaryOperator(operations: Collection<String>?) : Binary(operations) { public class BinaryOperator(operations: Collection<String>?) : Binary(operations) {
@ -309,6 +327,8 @@ public class BinaryOperator(operations: Collection<String>?) : Binary(operations
/** /**
* Handles unary nodes by producing [UnaryOperatorSyntax]. * Handles unary nodes by producing [UnaryOperatorSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class UnaryOperator(operations: Collection<String>?) : Unary(operations) { public class UnaryOperator(operations: Collection<String>?) : Unary(operations) {
@ -329,6 +349,8 @@ public class UnaryOperator(operations: Collection<String>?) : Unary(operations)
/** /**
* Handles binary nodes by producing [SuperscriptSyntax]. * Handles binary nodes by producing [SuperscriptSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class Power(operations: Collection<String>?) : Binary(operations) { public class Power(operations: Collection<String>?) : Binary(operations) {
@ -365,6 +387,8 @@ public class SquareRoot(operations: Collection<String>?) : Unary(operations) {
/** /**
* Handles unary nodes by producing [ExponentSyntax]. * Handles unary nodes by producing [ExponentSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class Exponent(operations: Collection<String>?) : Unary(operations) { public class Exponent(operations: Collection<String>?) : Unary(operations) {
@ -384,6 +408,8 @@ public class Exponent(operations: Collection<String>?) : Unary(operations) {
/** /**
* Handles binary nodes by producing [MultiplicationSyntax]. * Handles binary nodes by producing [MultiplicationSyntax].
*
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class Multiplication(operations: Collection<String>?) : Binary(operations) { public class Multiplication(operations: Collection<String>?) : Binary(operations) {
@ -404,36 +430,52 @@ public class Multiplication(operations: Collection<String>?) : Binary(operations
} }
/** /**
* Handles binary nodes by producing inverse [UnaryOperatorSyntax] (like *sin<sup>-1</sup>*) with removing the `a` * Handles binary nodes by producing inverse [UnaryOperatorSyntax] with *arc* prefix instead of *a*.
* prefix of operation ID. *
* @author Iaroslav Postovalov
*/ */
@UnstableKMathAPI @UnstableKMathAPI
public class InverseTrigonometricOperations(operations: Collection<String>?) : Unary(operations) { public class InverseTrigonometricOperations(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax = public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax =
UnaryOperatorSyntax( UnaryOperatorSyntax(
operation = node.operation, operation = node.operation,
prefix = SuperscriptSyntax( prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "arc")),
operation = PowerOperations.POW_OPERATION,
left = OperatorNameSyntax(name = node.operation.removePrefix("a")),
right = UnaryMinusSyntax(
operation = GroupOperations.MINUS_OPERATION,
operand = OperandSyntax(operand = NumberSyntax(string = "1"), parentheses = true),
),
),
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true), operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
) )
public companion object { public companion object {
/** /**
* The default instance configured with [TrigonometricOperations.ACOS_OPERATION], * The default instance configured with [TrigonometricOperations.ACOS_OPERATION],
* [TrigonometricOperations.ASIN_OPERATION], [TrigonometricOperations.ATAN_OPERATION], * [TrigonometricOperations.ASIN_OPERATION], [TrigonometricOperations.ATAN_OPERATION].
* [ExponentialOperations.ACOSH_OPERATION], [ExponentialOperations.ASINH_OPERATION], and
* [ExponentialOperations.ATANH_OPERATION].
*/ */
public val Default: InverseTrigonometricOperations = InverseTrigonometricOperations(setOf( public val Default: InverseTrigonometricOperations = InverseTrigonometricOperations(setOf(
TrigonometricOperations.ACOS_OPERATION, TrigonometricOperations.ACOS_OPERATION,
TrigonometricOperations.ASIN_OPERATION, TrigonometricOperations.ASIN_OPERATION,
TrigonometricOperations.ATAN_OPERATION, TrigonometricOperations.ATAN_OPERATION,
))
}
}
/**
* Handles binary nodes by producing inverse [UnaryOperatorSyntax] with *ar* prefix instead of *a*.
*
* @author Iaroslav Postovalov
*/
@UnstableKMathAPI
public class InverseHyperbolicOperations(operations: Collection<String>?) : Unary(operations) {
public override fun renderUnary(parent: FeaturedMathRenderer, node: MST.Unary): UnaryOperatorSyntax =
UnaryOperatorSyntax(
operation = node.operation,
prefix = OperatorNameSyntax(name = node.operation.replaceFirst("a", "ar")),
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
)
public companion object {
/**
* The default instance configured with [ExponentialOperations.ACOSH_OPERATION],
* [ExponentialOperations.ASINH_OPERATION], and [ExponentialOperations.ATANH_OPERATION].
*/
public val Default: InverseHyperbolicOperations = InverseHyperbolicOperations(setOf(
ExponentialOperations.ACOSH_OPERATION, ExponentialOperations.ACOSH_OPERATION,
ExponentialOperations.ASINH_OPERATION, ExponentialOperations.ASINH_OPERATION,
ExponentialOperations.ATANH_OPERATION, ExponentialOperations.ATANH_OPERATION,

View File

@ -83,6 +83,75 @@ public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostPro
} }
} }
/**
* Chooses [FractionSyntax.infix] depending on the context.
*
* @author Iaroslav Postovalov
*/
@UnstableKMathAPI
public object BetterFraction : FeaturedMathRendererWithPostProcess.PostProcessStage {
private fun perform0(node: MathSyntax, infix: Boolean = false): Unit = when (node) {
is NumberSyntax -> Unit
is SymbolSyntax -> Unit
is OperatorNameSyntax -> Unit
is SpecialSymbolSyntax -> Unit
is OperandSyntax -> perform0(node.operand, infix)
is UnaryOperatorSyntax -> {
perform0(node.prefix, infix)
perform0(node.operand, infix)
}
is UnaryPlusSyntax -> perform0(node.operand, infix)
is UnaryMinusSyntax -> perform0(node.operand, infix)
is RadicalSyntax -> perform0(node.operand, infix)
is ExponentSyntax -> perform0(node.operand, infix)
is SuperscriptSyntax -> {
perform0(node.left, true)
perform0(node.right, true)
}
is SubscriptSyntax -> {
perform0(node.left, true)
perform0(node.right, true)
}
is BinaryOperatorSyntax -> {
perform0(node.prefix, infix)
perform0(node.left, infix)
perform0(node.right, infix)
}
is BinaryPlusSyntax -> {
perform0(node.left, infix)
perform0(node.right, infix)
}
is BinaryMinusSyntax -> {
perform0(node.left, infix)
perform0(node.right, infix)
}
is FractionSyntax -> {
node.infix = infix
perform0(node.left, infix)
perform0(node.right, infix)
}
is RadicalWithIndexSyntax -> {
perform0(node.left, true)
perform0(node.right, true)
}
is MultiplicationSyntax -> {
perform0(node.left, infix)
perform0(node.right, infix)
}
}
public override fun perform(node: MathSyntax): Unit = perform0(node)
}
/** /**
* Applies [ExponentSyntax.useOperatorForm] to [ExponentSyntax] when the operand contains a fraction, a * Applies [ExponentSyntax.useOperatorForm] to [ExponentSyntax] when the operand contains a fraction, a
@ -102,7 +171,7 @@ public object BetterExponent : FeaturedMathRendererWithPostProcess.PostProcessSt
is UnaryOperatorSyntax -> perform0(node.prefix) || perform0(node.operand) is UnaryOperatorSyntax -> perform0(node.prefix) || perform0(node.operand)
is UnaryPlusSyntax -> perform0(node.operand) is UnaryPlusSyntax -> perform0(node.operand)
is UnaryMinusSyntax -> perform0(node.operand) is UnaryMinusSyntax -> perform0(node.operand)
is RadicalSyntax -> perform0(node.operand) is RadicalSyntax -> true
is ExponentSyntax -> { is ExponentSyntax -> {
val r = perform0(node.operand) val r = perform0(node.operand)
@ -116,7 +185,7 @@ public object BetterExponent : FeaturedMathRendererWithPostProcess.PostProcessSt
is BinaryPlusSyntax -> perform0(node.left) || perform0(node.right) is BinaryPlusSyntax -> perform0(node.left) || perform0(node.right)
is BinaryMinusSyntax -> perform0(node.left) || perform0(node.right) is BinaryMinusSyntax -> perform0(node.left) || perform0(node.right)
is FractionSyntax -> true is FractionSyntax -> true
is RadicalWithIndexSyntax -> perform0(node.left) || perform0(node.right) is RadicalWithIndexSyntax -> true
is MultiplicationSyntax -> perform0(node.left) || perform0(node.right) is MultiplicationSyntax -> perform0(node.left) || perform0(node.right)
} }
} }
@ -163,8 +232,11 @@ public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) ->
val isInsideExpOperator = val isInsideExpOperator =
node.parent is ExponentSyntax && (node.parent as ExponentSyntax).useOperatorForm node.parent is ExponentSyntax && (node.parent as ExponentSyntax).useOperatorForm
val isOnOrUnderNormalFraction = node.parent is FractionSyntax && !((node.parent as FractionSyntax).infix)
node.parentheses = !isRightOfSuperscript node.parentheses = !isRightOfSuperscript
&& (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax || isInsideExpOperator) && (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax || isInsideExpOperator)
&& !isOnOrUnderNormalFraction
perform(node.operand) perform(node.operand)
} }

View File

@ -99,13 +99,17 @@ internal class TestFeatures {
fun multiplication() = testLatex("x*1", "x\\times1") fun multiplication() = testLatex("x*1", "x\\times1")
@Test @Test
fun inverseTrigonometry() { fun inverseTrigonometric() {
testLatex("asin(x)", "\\operatorname{sin}^{-1}\\,\\left(x\\right)") testLatex("asin(x)", "\\operatorname{arcsin}\\,\\left(x\\right)")
testLatex("asinh(x)", "\\operatorname{sinh}^{-1}\\,\\left(x\\right)") testLatex("acos(x)", "\\operatorname{arccos}\\,\\left(x\\right)")
testLatex("acos(x)", "\\operatorname{cos}^{-1}\\,\\left(x\\right)") testLatex("atan(x)", "\\operatorname{arctan}\\,\\left(x\\right)")
testLatex("acosh(x)", "\\operatorname{cosh}^{-1}\\,\\left(x\\right)") }
testLatex("atan(x)", "\\operatorname{tan}^{-1}\\,\\left(x\\right)")
testLatex("atanh(x)", "\\operatorname{tanh}^{-1}\\,\\left(x\\right)") @Test
fun inverseHyperbolic() {
testLatex("asinh(x)", "\\operatorname{arsinh}\\,\\left(x\\right)")
testLatex("acosh(x)", "\\operatorname{arcosh}\\,\\left(x\\right)")
testLatex("atanh(x)", "\\operatorname{artanh}\\,\\left(x\\right)")
} }
// @Test // @Test

View File

@ -37,4 +37,10 @@ internal class TestStages {
testLatex("exp(x/2)", "\\operatorname{exp}\\,\\left(\\frac{x}{2}\\right)") testLatex("exp(x/2)", "\\operatorname{exp}\\,\\left(\\frac{x}{2}\\right)")
testLatex("exp(x^2)", "\\operatorname{exp}\\,\\left(x^{2}\\right)") testLatex("exp(x^2)", "\\operatorname{exp}\\,\\left(x^{2}\\right)")
} }
@Test
fun fraction() {
testLatex("x/y", "\\frac{x}{y}")
testLatex("x^(x/y)", "x^{x/y}")
}
} }

View File

@ -15,7 +15,7 @@ The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0-d
```gradle ```gradle
repositories { repositories {
maven { url 'https://repo.kotlin.link' } maven { url 'https://repo.kotlin.link' }
maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap mavenCentral()
} }
dependencies { dependencies {
@ -26,7 +26,7 @@ dependencies {
```kotlin ```kotlin
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap mavenCentral()
} }
dependencies { dependencies {

View File

@ -17,7 +17,7 @@ The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0-
```gradle ```gradle
repositories { repositories {
maven { url 'https://repo.kotlin.link' } maven { url 'https://repo.kotlin.link' }
maven { url "https://dl.bintray.com/kotlin/kotlin-eap" } // include for builds based on kotlin-eap mavenCentral()
} }
dependencies { dependencies {
@ -28,7 +28,7 @@ dependencies {
```kotlin ```kotlin
repositories { repositories {
maven("https://repo.kotlin.link") maven("https://repo.kotlin.link")
maven("https://dl.bintray.com/kotlin/kotlin-eap") // include for builds based on kotlin-eap mavenCentral()
} }
dependencies { dependencies {