kyonifer / koma

A scientific computing library for Kotlin. https://kyonifer.github.io/koma
Other
270 stars 23 forks source link

Implement toMatrix(), toVector(), matrixOf(), vectorOf() #66

Closed thomasnield closed 5 years ago

thomasnield commented 5 years ago

I implemented some syntactic sugar to turn an Iterable<T> or Sequence<T> into a Matrix<N>. This can be used via toMatrix() and toVector().

import javafx.scene.paint.Color
import koma.matrix.Matrix

fun main(args: Array<String>) {

    val colors = sequenceOf(
            Color.RED,
            Color.PINK,
            Color.YELLOW,
            Color.BLUE,
            Color.GREEN
    )

    val matrix = colors.toMatrix(
            { it.red },
            { it.green },
            { it.blue }
    )

    println(matrix)
}

I also implemented a matrixOf() and vectorOf() set of functions to follow Kotlin idioms.

val a = matrixOf(0, 1, 2 end 3, 4, 5)
thomasnield commented 5 years ago

If you feel strongly opposed to toVector() and vectorOf(), let me know and I'll remove them. I think it's a nice convenience to enforce a single column of values and not have to transpose a matrix literal.

kyonifer commented 5 years ago

Looks good overall. What do you think of naming the vector functions colVectorOf and toColVector to make it explicit that the user will be getting a vertical vector? Might make it more consistent with asColVector, and open the door to having rowVectorOf and toRowVector variants as well.

thomasnield commented 5 years ago

I think that makes perfect sense. Ill tweak those function names to indicate col vectors.

thomasnield commented 5 years ago

I put in the proposed changes, thanks. Please let me know if there is anything else.

drmoose commented 5 years ago

Once I figured out what it was doing, @thomasnield's toMatrix is an awesome idea, but I did have to read the source to figure out what it was doing. You might consider renaming it as well to something more descriptive -- just reading the example, I couldn't figure out what the callbacks were doing. The best I've been able to think of is mapAsRows/mapAsCols, but I'm not sure that's good either.

Sorry for barging in on somebody else's PR. By whatever name, :+1:

thomasnield commented 5 years ago

@drmoose that is an interesting idea. I see your intention with mapAsRows() and mapAsCols() but I don't think it communicates effectively that the elements are going into a Matrix. I suppose we could add an optional argument to toMatrix() specifying each element supplying to a column of values rather than a row. You can also transpose it after population....

thomasnield commented 5 years ago

Maybe mapToMatrix()? If that example was confusing, maybe it is esoteric and only makes sense to me....

kyonifer commented 5 years ago

I think having an extension named toMatrix() on Iterables et al makes sense, but usually the signature of these types of extensions take no arguments (i.e. foo.toList() in kotlin). The ability to specify selectors is powerful, so probably what we want here is to also add a default case where calling Iterable<T>.toMatrix() with no selectors specified by the user tries to directly insert each element into a matrix with a default selector, e.g.

listOf(1,2,3).toMatrix() // returns [1,2,3].T
listOf(1,2,3).toMatrix( { it }, {it + 100} ) // returns [1, 101 end 2, 102 end 3, 103]

That way we get the power of selectors but also the intuitive default behavior of "try and make this sequence of numerical things into a vector for me". Probably it makes sense to define the toMatrix() form without arguments only on numerical types, so we'll want to generate those extensions for each primitive. Going to merge this as-is for now as I think the only changes needed are augmentation of some default cases.

thomasnield commented 5 years ago

Sweet! Thank you, looking forward to the next release!