Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ad62d69e17 | ||
|
18454c56fc | ||
|
a8fd6fdb92 | ||
|
ba230d0de1 | ||
1e425cb814 | |||
124159d43e | |||
3ea7e39ecd | |||
|
1b411872ef | ||
|
f0e380304e | ||
|
36609827dd |
@ -0,0 +1,25 @@
|
|||||||
|
package scientifik.kmath.signal
|
||||||
|
|
||||||
|
import scientifik.kmath.structures.NDStructure
|
||||||
|
|
||||||
|
|
||||||
|
interface Filter<T :Any>{
|
||||||
|
fun process(input : T) : T
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Convolve<T : Any>{
|
||||||
|
fun convolve(input1 : T, input2: T) : T
|
||||||
|
}
|
||||||
|
|
||||||
|
object Convolve1D<Vec>
|
||||||
|
|
||||||
|
fun NDStructure<out Number>.convolve(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class SignalProcessing<T : Any>(
|
||||||
|
val filter: Filter<T>,
|
||||||
|
val convolver : Convolve<T>
|
||||||
|
){
|
||||||
|
fun process()
|
||||||
|
}
|
@ -0,0 +1,71 @@
|
|||||||
|
package scientifik.kmath.stat
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.Field
|
||||||
|
import scientifik.kmath.operations.RealField
|
||||||
|
import scientifik.kmath.operations.Ring
|
||||||
|
import scientifik.kmath.operations.Space
|
||||||
|
import scientifik.kmath.structures.NDStructure
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO tailrec
|
||||||
|
fun <T> Ring<T>.pow(element : T, n : Int) : T {
|
||||||
|
if (n == 0 ){
|
||||||
|
return one
|
||||||
|
}
|
||||||
|
if (n==1){
|
||||||
|
return element
|
||||||
|
}
|
||||||
|
val temp = pow(element, n / 2)
|
||||||
|
return if (n%2==0) temp*temp else element*temp*temp
|
||||||
|
}
|
||||||
|
|
||||||
|
//fun <T,R> NDStructure<T>.map(transform : (T) -> R) : NDStructure<R>{
|
||||||
|
//
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for sequence-like operations
|
||||||
|
*/
|
||||||
|
class CollectionsOperations<T>(val context: Space<T>){
|
||||||
|
fun sum(structure: NDStructure<T>): T {
|
||||||
|
return with(context){
|
||||||
|
var sum = zero
|
||||||
|
for (element in structure.elements()) {
|
||||||
|
sum += element.second
|
||||||
|
}
|
||||||
|
sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Context for statistical operations
|
||||||
|
*/
|
||||||
|
open class Statistical<T>(val context : Field<T>){
|
||||||
|
fun mean(data : NDStructure<T>) = moment(data, 1)
|
||||||
|
|
||||||
|
fun variance(data: NDStructure<T>) = centralMomentum(data, 2)
|
||||||
|
|
||||||
|
fun moment(data: NDStructure<T>, k : Int) : T{
|
||||||
|
return with(context){
|
||||||
|
var result = zero
|
||||||
|
val number = data.shape.reduce { acc, i -> acc*i }
|
||||||
|
for (element in data.elements()){
|
||||||
|
result += pow(element.second, k)
|
||||||
|
}
|
||||||
|
result/number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun centralMomentum(data: NDStructure<T>, k: Int) = with(context){moment(data, k) - pow(mean(data), k)}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class RealStatistical : Statistical<Double>(RealField){
|
||||||
|
fun std(data : NDStructure<Double>) = with(context){variance(data).pow(0.5)}
|
||||||
|
}
|
@ -0,0 +1,179 @@
|
|||||||
|
package scientifik.kmath.structures
|
||||||
|
|
||||||
|
import scientifik.kmath.operations.RealField.power
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.log
|
||||||
|
import kotlin.math.min
|
||||||
|
import kotlin.math.sign
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Numpy-like factories for [RealNDElement]
|
||||||
|
*/
|
||||||
|
object RealNDFactory {
|
||||||
|
/**
|
||||||
|
* 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) = NDField.real(shape).one
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a 2D NDArray, with ones on the diagonal and zeros elsewhere.
|
||||||
|
*
|
||||||
|
* @param offset Index of the diagonal: 0 (the default) refers to the main diagonal, a positive value refers to an upper diagonal, and a negative value to a lower diagonal.
|
||||||
|
*/
|
||||||
|
fun eye(dim1: Int, dim2: Int, offset: Int = 0) =
|
||||||
|
NDElement.real2D(dim1, dim2) { i, j -> if (i == j + offset) 1.0 else 0.0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array with ones at and below the given diagonal and zeros elsewhere.
|
||||||
|
* T[i,j] == 1 for i <= j + offset
|
||||||
|
*
|
||||||
|
* @param offset Index of the diagonal: 0 (the default) refers to the main diagonal, a positive value refers to an upper diagonal, and a negative value to a lower diagonal.
|
||||||
|
*/
|
||||||
|
fun triangle(dim1: Int, dim2: Int, offset: Int = 0) =
|
||||||
|
NDElement.real2D(dim1, dim2) { i, j -> if (i <= j + offset) 1.0 else 0.0 }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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).
|
||||||
|
*/
|
||||||
|
fun range(range: ClosedFloatingPointRange<Double>, step: Double = 1.0) =
|
||||||
|
NDElement.real1D(ceil((range.endInclusive - range.start) / step).toInt()) { i ->
|
||||||
|
range.start + i * step
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return evenly spaced numbers over a specified interval.
|
||||||
|
* @param range start is starting value, final value depend from endPoint parameter
|
||||||
|
* @param endPoint If True, right boundary of range is the last sample. Otherwise, it is not included.
|
||||||
|
*/
|
||||||
|
fun linspace(
|
||||||
|
range: ClosedFloatingPointRange<Double>,
|
||||||
|
num: Int = 100,
|
||||||
|
endPoint: Boolean = true
|
||||||
|
): RealNDElement {
|
||||||
|
val div = if (endPoint) (num - 1) else num
|
||||||
|
val delta = range.start - range.endInclusive
|
||||||
|
return if (num > 1) {
|
||||||
|
val step = delta / div
|
||||||
|
if (step == 0.0) {
|
||||||
|
error("Bad ranges: step = $step")
|
||||||
|
}
|
||||||
|
NDElement.real1D(num) {
|
||||||
|
if (endPoint and (it == num - 1)) {
|
||||||
|
range.endInclusive
|
||||||
|
}
|
||||||
|
range.start + it * step
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NDElement.real1D(1) { range.start }
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return numbers spaced evenly on a log scale.
|
||||||
|
* @param range use it like:
|
||||||
|
* (start..stop) to number
|
||||||
|
* power(base,start) is starting value, endvalue depend from endPoint parameter
|
||||||
|
* @param endPoint If True, power(base,stop) is the last sample. Otherwise, it is not included.
|
||||||
|
* @param base - The base of the log space.
|
||||||
|
*/
|
||||||
|
fun logspace(
|
||||||
|
range: ClosedFloatingPointRange<Double>,
|
||||||
|
num: Int = 100,
|
||||||
|
endPoint: Boolean = true,
|
||||||
|
base: Double = 10.0
|
||||||
|
) = linspace(range, num, endPoint).map { power(base, it) }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
* @param range use it like:
|
||||||
|
* (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.
|
||||||
|
*/
|
||||||
|
fun geomspace(range: ClosedFloatingPointRange<Double>, num : Int = 100, endPoint: Boolean = true): RealNDElement {
|
||||||
|
var start = range.start
|
||||||
|
var stop = range.endInclusive
|
||||||
|
if (start == 0.0 || stop == 0.0) {
|
||||||
|
error("Geometric sequence cannot include zero")
|
||||||
|
}
|
||||||
|
var outSign = 1.0
|
||||||
|
if (sign(start) == -1.0 && sign(stop) == -1.0) {
|
||||||
|
start = -start
|
||||||
|
stop = -stop
|
||||||
|
outSign = -outSign
|
||||||
|
}
|
||||||
|
|
||||||
|
return logspace(log(start, 10.0)..log(stop, 10.0), num, endPoint = endPoint).map {
|
||||||
|
outSign * it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return specified diagonals of 2D NDArray.
|
||||||
|
*
|
||||||
|
* @param offset Index of the diagonal: 0 (the default) refers to the main diagonal, a positive value refers to an upper diagonal, and a negative value to a lower diagonal.
|
||||||
|
*/
|
||||||
|
fun extractDiagonal(array: RealNDElement, offset: Int = 0): RealNDElement {
|
||||||
|
if (array.dimension != 2) {
|
||||||
|
error("Input must be 2D NDArray")
|
||||||
|
}
|
||||||
|
val size = min(array.shape[0], array.shape[0])
|
||||||
|
return if (offset >= 0) {
|
||||||
|
NDElement.real1D(size) { i -> array[i, i + offset] }
|
||||||
|
} else {
|
||||||
|
NDElement.real1D(size) { i -> array[i - offset, i] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a 2-D array with [array] on the [offset] diagonal.
|
||||||
|
*
|
||||||
|
* @param offset Index of the diagonal: 0 (the default) refers to the main diagonal, a positive value refers to an upper diagonal, and a negative value to a lower diagonal.
|
||||||
|
*/
|
||||||
|
fun fromDiagonal(array: RealNDElement, offset: Int = 0): RealNDElement {
|
||||||
|
if (array.dimension != 1) {
|
||||||
|
error("Input must be 1D NDArray")
|
||||||
|
}
|
||||||
|
val size = array.shape[0]
|
||||||
|
return if (offset < 0) {
|
||||||
|
NDElement.real2D(size - offset, size) { i, j ->
|
||||||
|
if (i - offset == j) array[j] else 0.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NDElement.real2D(size, size + offset) { i, j ->
|
||||||
|
if (i == j + offset) array[i] else 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a [Vandermonde matrix](https://en.wikipedia.org/wiki/Vandermonde_matrix).
|
||||||
|
*
|
||||||
|
* @param nCols --- number of columns, as default using length of [array]
|
||||||
|
* @param increasing --- Order of the powers of the columns. If True, the powers increase from left to right, if False (the default) they are reversed. FIXME: Default order like numpy
|
||||||
|
*/
|
||||||
|
fun vandermonde(array: RealNDElement, nCols: Int = 0, increasing: Boolean = false): RealNDElement {
|
||||||
|
if (array.dimension != 1) {
|
||||||
|
error("Input must be 1D NDArray")
|
||||||
|
}
|
||||||
|
val size = if (nCols == 0) array.shape[0] else nCols
|
||||||
|
return if (increasing) {
|
||||||
|
NDElement.real2D(array.shape[0], size) { i, j ->
|
||||||
|
power(array[i], j)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NDElement.real2D(array.shape[0], size) { i, j ->
|
||||||
|
power(array[i], size - j - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user