forked from kscience/kmath
Merge remote-tracking branch 'upstream/zelenyy' into zelenyy
# Conflicts: # kmath-core/src/commonMain/kotlin/scientifik/kmath/structures/CreationRoutines.kt
This commit is contained in:
commit
ba230d0de1
@ -14,7 +14,7 @@ class ShapeMismatchException(val expected: IntArray, val actual: IntArray) : Run
|
|||||||
/**
|
/**
|
||||||
* The base interface for all nd-algebra implementations
|
* The base interface for all nd-algebra implementations
|
||||||
* @param T the type of nd-structure element
|
* @param T the type of nd-structure element
|
||||||
* @param C the type of the context
|
* @param C the type of the element context
|
||||||
* @param N the type of the structure
|
* @param N the type of the structure
|
||||||
*/
|
*/
|
||||||
interface NDAlgebra<T, C, N : NDStructure<T>> {
|
interface NDAlgebra<T, C, N : NDStructure<T>> {
|
||||||
@ -112,10 +112,13 @@ interface NDField<T, F : Field<T>, N : NDStructure<T>> : Field<N>, NDRing<T, F,
|
|||||||
operator fun T.div(arg: N) = map(arg) { divide(it, this@div) }
|
operator fun T.div(arg: N) = map(arg) { divide(it, this@div) }
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
|
private val realNDFieldCache = HashMap<IntArray, RealNDField>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a nd-field for [Double] values
|
* Create a nd-field for [Double] values or pull it from cache if it was created previously
|
||||||
*/
|
*/
|
||||||
fun real(shape: IntArray) = RealNDField(shape)
|
fun real(shape: IntArray) = realNDFieldCache.getOrPut(shape){RealNDField(shape)}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a nd-field with boxing generic buffer
|
* Create a nd-field with boxing generic buffer
|
||||||
|
@ -7,6 +7,9 @@ import scientifik.kmath.operations.Space
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The root for all [NDStructure] based algebra elements. Does not implement algebra element root because of problems with recursive self-types
|
* The root for all [NDStructure] based algebra elements. Does not implement algebra element root because of problems with recursive self-types
|
||||||
|
* @param T the type of the element of the structure
|
||||||
|
* @param C the type of the context for the element
|
||||||
|
* @param N the type of the underlying [NDStructure]
|
||||||
*/
|
*/
|
||||||
interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
||||||
|
|
||||||
@ -16,9 +19,6 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
|||||||
|
|
||||||
fun N.wrap(): NDElement<T, C, N>
|
fun N.wrap(): NDElement<T, C, N>
|
||||||
|
|
||||||
fun mapIndexed(transform: C.(index: IntArray, T) -> T) = context.mapIndexed(unwrap(), transform).wrap()
|
|
||||||
fun map(transform: C.(T) -> T) = context.map(unwrap(), transform).wrap()
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
* Create a optimized NDArray of doubles
|
* Create a optimized NDArray of doubles
|
||||||
@ -61,10 +61,17 @@ interface NDElement<T, C, N : NDStructure<T>> : NDStructure<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun <T, C, N : NDStructure<T>> NDElement<T, C, N>.mapIndexed(transform: C.(index: IntArray, T) -> T) =
|
||||||
|
context.mapIndexed(unwrap(), transform).wrap()
|
||||||
|
|
||||||
|
fun <T, C, N : NDStructure<T>> NDElement<T, C, N>.map(transform: C.(T) -> T) = context.map(unwrap(), transform).wrap()
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Element by element application of any operation on elements to the whole [NDElement]
|
* Element by element application of any operation on elements to the whole [NDElement]
|
||||||
*/
|
*/
|
||||||
operator fun <T, C> Function1<T, T>.invoke(ndElement: NDElement<T, C, *>) =
|
operator fun <T, C, N : NDStructure<T>> Function1<T, T>.invoke(ndElement: NDElement<T, C, N>) =
|
||||||
ndElement.map { value -> this@invoke(value) }
|
ndElement.map { value -> this@invoke(value) }
|
||||||
|
|
||||||
/* plus and minus */
|
/* plus and minus */
|
||||||
@ -72,13 +79,13 @@ operator fun <T, C> Function1<T, T>.invoke(ndElement: NDElement<T, C, *>) =
|
|||||||
/**
|
/**
|
||||||
* Summation operation for [NDElement] and single element
|
* Summation operation for [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, S : Space<T>> NDElement<T, S, *>.plus(arg: T) =
|
operator fun <T, S : Space<T>, N : NDStructure<T>> NDElement<T, S, N>.plus(arg: T) =
|
||||||
map { value -> arg + value }
|
map { value -> arg + value }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtraction operation between [NDElement] and single element
|
* Subtraction operation between [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, S : Space<T>> NDElement<T, S, *>.minus(arg: T) =
|
operator fun <T, S : Space<T>, N : NDStructure<T>> NDElement<T, S, N>.minus(arg: T) =
|
||||||
map { value -> arg - value }
|
map { value -> arg - value }
|
||||||
|
|
||||||
/* prod and div */
|
/* prod and div */
|
||||||
@ -86,13 +93,13 @@ operator fun <T, S : Space<T>> NDElement<T, S, *>.minus(arg: T) =
|
|||||||
/**
|
/**
|
||||||
* Product operation for [NDElement] and single element
|
* Product operation for [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, R : Ring<T>> NDElement<T, R, *>.times(arg: T) =
|
operator fun <T, R : Ring<T>, N : NDStructure<T>> NDElement<T, R, N>.times(arg: T) =
|
||||||
map { value -> arg * value }
|
map { value -> arg * value }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Division operation between [NDElement] and single element
|
* Division operation between [NDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun <T, F : Field<T>> NDElement<T, F, *>.div(arg: T) =
|
operator fun <T, F : Field<T>, N : NDStructure<T>> NDElement<T, F, N>.div(arg: T) =
|
||||||
map { value -> arg / value }
|
map { value -> arg / value }
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,14 +1,19 @@
|
|||||||
package scientifik.kmath.structures
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
import scientifik.kmath.operations.RealField.power
|
import scientifik.kmath.operations.RealField.power
|
||||||
import kotlin.math.*
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.log
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.math.sign
|
||||||
|
|
||||||
|
/**
|
||||||
object RealFactory {
|
* Numpy-like factories for [RealNDElement]
|
||||||
|
*/
|
||||||
|
object RealNDFactory {
|
||||||
/**
|
/**
|
||||||
* Create a NDArray filled with ones
|
* Get a [RealNDElement] filled with [RealNDField.one]. Due to caching all instances with the same shape point to the same object
|
||||||
*/
|
*/
|
||||||
fun ones(vararg shape: Int) = NDElement.real(shape) { 1.0 }
|
fun ones(vararg shape: Int) = NDField.real(shape).one
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a 2D NDArray, with ones on the diagonal and zeros elsewhere.
|
* Create a 2D NDArray, with ones on the diagonal and zeros elsewhere.
|
||||||
@ -31,40 +36,37 @@ object RealFactory {
|
|||||||
* Return evenly spaced values within a given interval.
|
* Return evenly spaced values within a given interval.
|
||||||
*
|
*
|
||||||
* Values are generated within the half-open interval [start, stop) (in other words, the interval including start but excluding stop).
|
* Values are generated within the half-open interval [start, stop) (in other words, the interval including start but excluding stop).
|
||||||
* @param range use it like:
|
|
||||||
* (start..stop) to step
|
|
||||||
*/
|
*/
|
||||||
fun range(range: Pair<ClosedFloatingPointRange<Double>, Double>) =
|
fun range(range: ClosedFloatingPointRange<Double>, step: Double = 1.0) =
|
||||||
NDElement.real1D(ceil((range.first.endInclusive - range.first.start) / range.second).toInt()) { i -> range.first.start + i * range.second }
|
NDElement.real1D(ceil((range.endInclusive - range.start) / step).toInt()) { i ->
|
||||||
|
range.start + i * step
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return evenly spaced numbers over a specified interval.
|
* Return evenly spaced numbers over a specified interval.
|
||||||
* @param range use it like:
|
* @param range start is starting value, final value depend from endPoint parameter
|
||||||
* (start..stop) to number
|
|
||||||
* start is starting value, finaly value depend from endPoint parameter
|
|
||||||
* @param endPoint If True, right boundary of range is the last sample. Otherwise, it is not included.
|
* @param endPoint If True, right boundary of range is the last sample. Otherwise, it is not included.
|
||||||
*/
|
*/
|
||||||
fun linSpace(
|
fun linspace(
|
||||||
range: Pair<ClosedFloatingPointRange<Double>, Int>,
|
range: ClosedFloatingPointRange<Double>,
|
||||||
|
num: Int = 100,
|
||||||
endPoint: Boolean = true
|
endPoint: Boolean = true
|
||||||
): Pair<RealNDElement, Double> {
|
): RealNDElement {
|
||||||
val div = if (endPoint) (range.second - 1) else range.second
|
val div = if (endPoint) (num - 1) else num
|
||||||
val delta = range.first.start - range.first.endInclusive
|
val delta = range.start - range.endInclusive
|
||||||
if (range.second > 1) {
|
return if (num > 1) {
|
||||||
val step = delta / div
|
val step = delta / div
|
||||||
if (step == 0.0) {
|
if (step == 0.0) {
|
||||||
error("Bad ranges: step = $step")
|
error("Bad ranges: step = $step")
|
||||||
}
|
}
|
||||||
val result = NDElement.real1D(range.second) {
|
NDElement.real1D(num) {
|
||||||
if (endPoint and (it == range.second - 1)) {
|
if (endPoint and (it == num - 1)) {
|
||||||
range.first.endInclusive
|
range.endInclusive
|
||||||
}
|
}
|
||||||
range.first.start + it * step
|
range.start + it * step
|
||||||
}
|
}
|
||||||
return result to step
|
|
||||||
} else {
|
} else {
|
||||||
val step = Double.NaN
|
NDElement.real1D(1) { range.start }
|
||||||
return NDElement.real1D(1) { range.first.start } to step
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -77,29 +79,25 @@ object RealFactory {
|
|||||||
* @param endPoint If True, power(base,stop) is the last sample. Otherwise, it is not included.
|
* @param endPoint If True, power(base,stop) is the last sample. Otherwise, it is not included.
|
||||||
* @param base - The base of the log space.
|
* @param base - The base of the log space.
|
||||||
*/
|
*/
|
||||||
fun logSpace(
|
fun logspace(
|
||||||
range: Pair<ClosedFloatingPointRange<Double>, Int>,
|
range: ClosedFloatingPointRange<Double>,
|
||||||
|
num: Int = 100,
|
||||||
endPoint: Boolean = true,
|
endPoint: Boolean = true,
|
||||||
base: Double = 10.0
|
base: Double = 10.0
|
||||||
): RealNDElement {
|
) = linspace(range, num, endPoint).map { power(base, it) }
|
||||||
val lin = linSpace(range, endPoint).first
|
|
||||||
val tempFun = { x: Double -> power(base, x) }
|
|
||||||
return tempFun(lin) // FIXME: RealNDElement.map return not suitable type ( `linSpace(range, endPoint).first.map{power(base, it}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return numbers spaced evenly on a log scale (a geometric progression).
|
* Return numbers spaced evenly on a log scale (a geometric progression).
|
||||||
*
|
*
|
||||||
* This is similar to [logSpace], but with endpoints specified directly. Each output sample is a constant multiple of the previous.
|
* This is similar to [logspace], but with endpoints specified directly. Each output sample is a constant multiple of the previous.
|
||||||
* @param range use it like:
|
* @param range use it like:
|
||||||
* (start..stop) to number
|
* (start..stop) to number
|
||||||
* start is starting value, finaly value depend from endPoint parameter
|
* start is starting value, finaly value depend from endPoint parameter
|
||||||
* @param endPoint If True, right boundary of range is the last sample. Otherwise, it is not included.
|
* @param endPoint If True, right boundary of range is the last sample. Otherwise, it is not included.
|
||||||
*/
|
*/
|
||||||
fun geomSpace(range: Pair<ClosedFloatingPointRange<Double>, Int>, endPoint: Boolean = true): RealNDElement {
|
fun geomspace(range: ClosedFloatingPointRange<Double>, num : Int = 100, endPoint: Boolean = true): RealNDElement {
|
||||||
var start = range.first.start
|
var start = range.start
|
||||||
var stop = range.first.endInclusive
|
var stop = range.endInclusive
|
||||||
val num = range.second
|
|
||||||
if (start == 0.0 || stop == 0.0) {
|
if (start == 0.0 || stop == 0.0) {
|
||||||
error("Geometric sequence cannot include zero")
|
error("Geometric sequence cannot include zero")
|
||||||
}
|
}
|
||||||
@ -110,10 +108,9 @@ object RealFactory {
|
|||||||
outSign = -outSign
|
outSign = -outSign
|
||||||
}
|
}
|
||||||
|
|
||||||
val logRange = logSpace((log(start, 10.0)..log(stop, 10.0) to num), endPoint = endPoint)
|
return logspace(log(start, 10.0)..log(stop, 10.0), num, endPoint = endPoint).map {
|
||||||
val function = { x: Double -> outSign * x }
|
outSign * it
|
||||||
return function(logRange) // FIXME: `outSign*log_` --- don't define times operator
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -126,12 +123,11 @@ object RealFactory {
|
|||||||
error("Input must be 2D NDArray")
|
error("Input must be 2D NDArray")
|
||||||
}
|
}
|
||||||
val size = min(array.shape[0], array.shape[0])
|
val size = min(array.shape[0], array.shape[0])
|
||||||
if (offset >= 0) {
|
return if (offset >= 0) {
|
||||||
return NDElement.real1D(size) { i -> array[i, i + offset] }
|
NDElement.real1D(size) { i -> array[i, i + offset] }
|
||||||
} else {
|
} else {
|
||||||
return NDElement.real1D(size) { i -> array[i - offset, i] }
|
NDElement.real1D(size) { i -> array[i - offset, i] }
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -144,13 +140,13 @@ object RealFactory {
|
|||||||
error("Input must be 1D NDArray")
|
error("Input must be 1D NDArray")
|
||||||
}
|
}
|
||||||
val size = array.shape[0]
|
val size = array.shape[0]
|
||||||
if (offset >= 0) {
|
return if (offset < 0) {
|
||||||
return NDElement.real2D(size, size + offset) { i, j ->
|
NDElement.real2D(size - offset, size) { i, j ->
|
||||||
if (i == j + offset) array[i] else 0.0
|
if (i - offset == j) array[j] else 0.0
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return NDElement.real2D(size - offset, size) { i, j ->
|
NDElement.real2D(size, size + offset) { i, j ->
|
||||||
if (i - offset == j) array[j] else 0.0
|
if (i == j + offset) array[i] else 0.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -166,12 +162,12 @@ object RealFactory {
|
|||||||
error("Input must be 1D NDArray")
|
error("Input must be 1D NDArray")
|
||||||
}
|
}
|
||||||
val size = if (nCols == 0) array.shape[0] else nCols
|
val size = if (nCols == 0) array.shape[0] else nCols
|
||||||
if (increasing) {
|
return if (increasing) {
|
||||||
return NDElement.real2D(array.shape[0], size) { i, j ->
|
NDElement.real2D(array.shape[0], size) { i, j ->
|
||||||
power(array[i], j)
|
power(array[i], j)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return NDElement.real2D(array.shape[0], size) { i, j ->
|
NDElement.real2D(array.shape[0], size) { i, j ->
|
||||||
power(array[i], size - j - 1)
|
power(array[i], size - j - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -86,11 +86,25 @@ inline fun BufferedNDField<Double, RealField>.produceInline(crossinline initiali
|
|||||||
return BufferedNDFieldElement(this, DoubleBuffer(array))
|
return BufferedNDFieldElement(this, DoubleBuffer(array))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map one [RealNDElement] using function with indexes
|
||||||
|
*/
|
||||||
|
inline fun RealNDElement.mapIndexed(crossinline transform: RealField.(index: IntArray, Double) -> Double) =
|
||||||
|
context.produceInline { offset -> transform(strides.index(offset), buffer[offset]) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map one [RealNDElement] using function without indexes
|
||||||
|
*/
|
||||||
|
inline fun RealNDElement.map(crossinline transform: RealField.(Double) -> Double): RealNDElement {
|
||||||
|
val array = DoubleArray(strides.linearSize) { offset -> RealField.transform(buffer[offset]) }
|
||||||
|
return BufferedNDFieldElement(context, DoubleBuffer(array))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
* Element by element application of any operation on elements to the whole array. Just like in numpy
|
||||||
*/
|
*/
|
||||||
operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement) =
|
operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement) =
|
||||||
ndElement.context.produceInline { i -> invoke(ndElement.buffer[i]) }
|
ndElement.map { this@invoke(it) }
|
||||||
|
|
||||||
|
|
||||||
/* plus and minus */
|
/* plus and minus */
|
||||||
@ -99,10 +113,10 @@ operator fun Function1<Double, Double>.invoke(ndElement: RealNDElement) =
|
|||||||
* Summation operation for [BufferedNDElement] and single element
|
* Summation operation for [BufferedNDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun RealNDElement.plus(arg: Double) =
|
operator fun RealNDElement.plus(arg: Double) =
|
||||||
context.produceInline { i -> buffer[i] + arg }
|
map { it + arg }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subtraction operation between [BufferedNDElement] and single element
|
* Subtraction operation between [BufferedNDElement] and single element
|
||||||
*/
|
*/
|
||||||
operator fun RealNDElement.minus(arg: Double) =
|
operator fun RealNDElement.minus(arg: Double) =
|
||||||
context.produceInline { i -> buffer[i] - arg }
|
map { it - arg }
|
||||||
|
@ -2,8 +2,7 @@ pluginManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven("https://plugins.gradle.org/m2/")
|
maven("https://plugins.gradle.org/m2/")
|
||||||
maven { setUrl("https://dl.bintray.com/kotlin/kotlin-eap") }
|
maven ("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||||
maven { setUrl("https://plugins.gradle.org/m2/") }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user