Norm is a separate context

This commit is contained in:
Alexander Nozik 2018-11-14 23:26:38 +03:00
parent be18014d54
commit 68bd0ae0af
14 changed files with 168 additions and 101 deletions

View File

@ -1,7 +1,8 @@
buildscript { buildscript {
ext.kotlin_version = '1.3.0' ext.kotlin_version = '1.3.10'
repositories { repositories {
mavenCentral()
jcenter() jcenter()
} }
@ -17,6 +18,11 @@ allprojects {
group = 'scientifik' group = 'scientifik'
version = '0.0.1-SNAPSHOT' version = '0.0.1-SNAPSHOT'
repositories {
mavenCentral()
jcenter()
}
} }
if(file('artifactory.gradle').exists()){ if(file('artifactory.gradle').exists()){

View File

@ -2,11 +2,6 @@ plugins {
id 'kotlin-multiplatform' id 'kotlin-multiplatform'
} }
repositories {
maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' }
mavenCentral()
}
kotlin { kotlin {
targets { targets {
fromPreset(presets.jvm, 'jvm') fromPreset(presets.jvm, 'jvm')

View File

@ -1,9 +1,6 @@
package scientifik.kmath.linear package scientifik.kmath.linear
import scientifik.kmath.operations.DoubleField import scientifik.kmath.operations.*
import scientifik.kmath.operations.Field
import scientifik.kmath.operations.Space
import scientifik.kmath.operations.SpaceElement
import scientifik.kmath.structures.* import scientifik.kmath.structures.*
/** /**
@ -70,8 +67,6 @@ abstract class MatrixSpace<T : Any>(val rows: Int, val columns: Int, val field:
result = 31 * result + field.hashCode() result = 31 * result + field.hashCode()
return result return result
} }
} }
infix fun <T : Any> Matrix<T>.dot(b: Matrix<T>): Matrix<T> = this.context.multiply(this, b) infix fun <T : Any> Matrix<T>.dot(b: Matrix<T>): Matrix<T> = this.context.multiply(this, b)
@ -191,11 +186,11 @@ interface Vector<T : Any> : SpaceElement<Vector<T>, VectorSpace<T>>, Buffer<T>,
typealias NDFieldFactory<T> = (IntArray) -> NDField<T> typealias NDFieldFactory<T> = (IntArray) -> NDField<T>
internal fun <T : Any> genericNDFieldFactory(field: Field<T>): NDFieldFactory<T> = { index -> GenericNDField(index, field) } internal fun <T : Any> genericNDFieldFactory(field: Field<T>): NDFieldFactory<T> = { index -> GenericNDField(index, field) }
internal val realNDFieldFactory: NDFieldFactory<Double> = { index -> GenericNDField(index, DoubleField) } internal val realNDFieldFactory: NDFieldFactory<Double> = { index -> ExtendedNDField(index, DoubleField) }
/** /**
* NDArray-based implementation of vector space. By default uses slow [SimpleNDField], but could be overridden with custom [NDField] factory. * NDArray-based implementation of vector space. By default uses slow [GenericNDField], but could be overridden with custom [NDField] factory.
*/ */
class ArrayMatrixSpace<T : Any>( class ArrayMatrixSpace<T : Any>(
rows: Int, rows: Int,
@ -318,3 +313,8 @@ fun <T : Any> Vector<T>.toMatrix(): Matrix<T> {
return Matrix.of(size, 1, context.field) { i, _ -> get(i) } return Matrix.of(size, 1, context.field) { i, _ -> get(i) }
} }
object VectorL2Norm: Norm<Vector<out Number>, Double> {
override fun norm(arg: Vector<out Number>): Double {
return kotlin.math.sqrt(arg.sumByDouble { it.toDouble() })
}
}

View File

@ -2,10 +2,20 @@ package scientifik.kmath.operations
import kotlin.math.pow import kotlin.math.pow
/**
* Advanced Number-like field that implements basic operations
*/
interface ExtendedField<N : Any> :
Field<N>,
TrigonometricOperations<N>,
PowerOperations<N>,
ExponentialOperations<N>
/** /**
* Field for real values * Field for real values
*/ */
object RealField : Field<Real>, TrigonometricOperations<Real>, PowerOperations<Real>, ExponentialOperations<Real> { object RealField : ExtendedField<Real>, Norm<Real, Real> {
override val zero: Real = Real(0.0) override val zero: Real = Real(0.0)
override fun add(a: Real, b: Real): Real = Real(a.value + b.value) override fun add(a: Real, b: Real): Real = Real(a.value + b.value)
override val one: Real = Real(1.0) override val one: Real = Real(1.0)
@ -13,33 +23,24 @@ object RealField : Field<Real>, TrigonometricOperations<Real>, PowerOperations<R
override fun multiply(a: Real, k: Double): Real = Real(a.value * k) override fun multiply(a: Real, k: Double): Real = Real(a.value * k)
override fun divide(a: Real, b: Real): Real = Real(a.value / b.value) override fun divide(a: Real, b: Real): Real = Real(a.value / b.value)
override fun sin(arg: Real): Real = Real(kotlin.math.sin(arg.value)) override fun sin(arg: Real): Real = Real(kotlin.math.sin(arg.value))
override fun cos(arg: Real): Real = Real(kotlin.math.cos(arg.value)) override fun cos(arg: Real): Real = Real(kotlin.math.cos(arg.value))
override fun power(arg: Real, pow: Double): Real = Real(arg.value.pow(pow)) override fun power(arg: Real, pow: Double): Real = Real(arg.value.pow(pow))
override fun exp(arg: Real): Real = Real(kotlin.math.exp(arg.value)) override fun exp(arg: Real): Real = Real(kotlin.math.exp(arg.value))
override fun ln(arg: Real): Real = Real(kotlin.math.ln(arg.value)) override fun ln(arg: Real): Real = Real(kotlin.math.ln(arg.value))
override fun norm(arg: Real): Real = Real(kotlin.math.abs(arg.value))
} }
/** /**
* Real field element wrapping double. * Real field element wrapping double.
* *
* TODO could be replaced by inline class in kotlin 1.3 if it would allow to avoid boxing * TODO inline does not work due to compiler bug. Waiting for fix
*/ */
data class Real(val value: Double) : Number(), FieldElement<Real, RealField> { class Real(val value: Double) : FieldElement<Real, RealField> {
/*
* The class uses composition instead of inheritance since Double is final
*/
override fun toByte(): Byte = value.toByte()
override fun toChar(): Char = value.toChar()
override fun toDouble(): Double = value
override fun toFloat(): Float = value.toFloat()
override fun toInt(): Int = value.toInt()
override fun toLong(): Long = value.toLong()
override fun toShort(): Short = value.toShort()
//values are dynamically calculated to save memory //values are dynamically calculated to save memory
override val self override val self
@ -53,19 +54,21 @@ data class Real(val value: Double) : Number(), FieldElement<Real, RealField> {
/** /**
* A field for double without boxing. Does not produce appropriate field element * A field for double without boxing. Does not produce appropriate field element
*/ */
object DoubleField : Field<Double>, TrigonometricOperations<Double>, PowerOperations<Double>, ExponentialOperations<Double> { object DoubleField : ExtendedField<Double>, Norm<Double, Double> {
override val zero: Double = 0.0 override val zero: Double = 0.0
override fun add(a: Double, b: Double): Double = a + b override fun add(a: Double, b: Double): Double = a + b
override fun multiply(a: Double, @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") b: Double): Double = a * b override fun multiply(a: Double, @Suppress("PARAMETER_NAME_CHANGED_ON_OVERRIDE") b: Double): Double = a * b
override val one: Double = 1.0 override val one: Double = 1.0
override fun divide(a: Double, b: Double): Double = a / b override fun divide(a: Double, b: Double): Double = a / b
override fun sin(arg: Double): Double = kotlin.math.sin(arg) override fun sin(arg: Double): Double = kotlin.math.sin(arg)
override fun cos(arg: Double): Double = kotlin.math.cos(arg) override fun cos(arg: Double): Double = kotlin.math.cos(arg)
override fun power(arg: Double, pow: Double): Double = arg.pow(pow) override fun power(arg: Double, pow: Double): Double = arg.pow(pow)
override fun exp(arg: Double): Double =kotlin.math.exp(arg) override fun exp(arg: Double): Double = kotlin.math.exp(arg)
override fun ln(arg: Double): Double = kotlin.math.ln(arg) override fun ln(arg: Double): Double = kotlin.math.ln(arg)
override fun norm(arg: Double): Double = kotlin.math.abs(arg)
} }

View File

@ -45,4 +45,10 @@ interface ExponentialOperations<T> {
} }
fun <T : MathElement<T, out ExponentialOperations<T>>> exp(arg: T): T = arg.context.exp(arg) fun <T : MathElement<T, out ExponentialOperations<T>>> exp(arg: T): T = arg.context.exp(arg)
fun <T : MathElement<T, out ExponentialOperations<T>>> ln(arg: T): T = arg.context.ln(arg) fun <T : MathElement<T, out ExponentialOperations<T>>> ln(arg: T): T = arg.context.ln(arg)
interface Norm<in T, out R> {
fun norm(arg: T): R
}
fun <T : MathElement<T, out Norm<T, R>>, R> norm(arg: T): R = arg.context.norm(arg)

View File

@ -42,6 +42,7 @@ inline class ListBuffer<T>(private val list: MutableList<T>) : MutableBuffer<T>
} }
class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> { class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> {
//Can't inline because array invariant
override val size: Int override val size: Int
get() = array.size get() = array.size
@ -56,7 +57,7 @@ class ArrayBuffer<T>(private val array: Array<T>) : MutableBuffer<T> {
override fun copy(): MutableBuffer<T> = ArrayBuffer(array.copyOf()) override fun copy(): MutableBuffer<T> = ArrayBuffer(array.copyOf())
} }
class DoubleBuffer(private val array: DoubleArray) : MutableBuffer<Double> { inline class DoubleBuffer(private val array: DoubleArray) : MutableBuffer<Double> {
override val size: Int override val size: Int
get() = array.size get() = array.size

View File

@ -0,0 +1,39 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.*
/**
* NDField that supports [ExtendedField] operations on its elements
*/
class ExtendedNDField<N: Any>(shape: IntArray, override val field: ExtendedField<N>) : NDField<N>(shape, field),
TrigonometricOperations<NDArray<N>>,
PowerOperations<NDArray<N>>,
ExponentialOperations<NDArray<N>> {
override fun produceStructure(initializer: (IntArray) -> N): NDStructure<N> {
return genericNdStructure(shape, initializer)
}
override fun power(arg: NDArray<N>, pow: Double): NDArray<N> {
return arg.transform { d -> with(field){power(d,pow)} }
}
override fun exp(arg: NDArray<N>): NDArray<N> {
return arg.transform { d -> with(field){exp(d)} }
}
override fun ln(arg: NDArray<N>): NDArray<N> {
return arg.transform { d -> with(field){ln(d)} }
}
override fun sin(arg: NDArray<N>): NDArray<N> {
return arg.transform { d -> with(field){sin(d)} }
}
override fun cos(arg: NDArray<N>): NDArray<N> {
return arg.transform { d -> with(field){cos(d)} }
}
}

View File

@ -1,5 +1,6 @@
package scientifik.kmath.structures package scientifik.kmath.structures
import scientifik.kmath.operations.DoubleField
import scientifik.kmath.operations.Field import scientifik.kmath.operations.Field
import scientifik.kmath.operations.FieldElement import scientifik.kmath.operations.FieldElement
@ -14,7 +15,7 @@ class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : Run
* @param field - operations field defined on individual array element * @param field - operations field defined on individual array element
* @param T the type of the element contained in NDArray * @param T the type of the element contained in NDArray
*/ */
abstract class NDField<T>(val shape: IntArray, val field: Field<T>) : Field<NDArray<T>> { abstract class NDField<T>(val shape: IntArray, open val field: Field<T>) : Field<NDArray<T>> {
abstract fun produceStructure(initializer: (IntArray) -> T): NDStructure<T> abstract fun produceStructure(initializer: (IntArray) -> T): NDStructure<T>
@ -173,7 +174,7 @@ object NDArrays {
* Create a platform-optimized NDArray of doubles * Create a platform-optimized NDArray of doubles
*/ */
fun realNDArray(shape: IntArray, initializer: (IntArray) -> Double = { 0.0 }): NDArray<Double> { fun realNDArray(shape: IntArray, initializer: (IntArray) -> Double = { 0.0 }): NDArray<Double> {
return RealNDField(shape).produce(initializer) return ExtendedNDField(shape, DoubleField).produce(initializer)
} }
fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDArray<Double> { fun real1DArray(dim: Int, initializer: (Int) -> Double = { _ -> 0.0 }): NDArray<Double> {
@ -188,7 +189,7 @@ object NDArrays {
return realNDArray(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) } return realNDArray(intArrayOf(dim1, dim2, dim3)) { initializer(it[0], it[1], it[2]) }
} }
inline fun produceReal(shape: IntArray, block: RealNDField.() -> RealNDArray) = RealNDField(shape).run(block) inline fun produceReal(shape: IntArray, block: ExtendedNDField<Double>.() -> NDArray<Double>) = ExtendedNDField(shape, DoubleField).run(block)
// /** // /**
// * Simple boxing NDField // * Simple boxing NDField

View File

@ -1,46 +0,0 @@
package scientifik.kmath.structures
import scientifik.kmath.operations.DoubleField
import scientifik.kmath.operations.ExponentialOperations
import scientifik.kmath.operations.PowerOperations
import scientifik.kmath.operations.TrigonometricOperations
import kotlin.math.*
typealias RealNDArray = NDArray<Double>
class RealNDField(shape: IntArray) : NDField<Double>(shape, DoubleField),
TrigonometricOperations<RealNDArray>,
PowerOperations<RealNDArray>,
ExponentialOperations<RealNDArray> {
override fun produceStructure(initializer: (IntArray) -> Double): NDStructure<Double> {
return genericNdStructure(shape, initializer)
}
override fun power(arg: RealNDArray, pow: Double): RealNDArray {
return arg.transform { d -> d.pow(pow) }
}
override fun exp(arg: RealNDArray): RealNDArray {
return arg.transform { d -> exp(d) }
}
override fun ln(arg: RealNDArray): RealNDArray {
return arg.transform { d -> ln(d) }
}
override fun sin(arg: RealNDArray): RealNDArray {
return arg.transform { d -> sin(d) }
}
override fun cos(arg: RealNDArray): RealNDArray {
return arg.transform { d -> cos(d) }
}
fun abs(arg: RealNDArray): RealNDArray {
return arg.transform { d -> abs(d) }
}
}

View File

@ -7,8 +7,8 @@ class RealFieldTest {
@Test @Test
fun testSqrt() { fun testSqrt() {
val sqrt = with(RealField) { val sqrt = with(RealField) {
sqrt(25 * one) sqrt( 25 * one)
} }
assertEquals(5.0, sqrt.toDouble()) assertEquals(5.0, sqrt.value)
} }
} }

View File

@ -1,5 +1,8 @@
package scientifik.kmath.structures package scientifik.kmath.structures
import scientifik.kmath.linear.Vector
import scientifik.kmath.linear.VectorL2Norm
import scientifik.kmath.operations.Norm
import scientifik.kmath.structures.NDArrays.produceReal import scientifik.kmath.structures.NDArrays.produceReal
import scientifik.kmath.structures.NDArrays.real2DArray import scientifik.kmath.structures.NDArrays.real2DArray
import kotlin.math.abs import kotlin.math.abs
@ -7,7 +10,7 @@ import kotlin.math.pow
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
class RealNDFieldTest { class NumberNDFieldTest {
val array1 = real2DArray(3, 3) { i, j -> (i + j).toDouble() } val array1 = real2DArray(3, 3) { i, j -> (i + j).toDouble() }
val array2 = real2DArray(3, 3) { i, j -> (i - j).toDouble() } val array2 = real2DArray(3, 3) { i, j -> (i - j).toDouble() }
@ -46,14 +49,22 @@ class RealNDFieldTest {
@Test @Test
fun testLibraryFunction() { fun testLibraryFunction() {
val abs: (Double) -> Double = ::abs val abs: (Double) -> Double = ::abs
val result = abs(array1) val result = abs(array2)
assertEquals(10.0, result[1,1]) assertEquals(2.0, result[0,2])
}
object L2Norm: Norm<NDArray<out Number>, Double> {
override fun norm(arg: NDArray<out Number>): Double {
return kotlin.math.sqrt(arg.sumByDouble { it.second.toDouble() })
}
} }
@Test @Test
fun testAbs(){ fun testInternalContext(){
val res = produceReal(array1.shape){ produceReal(array1.shape){
1 + abs(array1) + exp(array2) with(L2Norm) {
1 + norm(array1) + exp(array2)
}
} }
} }
} }

53
kmath-io/build.gradle Normal file
View File

@ -0,0 +1,53 @@
plugins {
id 'kotlin-multiplatform'
}
kotlin {
targets {
fromPreset(presets.jvm, 'jvm')
fromPreset(presets.js, 'js')
// For ARM, preset should be changed to presets.iosArm32 or presets.iosArm64
// For Linux, preset should be changed to e.g. presets.linuxX64
// For MacOS, preset should be changed to e.g. presets.macosX64
//fromPreset(presets.mingwX64, 'mingw')
}
sourceSets {
commonMain {
dependencies {
api project(":kmath-core")
implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
}
}
commonTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test-common'
implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
}
}
jvmMain {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
}
}
jvmTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test'
implementation 'org.jetbrains.kotlin:kotlin-test-junit'
}
}
jsMain {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-stdlib-js'
}
}
jsTest {
dependencies {
implementation 'org.jetbrains.kotlin:kotlin-test-js'
}
}
// mingwMain {
// }
// mingwTest {
// }
}
}

View File

@ -4,11 +4,6 @@ plugins {
id "me.champeau.gradle.jmh" version "0.4.7" id "me.champeau.gradle.jmh" version "0.4.7"
} }
repositories {
maven { url = 'http://dl.bintray.com/kotlin/kotlin-eap' }
mavenCentral()
}
dependencies { dependencies {
implementation project(':kmath-core') implementation project(':kmath-core')
jmh 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' jmh 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'

View File

@ -1,6 +1,7 @@
pluginManagement { pluginManagement {
repositories { repositories {
mavenCentral() mavenCentral()
jcenter()
maven { url = 'https://plugins.gradle.org/m2/' } maven { url = 'https://plugins.gradle.org/m2/' }
} }
} }
@ -9,5 +10,7 @@ enableFeaturePreview('GRADLE_METADATA')
rootProject.name = 'kmath' rootProject.name = 'kmath'
include ':kmath-core' include ':kmath-core'
include ':kmath-io'
include ':kmath-jmh' include ':kmath-jmh'