Files
orx/orx-math/src/jvmDemo/kotlin/matrix/DemoLeastSquares01.kt

65 lines
1.9 KiB
Kotlin

package matrix
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.extra.math.matrix.Matrix
import org.openrndr.extra.math.matrix.invertMatrixCholesky
import org.openrndr.extra.noise.uniform
import org.openrndr.extra.noise.uniformRing
import org.openrndr.math.Vector2
import kotlin.math.cos
import kotlin.random.Random
/**
* Demonstrate least squares method to find a regression line through noisy points
* Line drawn in red is the estimated line, in green is the ground-truth line
*
* Ax = b => x = A⁻¹b
* because A is likely inconsistent, we look for an approximate x based on AᵀA, which is consistent.
* x̂ = (AᵀA)⁻¹ Aᵀb
*/
fun main() {
application {
configure {
width = 720
height = 720
}
program {
extend {
val ls = drawer.bounds.horizontal(0.5).rotateBy(cos(seconds)*45.0)
val r = Random((seconds*10).toInt())
val pointCount = 100
val A = Matrix(pointCount, 2)
val b = Matrix(pointCount, 1)
for (i in 0 until pointCount) {
val p = ls.position(Double.uniform(0.0, 1.0, r))
val pr = p + Vector2.uniformRing(0.0, 130.0, r)
A[i, 0] = 1.0
A[i, 1] = pr.x
b[i, 0] = pr.y
drawer.circle(pr, 5.0)
}
val At = A.transposed()
val AtA = At * A
val Atb = At * b
val AtAI = invertMatrixCholesky(AtA)
val x = AtAI * Atb
val p0 = Vector2(0.0, x[0, 0])
val p1 = Vector2(720.0, x[0, 0] + x[1, 0] * 720.0)
drawer.stroke = ColorRGBa.RED
drawer.lineSegment(p0, p1)
drawer.stroke = ColorRGBa.GREEN
drawer.lineSegment(ls)
}
}
}
}