From fbe1ab94a4127263b09b5182797deaeab5012e2c Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 29 Oct 2020 19:35:08 +0300 Subject: [PATCH 1/2] Change DifferentiableExpression API to use ordered symbol list instead of orders map. --- .../DerivativeStructureExpression.kt | 42 +++++++++++-------- .../DerivativeStructureExpressionTest.kt | 22 ++++++---- .../expressions/DifferentiableExpression.kt | 20 ++++----- .../kscience/kmath/expressions/Expression.kt | 2 +- 4 files changed, 48 insertions(+), 38 deletions(-) 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) } From 6f31ddba301b2cfb2a8ebbc900afb566b510f612 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Thu, 29 Oct 2020 19:50:45 +0300 Subject: [PATCH 2/2] Fix CM DerivativeStructureField constants --- .../expressions/DerivativeStructureExpression.kt | 10 +++++----- .../kmath/commons/optimization/OptimizeTest.kt | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) 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 e4311a56b..244dc1314 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 @@ -14,8 +14,10 @@ public class DerivativeStructureField( public val order: Int, bindings: Map, ) : ExtendedField, ExpressionAlgebra { - public override val zero: DerivativeStructure by lazy { DerivativeStructure(bindings.size, 0) } - public override val one: DerivativeStructure by lazy { DerivativeStructure(bindings.size, 0, 1.0) } + public val numberOfVariables: Int = bindings.size + + public override val zero: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order) } + public override val one: DerivativeStructure by lazy { DerivativeStructure(numberOfVariables, order, 1.0) } /** * A class that implements both [DerivativeStructure] and a [Symbol] @@ -32,8 +34,6 @@ public class DerivativeStructureField( override fun hashCode(): Int = identity.hashCode() } - public val numberOfVariables: Int = bindings.size - /** * Identity-based symbol bindings map */ @@ -41,7 +41,7 @@ public class DerivativeStructureField( key.identity to DerivativeStructureSymbol(numberOfVariables, index, key, value) }.toMap() - override fun const(value: Double): DerivativeStructure = DerivativeStructure(numberOfVariables, 0, value) + override fun const(value: Double): DerivativeStructure = DerivativeStructure(numberOfVariables, order, value) public override fun bindOrNull(symbol: Symbol): DerivativeStructureSymbol? = variables[symbol.identity] diff --git a/kmath-commons/src/test/kotlin/kscience/kmath/commons/optimization/OptimizeTest.kt b/kmath-commons/src/test/kotlin/kscience/kmath/commons/optimization/OptimizeTest.kt index 4384a5124..fa1978f95 100644 --- a/kmath-commons/src/test/kotlin/kscience/kmath/commons/optimization/OptimizeTest.kt +++ b/kmath-commons/src/test/kotlin/kscience/kmath/commons/optimization/OptimizeTest.kt @@ -6,7 +6,6 @@ import kscience.kmath.stat.Distribution import kscience.kmath.stat.Fitting import kscience.kmath.stat.RandomGenerator import kscience.kmath.stat.normal -import kscience.kmath.structures.asBuffer import org.junit.jupiter.api.Test import kotlin.math.pow @@ -53,7 +52,7 @@ internal class OptimizeTest { it.pow(2) + it + 1 + chain.nextDouble() } val yErr = x.map { sigma } - val chi2 = Fitting.chiSquared(x.asBuffer(), y.asBuffer(), yErr.asBuffer()) { x -> + val chi2 = Fitting.chiSquared(x, y, yErr) { x -> val cWithDefault = bindOrNull(c) ?: one bind(a) * x.pow(2) + bind(b) * x + cWithDefault }