feat(Core): add a permutation sorting prototype for buffers
This is a Buffer extension function to create a list of permuted indices that represent the sequence of naturally sorted buffer elements
This commit is contained in:
parent
a6922ab9d8
commit
0f7a25762e
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2021 KMath contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package space.kscience.kmath.misc
|
||||||
|
|
||||||
|
import kotlin.comparisons.*
|
||||||
|
import space.kscience.kmath.misc.UnstableKMathAPI
|
||||||
|
import space.kscience.kmath.structures.Buffer
|
||||||
|
import space.kscience.kmath.structures.indices
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a new list filled with buffer indices. Indice order is defined by sorting associated buffer value.
|
||||||
|
* This feature allows to sort buffer values without reordering its content.
|
||||||
|
*
|
||||||
|
* @param descending True to revert sort order from highest to lowest values. Default to ascending order.
|
||||||
|
* @return List of buffer indices, sorted by associated value.
|
||||||
|
*/
|
||||||
|
@PerformancePitfall
|
||||||
|
@UnstableKMathAPI
|
||||||
|
public fun <V: Comparable<V>> Buffer<V>.permSort(descending : Boolean = false) : IntArray {
|
||||||
|
if (size < 2) return IntArray(size)
|
||||||
|
|
||||||
|
val comparator = if (descending) compareByDescending<Int> { get(it) } else compareBy<Int> { get(it) }
|
||||||
|
|
||||||
|
/* TODO: optimisation : keep a constant big array of indices (Ex: from 0 to 4096), then create indice
|
||||||
|
* arrays more efficiently by copying subpart of cached one. For bigger needs, we could copy entire
|
||||||
|
* cached array, then fill remaining indices manually. Not done for now, because:
|
||||||
|
* 1. doing it right would require some statistics about common used buffer sizes.
|
||||||
|
* 2. Some benchmark would be needed to ensure it would really provide better performance
|
||||||
|
*/
|
||||||
|
val packedIndices = IntArray(size) { idx -> idx }
|
||||||
|
|
||||||
|
/* TODO: find an efficient way to sort in-place instead, and return directly the IntArray.
|
||||||
|
* Not done for now, because no standard utility is provided yet. An open issue exists for this.
|
||||||
|
* See: https://youtrack.jetbrains.com/issue/KT-37860
|
||||||
|
*/
|
||||||
|
return packedIndices.sortedWith(comparator).toIntArray()
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2018-2021 KMath contributors.
|
||||||
|
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package space.kscience.kmath.misc
|
||||||
|
|
||||||
|
import kotlin.collections.mutableListOf
|
||||||
|
import kotlin.random.Random
|
||||||
|
import kotlin.test.Test
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
import space.kscience.kmath.structures.IntBuffer
|
||||||
|
|
||||||
|
class PermSortTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permutation on empty buffer should immediately return an empty array.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testOnEmptyBuffer() {
|
||||||
|
val emptyBuffer = IntBuffer(0) {it}
|
||||||
|
var permutations = emptyBuffer.permSort()
|
||||||
|
assertTrue(permutations.isEmpty(), "permutation on an empty buffer should return an empty result")
|
||||||
|
permutations = emptyBuffer.permSort(true)
|
||||||
|
assertTrue(permutations.isEmpty(), "permutation on an empty buffer should return an empty result")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testOnSingleValueBuffer() {
|
||||||
|
testPermutation(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public fun testOnSomeValues() {
|
||||||
|
testPermutation(10)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun testPermutation(bufferSize: Int) {
|
||||||
|
|
||||||
|
val seed = Random.nextLong()
|
||||||
|
println("Test randomization seed: $seed")
|
||||||
|
|
||||||
|
val buffer = Random(seed).buffer(bufferSize)
|
||||||
|
val indices = buffer.permSort()
|
||||||
|
|
||||||
|
assertEquals(bufferSize, indices.size)
|
||||||
|
// Ensure no doublon is present in indices
|
||||||
|
assertEquals(indices.toSet().size, indices.size)
|
||||||
|
|
||||||
|
for (i in 0 until (bufferSize-1)) {
|
||||||
|
val current = buffer[indices[i]]
|
||||||
|
val next = buffer[indices[i+1]]
|
||||||
|
assertTrue(current <= next, "Permutation indices not properly sorted")
|
||||||
|
}
|
||||||
|
|
||||||
|
val descIndices = buffer.permSort(true)
|
||||||
|
assertEquals(bufferSize, descIndices.size)
|
||||||
|
// Ensure no doublon is present in indices
|
||||||
|
assertEquals(descIndices.toSet().size, descIndices.size)
|
||||||
|
|
||||||
|
for (i in 0 until (bufferSize-1)) {
|
||||||
|
val current = buffer[descIndices[i]]
|
||||||
|
val next = buffer[descIndices[i+1]]
|
||||||
|
assertTrue(current >= next, "Permutation indices not properly sorted in descending order")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Random.buffer(size : Int) = IntBuffer(size) { nextInt() }
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user