Dev #194

Merged
altavir merged 266 commits from dev into master 2021-01-20 17:32:32 +03:00
6 changed files with 48 additions and 18 deletions
Showing only changes of commit d631c048c7 - Show all commits

View File

@ -38,7 +38,9 @@ This subproject implements the following features:
> ``` > ```
> >
## Dynamic Expression Code Generation with ObjectWeb ASM ## Dynamic expression code generation
### 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 special implementation of `Expression<T>` with implemented `invoke` function. a special implementation of `Expression<T>` with implemented `invoke` function.
@ -55,19 +57,20 @@ RealField.mstInField { symbol("x") + 2 }.compile()
package kscience.kmath.asm.generated; package kscience.kmath.asm.generated;
import java.util.Map; import java.util.Map;
import kotlin.jvm.functions.Function2;
import kscience.kmath.asm.internal.MapIntrinsics; import kscience.kmath.asm.internal.MapIntrinsics;
import kscience.kmath.expressions.Expression; import kscience.kmath.expressions.Expression;
import kscience.kmath.operations.RealField; import kscience.kmath.expressions.Symbol;
public final class AsmCompiledExpression_1073786867_0 implements Expression<Double> { public final class AsmCompiledExpression_45045_0 implements Expression<Double> {
private final RealField algebra; private final Object[] constants;
public final Double invoke(Map<String, ? extends Double> arguments) { public final Double invoke(Map<Symbol, Double> arguments) {
return (Double)this.algebra.add(((Double)MapIntrinsics.getOrFail(arguments, "x")).doubleValue(), 2.0D); return (Double)((Function2)this.constants[0]).invoke((Double)MapIntrinsics.getOrFail(arguments, "x"), 2);
} }
public AsmCompiledExpression_1073786867_0(RealField algebra) { public AsmCompiledExpression_45045_0(Object[] constants) {
this.algebra = algebra; this.constants = constants;
} }
} }
@ -82,10 +85,30 @@ RealField.mstInField { symbol("x") + 2 }.compile()
RealField.expression("x+2".parseMath()) RealField.expression("x+2".parseMath())
``` ```
### 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 loading overhead. class 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.
Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis). Contributed by [Iaroslav Postovalov](https://github.com/CommanderTvis).
### On JS
A similar feature is also available on JS.
```kotlin
RealField.mstInField { symbol("x") + 2 }.compile()
```
The code above returns expression implemented with such a JS function:
```js
var executable = function (constants, arguments) {
return constants[1](constants[0](arguments, "x"), 2);
};
```
#### Known issues
- This feature uses `eval` which can be unavailable in several environments.

View File

@ -27,7 +27,9 @@ public object MstSpace : Space<MST>, NumericAlgebra<MST> {
public override fun add(a: MST, b: MST): MST.Binary = binaryOperation(SpaceOperations.PLUS_OPERATION)(a, b) public override fun add(a: MST, b: MST): MST.Binary = binaryOperation(SpaceOperations.PLUS_OPERATION)(a, b)
public override operator fun MST.unaryPlus(): MST.Unary = unaryOperation(SpaceOperations.PLUS_OPERATION)(this) public override operator fun MST.unaryPlus(): MST.Unary = unaryOperation(SpaceOperations.PLUS_OPERATION)(this)
public override operator fun MST.unaryMinus(): MST.Unary = unaryOperation(SpaceOperations.MINUS_OPERATION)(this) public override operator fun MST.unaryMinus(): MST.Unary = unaryOperation(SpaceOperations.MINUS_OPERATION)(this)
public override operator fun MST.minus(b: MST): MST.Binary = binaryOperation(SpaceOperations.MINUS_OPERATION)(this, b)
public override operator fun MST.minus(b: MST): MST.Binary =
binaryOperation(SpaceOperations.MINUS_OPERATION)(this, b)
public override fun multiply(a: MST, k: Number): MST.Binary = public override fun multiply(a: MST, k: Number): MST.Binary =
binaryOperation(RingOperations.TIMES_OPERATION)(a, number(k)) binaryOperation(RingOperations.TIMES_OPERATION)(a, number(k))

View File

@ -2,13 +2,16 @@ package kscience.kmath.asm.internal
import kscience.kmath.asm.internal.AsmBuilder.ClassLoader import kscience.kmath.asm.internal.AsmBuilder.ClassLoader
import kscience.kmath.ast.MST import kscience.kmath.ast.MST
import kscience.kmath.ast.mstInField
import kscience.kmath.expressions.Expression import kscience.kmath.expressions.Expression
import kscience.kmath.operations.RealField
import org.objectweb.asm.* import org.objectweb.asm.*
import org.objectweb.asm.Opcodes.* import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.Type.* import org.objectweb.asm.Type.*
import org.objectweb.asm.commons.InstructionAdapter import org.objectweb.asm.commons.InstructionAdapter
import java.lang.invoke.MethodHandles import java.lang.invoke.MethodHandles
import java.lang.invoke.MethodType import java.lang.invoke.MethodType
import java.lang.reflect.Modifier
import java.util.stream.Collectors.toMap import java.util.stream.Collectors.toMap
import kotlin.contracts.InvocationKind import kotlin.contracts.InvocationKind
import kotlin.contracts.contract import kotlin.contracts.contract
@ -82,7 +85,7 @@ internal class AsmBuilder<T>(
ACC_PUBLIC or ACC_FINAL, ACC_PUBLIC or ACC_FINAL,
"invoke", "invoke",
getMethodDescriptor(tType, MAP_TYPE), getMethodDescriptor(tType, MAP_TYPE),
"(L${MAP_TYPE.internalName}<${STRING_TYPE.descriptor}+${tType.descriptor}>;)${tType.descriptor}", "(L${MAP_TYPE.internalName}<${SYMBOL_TYPE.descriptor}${if (Modifier.isFinal(classOfT.modifiers)) "" else "+"}${tType.descriptor}>;)${tType.descriptor}",
null, null,
).instructionAdapter { ).instructionAdapter {
invokeMethodVisitor = this invokeMethodVisitor = this
@ -159,7 +162,7 @@ internal class AsmBuilder<T>(
"<init>", "<init>",
getMethodDescriptor(VOID_TYPE, *OBJECT_ARRAY_TYPE.wrapToArrayIf { hasConstants }), getMethodDescriptor(VOID_TYPE, *OBJECT_ARRAY_TYPE.wrapToArrayIf { hasConstants }),
null, null,
null null,
).instructionAdapter { ).instructionAdapter {
val l0 = label() val l0 = label()
load(0, classType) load(0, classType)
@ -190,6 +193,7 @@ internal class AsmBuilder<T>(
} }
val cls = classLoader.defineClass(className, classWriter.toByteArray()) val cls = classLoader.defineClass(className, classWriter.toByteArray())
java.io.File("dump.class").writeBytes(classWriter.toByteArray())
val l = MethodHandles.publicLookup() val l = MethodHandles.publicLookup()
if (hasConstants) if (hasConstants)
@ -334,5 +338,10 @@ internal class AsmBuilder<T>(
* ASM type for MapIntrinsics. * ASM type for MapIntrinsics.
*/ */
val MAP_INTRINSICS_TYPE: Type by lazy { getObjectType("kscience/kmath/asm/internal/MapIntrinsics") } val MAP_INTRINSICS_TYPE: Type by lazy { getObjectType("kscience/kmath/asm/internal/MapIntrinsics") }
/**
* ASM Type for [kscience.kmath.expressions.Symbol].
*/
val SYMBOL_TYPE: Type by lazy { getObjectType("kscience/kmath/expressions/Symbol") }
} }
} }

View File

@ -2,8 +2,6 @@ package kscience.kmath.ast
import kscience.kmath.asm.compile import kscience.kmath.asm.compile
import kscience.kmath.asm.expression import kscience.kmath.asm.expression
import kscience.kmath.ast.mstInField
import kscience.kmath.ast.parseMath
import kscience.kmath.expressions.invoke import kscience.kmath.expressions.invoke
import kscience.kmath.operations.Complex import kscience.kmath.operations.Complex
import kscience.kmath.operations.ComplexField import kscience.kmath.operations.ComplexField

View File

@ -1,7 +1,5 @@
package kscience.kmath.ast package kscience.kmath.ast
import kscience.kmath.ast.evaluate
import kscience.kmath.ast.parseMath
import kscience.kmath.operations.Field import kscience.kmath.operations.Field
import kscience.kmath.operations.RealField import kscience.kmath.operations.RealField
import kotlin.test.Test import kotlin.test.Test

View File

@ -275,12 +275,12 @@ public interface SpaceOperations<T> : Algebra<T> {
public companion object { public companion object {
/** /**
* The identifier of addition and unary positive. * The identifier of addition and unary positive operator.
*/ */
public const val PLUS_OPERATION: String = "+" public const val PLUS_OPERATION: String = "+"
/** /**
* The identifier of subtraction and unary negation. * The identifier of subtraction and unary negative operator.
*/ */
public const val MINUS_OPERATION: String = "-" public const val MINUS_OPERATION: String = "-"
} }