diff --git a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt index c593f5103..e4311a56b 100644 --- a/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt +++ b/kmath-commons/src/main/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpression.kt @@ -12,46 +12,51 @@ import org.apache.commons.math3.analysis.differentiation.DerivativeStructure */ public class DerivativeStructureField( public val order: Int, - private val bindings: Map + bindings: Map, ) : ExtendedField, ExpressionAlgebra { - public override val zero: DerivativeStructure by lazy { DerivativeStructure(bindings.size, order) } - public override val one: DerivativeStructure by lazy { DerivativeStructure(bindings.size, order, 1.0) } + public override val zero: DerivativeStructure by lazy { DerivativeStructure(bindings.size, 0) } + public override val one: DerivativeStructure by lazy { DerivativeStructure(bindings.size, 0, 1.0) } /** * A class that implements both [DerivativeStructure] and a [Symbol] */ - public inner class DerivativeStructureSymbol(symbol: Symbol, value: Double) : - DerivativeStructure(bindings.size, order, bindings.keys.indexOf(symbol), value), Symbol { + public inner class DerivativeStructureSymbol( + size: Int, + index: Int, + symbol: Symbol, + value: Double, + ) : DerivativeStructure(size, order, index, value), Symbol { override val identity: String = symbol.identity override fun toString(): String = identity override fun equals(other: Any?): Boolean = this.identity == (other as? Symbol)?.identity override fun hashCode(): Int = identity.hashCode() } + public val numberOfVariables: Int = bindings.size + /** * Identity-based symbol bindings map */ - private val variables: Map = bindings.entries.associate { (key, value) -> - key.identity to DerivativeStructureSymbol(key, value) - } + private val variables: Map = bindings.entries.mapIndexed { index, (key, value) -> + key.identity to DerivativeStructureSymbol(numberOfVariables, index, key, value) + }.toMap() - override fun const(value: Double): DerivativeStructure = DerivativeStructure(bindings.size, order, value) + override fun const(value: Double): DerivativeStructure = DerivativeStructure(numberOfVariables, 0, value) public override fun bindOrNull(symbol: Symbol): DerivativeStructureSymbol? = variables[symbol.identity] public fun bind(symbol: Symbol): DerivativeStructureSymbol = variables.getValue(symbol.identity) - //public fun Number.const(): DerivativeStructure = const(toDouble()) + override fun symbol(value: String): DerivativeStructureSymbol = bind(StringSymbol(value)) - public fun DerivativeStructure.derivative(parameter: Symbol, order: Int = 1): Double { - return derivative(mapOf(parameter to order)) + public fun DerivativeStructure.derivative(symbols: List): Double { + require(symbols.size <= order) { "The order of derivative ${symbols.size} exceeds computed order $order" } + val ordersCount = symbols.map { it.identity }.groupBy { it }.mapValues { it.value.size } + return getPartialDerivative(*variables.keys.map { ordersCount[it] ?: 0 }.toIntArray()) } - public fun DerivativeStructure.derivative(orders: Map): Double { - return getPartialDerivative(*bindings.keys.map { orders[it] ?: 0 }.toIntArray()) - } + public fun DerivativeStructure.derivative(vararg symbols: Symbol): Double = derivative(symbols.toList()) - public fun DerivativeStructure.derivative(vararg orders: Pair): Double = derivative(mapOf(*orders)) public override fun add(a: DerivativeStructure, b: DerivativeStructure): DerivativeStructure = a.add(b) public override fun multiply(a: DerivativeStructure, k: Number): DerivativeStructure = when (k) { @@ -97,6 +102,7 @@ public class DerivativeStructureField( } } + /** * A constructs that creates a derivative structure with required order on-demand */ @@ -109,7 +115,7 @@ public class DerivativeStructureExpression( /** * Get the derivative expression with given orders */ - public override fun derivativeOrNull(orders: Map): Expression = Expression { arguments -> - with(DerivativeStructureField(orders.values.maxOrNull() ?: 0, arguments)) { function().derivative(orders) } + public override fun derivativeOrNull(symbols: List): Expression = Expression { arguments -> + with(DerivativeStructureField(symbols.size, arguments)) { function().derivative(symbols) } } } diff --git a/kmath-commons/src/test/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpressionTest.kt b/kmath-commons/src/test/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpressionTest.kt index 8886e123f..7511a38ed 100644 --- a/kmath-commons/src/test/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpressionTest.kt +++ b/kmath-commons/src/test/kotlin/kscience/kmath/commons/expressions/DerivativeStructureExpressionTest.kt @@ -5,14 +5,15 @@ import kotlin.contracts.InvocationKind import kotlin.contracts.contract import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFails -internal inline fun diff( +internal inline fun diff( order: Int, vararg parameters: Pair, - block: DerivativeStructureField.() -> R, -): R { + block: DerivativeStructureField.() -> Unit, +): Unit { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - return DerivativeStructureField(order, mapOf(*parameters)).run(block) + DerivativeStructureField(order, mapOf(*parameters)).run(block) } internal class AutoDiffTest { @@ -21,13 +22,16 @@ internal class AutoDiffTest { @Test fun derivativeStructureFieldTest() { - val res: Double = diff(3, x to 1.0, y to 1.0) { + diff(2, x to 1.0, y to 1.0) { val x = bind(x)//by binding() val y = symbol("y") - val z = x * (-sin(x * y) + y) - z.derivative(x) + val z = x * (-sin(x * y) + y) + 2.0 + println(z.derivative(x)) + println(z.derivative(y,x)) + assertEquals(z.derivative(x, y), z.derivative(y, x)) + //check that improper order cause failure + assertFails { z.derivative(x,x,y) } } - println(res) } @Test @@ -40,5 +44,7 @@ internal class AutoDiffTest { assertEquals(10.0, f(x to 1.0, y to 2.0)) assertEquals(6.0, f.derivative(x)(x to 1.0, y to 2.0)) + assertEquals(2.0, f.derivative(x, x)(x to 1.234, y to -2.0)) + assertEquals(2.0, f.derivative(x, y)(x to 1.0, y to 2.0)) } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/DifferentiableExpression.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/DifferentiableExpression.kt index 4fe73f283..ac1f4bc20 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/DifferentiableExpression.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/DifferentiableExpression.kt @@ -3,20 +3,18 @@ package kscience.kmath.expressions /** * An expression that provides derivatives */ -public interface DifferentiableExpression : Expression{ - public fun derivativeOrNull(orders: Map): Expression? +public interface DifferentiableExpression : Expression { + public fun derivativeOrNull(symbols: List): Expression? } -public fun DifferentiableExpression.derivative(orders: Map): Expression = - derivativeOrNull(orders) ?: error("Derivative with orders $orders not provided") +public fun DifferentiableExpression.derivative(symbols: List): Expression = + derivativeOrNull(symbols) ?: error("Derivative by symbols $symbols not provided") -public fun DifferentiableExpression.derivative(vararg orders: Pair): Expression = - derivative(mapOf(*orders)) - -public fun DifferentiableExpression.derivative(symbol: Symbol): Expression = derivative(symbol to 1) +public fun DifferentiableExpression.derivative(vararg symbols: Symbol): Expression = + derivative(symbols.toList()) public fun DifferentiableExpression.derivative(name: String): Expression = - derivative(StringSymbol(name) to 1) + derivative(StringSymbol(name)) /** * A [DifferentiableExpression] that defines only first derivatives @@ -25,8 +23,8 @@ public abstract class FirstDerivativeExpression : DifferentiableExpression public abstract fun derivativeOrNull(symbol: Symbol): Expression? - public override fun derivativeOrNull(orders: Map): Expression? { - val dSymbol = orders.entries.singleOrNull { it.value == 1 }?.key ?: return null + public override fun derivativeOrNull(symbols: List): Expression? { + val dSymbol = symbols.firstOrNull() ?: return null return derivativeOrNull(dSymbol) } } diff --git a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt index ab9ff0e72..8f408e09e 100644 --- a/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt +++ b/kmath-core/src/commonMain/kotlin/kscience/kmath/expressions/Expression.kt @@ -87,7 +87,7 @@ public fun ExpressionAlgebra.bind(symbol: Symbol): E = /** * A delegate to create a symbol with a string identity in this scope */ -public val symbol: ReadOnlyProperty = ReadOnlyProperty { thisRef, property -> +public val symbol: ReadOnlyProperty = ReadOnlyProperty { _, property -> StringSymbol(property.name) }