summaryrefslogtreecommitdiffstats
path: root/vendor/math/optimize.test
diff options
context:
space:
mode:
authorOmar Rizwan <omar@omar.website>2022-12-05 07:14:51 +0000
committerOmar Rizwan <omar@omar.website>2022-12-05 07:14:51 +0000
commit07fc041d7bc0793318dd31ebef6ab2da9bcb3fe2 (patch)
tree202d978bd50fd5dd4c3cce5c690ea3dedb0e62bf /vendor/math/optimize.test
parentWebSocket server (diff)
downloadfolk-07fc041d7bc0793318dd31ebef6ab2da9bcb3fe2.tar.gz
folk-07fc041d7bc0793318dd31ebef6ab2da9bcb3fe2.zip
Vendor tcllib linear algebra
Diffstat (limited to 'vendor/math/optimize.test')
-rw-r--r--vendor/math/optimize.test634
1 files changed, 634 insertions, 0 deletions
diff --git a/vendor/math/optimize.test b/vendor/math/optimize.test
new file mode 100644
index 00000000..f1a84753
--- /dev/null
+++ b/vendor/math/optimize.test
@@ -0,0 +1,634 @@
+# -*- tcl -*-
+# Tests for 1-d optimisation functions in math library -*- tcl -*-
+#
+# This file contains a collection of tests for one or more of the Tcllib
+# procedures. Sourcing this file into Tcl runs the tests and
+# generates output for errors. No output means no errors were found.
+#
+# $Id: optimize.test,v 1.17 2011/01/18 07:49:53 arjenmarkus Exp $
+#
+# Copyright (c) 2004 by Arjen Markus
+# Copyright (c) 2004, 2005 by Kevin B. Kenny
+# All rights reserved.
+#
+# Note:
+# By evaluating the tests in a different namespace than global,
+# we assure that the namespace issue (Bug #...) is checked.
+#
+
+# -------------------------------------------------------------------------
+
+source [file join \
+ [file dirname [file dirname [file join [pwd] [info script]]]] \
+ devtools testutilities.tcl]
+
+testsNeedTcl 8.5
+testsNeedTcltest 2.1
+
+support {
+ useLocal math.tcl math
+}
+testing {
+ useLocal optimize.tcl math::optimize
+}
+
+# -------------------------------------------------------------------------
+
+namespace eval optimizetest {
+
+namespace import ::math::optimize::*
+
+set old_precision $::tcl_precision
+if {![package vsatisfies [package present Tcl] 8.5]} {
+ set ::tcl_precision 17
+} else {
+ set ::tcl_precision 0
+}
+
+#
+# Simple test functions
+#
+proc const_func { x } {
+ return 1.0
+}
+proc ffunc { x } {
+ expr {$x*(1.0-$x*$x)}
+}
+proc minfunc { x } {
+ expr {-$x*(1.0-$x*$x)}
+}
+proc absfunc { x } {
+ expr {abs($x*(1.0-$x*$x))}
+}
+
+proc within_range { result min max } {
+ #puts "Within range? $result $min $max"
+ #puts "[expr {2.0*abs($result-$min)/abs($max+$min)}]"
+ if { $result >= $min && $result <= $max } {
+ set ok 1
+ } else {
+ set ok 0
+ }
+ return $ok
+}
+
+#
+# Test the minimum procedure
+#
+# Note about the uneven and even functions:
+# the initial interval is chosen symmetrical, so that the
+# three function values are equal.
+#
+test optimize-1.1 "Minimum of constant function" {
+ set result [minimum -1.0 1.0 ::optimizetest::const_func]
+ within_range $result -1.0 1.0
+} 1
+
+test optimize-1.2 "Minimum of odd function, case 1" {
+ set result [minimum -1.0 1.0 ::optimizetest::ffunc]
+ set xmin [expr {-sqrt(1.0/3.0)-0.0001}]
+ set xmax [expr {-sqrt(1.0/3.0)+0.0001}]
+ within_range $result $xmin $xmax
+} 1
+
+test optimize-1.3 "Minimum of odd function, asymmetric interval" {
+ set result [minimum -0.8 1.2 ::optimizetest::ffunc]
+ set xmin [expr {-sqrt(1.0/3.0)-0.0001}]
+ set xmax [expr {-sqrt(1.0/3.0)+0.0001}]
+ within_range $result $xmin $xmax
+} 1
+
+test optimize-1.4 "Minimum of odd function, case 2" {
+ set result [minimum -1.0 1.0 ::optimizetest::minfunc]
+ set xmin [expr {sqrt(1.0/3.0)-0.0001}]
+ set xmax [expr {sqrt(1.0/3.0)+0.0001}]
+ within_range $result $xmin $xmax
+} 1
+
+test optimize-1.5 "Minimum of even function" {
+ set result [minimum -1.0 1.0 ::optimizetest::absfunc]
+ set xmin -0.0001
+ set xmax 0.0001
+ within_range $result $xmin $xmax
+} 1
+
+#
+# Test the maximum procedure
+#
+# Note about the uneven and even functions:
+# the initial interval is chosen symmetrical, so that the
+# three function values are equal.
+#
+test optimize-2.1 "Maximum of constant function" {
+ set result [maximum -1.0 1.0 ::optimizetest::const_func]
+ within_range $result -1.0 1.0
+} 1
+
+test optimize-2.2 "Maximum of odd function, case 1" {
+ set result [maximum -1.0 1.0 ::optimizetest::ffunc]
+ set xmin [expr {sqrt(1.0/3.0)-0.0001}]
+ set xmax [expr {sqrt(1.0/3.0)+0.0001}]
+ within_range $result $xmin $xmax
+} 1
+
+test optimize-2.3 "Maximum of odd function, case 2" {
+ set result [maximum -1.0 1.0 ::optimizetest::minfunc]
+ set xmin [expr {-sqrt(1.0/3.0)-0.0001}]
+ set xmax [expr {-sqrt(1.0/3.0)+0.0001}]
+ within_range $result $xmin $xmax
+} 1
+
+#
+# Either of the two maxima will do
+#
+test optimize-2.4 "Maximum of even function" {
+ set result [maximum -1.0 1.0 ::optimizetest::absfunc]
+ set xmin [expr {-sqrt(1.0/3.0)-0.0001}]
+ set xmax [expr {-sqrt(1.0/3.0)+0.0001}]
+ set ok [within_range $result $xmin $xmax]
+ set xmin [expr {sqrt(1.0/3.0)-0.0001}]
+ set xmax [expr {sqrt(1.0/3.0)+0.0001}]
+ incr ok [within_range $result $xmin $xmax]
+} 1
+
+
+# Custom match procedure for approximate results
+
+proc withinEpsilon { shouldBe is } {
+ expr { [string is double $is]
+ && abs( $is - $shouldBe ) < 1.e-07 * abs($shouldBe) }
+}
+
+::tcltest::customMatch withinEpsilon [namespace code withinEpsilon]
+
+test linmin-1.1 {find minimum of a parabola - constrained} \
+ -setup {
+ proc f x { expr { ($x + 3.) * ($x - 1.) } }
+ } \
+ -body {
+ foreach {x y} [min_bound_1d f 10. -10.] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result -1. \
+ -match withinEpsilon
+
+test linmin-1.2 {find minimum of cosine} \
+ -setup {
+ proc f x { expr { cos($x) } }
+ } \
+ -body {
+ foreach { x y } [min_bound_1d f 0. 6.28318] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 3.1415926535897932 \
+ -match withinEpsilon
+
+test linmin-1.3 {find minimum of a bell-shaped function} \
+ -setup {
+ proc f x {
+ set t [expr { $x - 3. }]
+ return [expr { -exp ( -$t * $t / 2 ) }]
+ }
+ } \
+ -body {
+ foreach { x y } [min_bound_1d f 0 30.] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 3. \
+ -match withinEpsilon
+
+test linmin-1.4 {function where parabolic extrapolation never works} \
+ -setup {
+ proc f x { expr { -1. / ( 0.01 + abs( $x - 5.) ) } }
+ } \
+ -body {
+ foreach {x y} [min_bound_1d f 0 20.] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 5. \
+ -match withinEpsilon
+
+test linmin-2.1 {wrong \# args} \
+ -body {
+ min_bound_1d f
+ } \
+ -returnCodes 1 \
+ -result [tcltest::wrongNumArgs min_bound_1d {f x1 x2 args} 1]
+
+test linmin-2.2 {wrong \# args} \
+ -body {
+ min_bound_1d f 0 1 -bad
+ } \
+ -returnCodes 1 \
+ -result "wrong # args, should be \"min_bound_1d f x1 x2 ?-option value?...\""
+
+test linmin-2.3 {bad arg} \
+ -body {
+ min_bound_1d f 0 1 -bad option
+ } \
+ -returnCodes 1 \
+ -result "unknown option \"-bad\", should be -abserror,\
+ -fguess, -guess, -initial,\
+ -maxiter, -relerror, or -trace"
+
+test linmin-2.4 {iteration limit} \
+ -setup {
+ proc f x { expr { -1. / ( 0.01 + abs( $x - 5.) ) } }
+ } \
+ -body {
+ min_bound_1d f 20. 0 -maxiter 10
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -returnCodes 1 \
+ -result "min_bound_1d failed to converge after \\d* steps" \
+ -match regexp
+
+test linmin-3.1 {minimise cos(x), unbounded} \
+ -setup {
+ proc f x { expr { cos($x) } }
+ } -body {
+ foreach { x y } [min_unbound_1d f 3. 3.01] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 3.1415926535897932 \
+ -match withinEpsilon
+
+test linmin-3.2 {minimise cos(x), unbounded, too eager} \
+ -setup {
+ proc f x { expr { cos($x) } }
+ } -body {
+ foreach { x y } [min_unbound_1d f 0.1 0.15] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result [expr { 3. * 3.1415926535897932 }] \
+ -match withinEpsilon
+
+test linmin-3.3 {near underflow in parabolic extrapolation} \
+ -setup {
+ proc f x {
+ expr { ( 1.12712e-22 * $x * $x * $x - 1e-15 ) * $x + 1e-15 }
+ }
+ } \
+ -body {
+ foreach { x y } [min_unbound_1d f 1. 0.] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 130.41372 \
+ -match withinEpsilon
+
+test linmin-3.4 {near underflow in parabolic extrapolation} \
+ -setup {
+ proc f x {
+ expr { ( ( 1e-30 * $x * $x - 1.12712e-22 )
+ * $x * $x * $x - 1e-15 )
+ * $x + 1e-15 }
+ }
+ } \
+ -body {
+ foreach { x y } [min_unbound_1d f 1. 0. -relerror 1e-08] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 8668.4248 \
+ -match withinEpsilon
+
+test linmin-3.5 {parabolic interpolation finds a minimum - case 1} \
+ -setup {
+ proc f x {
+ expr { ( ( ( 1e-5 * $x - 2.69672 )
+ * $x + 10.0902 )
+ * $x - 8.39345 )
+ * $x + 1. }
+ }
+ } \
+ -body {
+ foreach { x y } [min_unbound_1d f 1. 0. -relerror 1e-08] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 0.527450252 \
+ -match withinEpsilon
+
+test linmin-3.6 {parabolic interpolation finds a minimum - case 2} \
+ -setup {
+ proc f x {
+ expr { ( ( 0.125669 * $x * $x - 0.982687 )
+ * $x - 0.142982 )
+ * $x + 1 }
+ }
+ } \
+ -body {
+ foreach { x y } [min_unbound_1d f 1. 0. -relerror 1e-08] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 2.0127451 \
+ -match withinEpsilon
+
+test linmin-3.7 {parabolic interpolation is useless} \
+ -setup {
+ proc f x {
+ expr { ( ( ( 1e-5 * $x - 6.79171 )
+ * $x + 24.8107 )
+ * $x - 19.019 )
+ * $x + 1. }
+ }
+ } \
+ -body {
+ foreach { x y } [min_unbound_1d f 1 0 -relerror 1e-8] break
+ set x
+ } \
+ -cleanup {
+ rename f {}
+ } \
+ -result 509375.81 \
+ -match withinEpsilon
+
+test linmin-4.1 {wrong \# args} \
+ -body {
+ min_unbound_1d f
+ } \
+ -returnCodes 1 \
+ -result [tcltest::wrongNumArgs min_unbound_1d {f x1 x2 args} 1]
+
+test linmin-4.2 {wrong \# args} \
+ -body {
+ min_unbound_1d f 0 1 -bad
+ } \
+ -returnCodes 1 \
+ -result "wrong # args, should be \"min_unbound_1d f x1 x2 ?-option value?...\""
+
+test linmin-4.3 {bad arg} \
+ -body {
+ min_unbound_1d f 0 1 -bad option
+ } \
+ -returnCodes 1 \
+ -result "unknown option \"-bad\", should be -trace"
+
+#
+# Test the solveLinearProgram procedure
+#
+
+set ::symm_constraints {
+ { 1.0 2.0 1.0 }
+ { 2.0 1.0 1.0 } }
+
+test linprog-1.0 "Symmetric constraints, case 1" \
+ -body {
+ set result [solveLinearProgram {1.0 1.0} $::symm_constraints]
+ set ok 1
+ if { ! [within_range [lindex $result 0] 0.333300 0.333360] ||
+ ! [within_range [lindex $result 1] 0.333300 0.333360] } {
+ set ok 0
+ }
+ set ok
+ } \
+ -result 1
+
+test linprog-1.1 "Symmetric constraints, case 2" \
+ -body {
+ set result [solveLinearProgram {1.0 0.0} $::symm_constraints]
+ set ok 1
+ if { ! [within_range [lindex $result 0] 0.49900 0.50100] ||
+ ! [within_range [lindex $result 1] -0.00100 0.00100] } {
+ set ok 0
+ }
+ set ok
+ } \
+ -result 1
+
+test linprog-1.2 "Symmetric constraints, case 3" \
+ -body {
+ set result [solveLinearProgram {0.0 1.0} $::symm_constraints]
+ set ok 1
+ if { ! [within_range [lindex $result 1] 0.499900 0.500100] ||
+ ! [within_range [lindex $result 0] -0.000100 0.000100] } {
+ set ok 0
+ }
+ set ok
+ } \
+ -result 1
+
+test linprog-1.3 "Symmetric constraints, case 4" \
+ -body {
+ set result [solveLinearProgram {3.0 4.0} $::symm_constraints]
+ set ok 1
+ if { ! [within_range [lindex $result 0] 0.333300 0.333360] ||
+ ! [within_range [lindex $result 1] 0.333300 0.333360] } {
+ set ok 0
+ }
+ set ok
+ } \
+ -result 1
+
+test linprog-2.1 "Unbounded program 1" \
+ -body {
+ set result [solveLinearProgram {3.0 4.0} {{1.0 -2.0 1.0} {-2.0 1.0 1.0}} ]
+ } \
+ -result "unbounded"
+
+test linprog-2.2 "Unbounded program 2" \
+ -body {
+ set result [::math::optimize::solveLinearProgram {2.0 1.0} {{3.0 0.0 6.0} {1.0 0.0 2.0}}]
+ } \
+ -result "unbounded"
+
+test linprog-2.3 "Infeasible program" \
+ -body {
+ set result [::math::optimize::solveLinearProgram {2.0 1.0} {{3.0 1.0 6.0} {1.0 -1.0 2.0} {0.0 1.0 -3.0}}]
+ } \
+ -result "infeasible"
+
+test linprog-2.4 "Degenerate program" \
+ -body {
+ # Solution: {1.0 3.0}
+ set result [::math::optimize::solveLinearProgram {2.0 1.0} {{3.0 1.0 6.0} {1.0 -1.0 2.0} {0.0 1.0 3.0}}]
+ set ok 1
+ if { ! [within_range [lindex $result 0] 0.99999 1.00001] ||
+ ! [within_range [lindex $result 1] 2.99999 3.00001] } {
+ set ok 0
+ }
+ set ok
+
+ } \
+ -result 1
+
+test linprog-3.1 "Simple 3D program" \
+ -body {
+ set result [solveLinearProgram \
+ {1.0 1.0 1.0} \
+ {{1.0 1.0 2.0 1.0}
+ {1.0 2.0 1.0 1.0}
+ {2.0 1.0 1.0 1.0}}]
+ set ok 1
+ if { ! [within_range [lindex $result 0] 0.249900 0.250100] ||
+ ! [within_range [lindex $result 1] 0.249900 0.250100] ||
+ ! [within_range [lindex $result 2] 0.249900 0.250100] } {
+ set ok 0
+ }
+ set ok
+ } \
+ -result 1
+
+test nelderMead-1.1 "Nelder-Mead - wrong \# args" \
+ -body {
+ ::math::optimize::nelderMead f {0.0 0.0} -bogus
+ } \
+ -returnCodes error \
+ -match glob \
+ -result "wrong \# args*"
+test nelderMead-1.2 "Nelder-Mead - bad param" \
+ -body {
+ ::math::optimize::nelderMead f {0.0 0.0} -bogus 1
+ } \
+ -returnCodes error \
+ -match glob \
+ -result {unknown option "-bogus"*}
+test nelderMead-1.3 "Nelder-Mead - bad size of scale" \
+ -body {
+ ::math::optimize::nelderMead f {0.0 0.0} -scale {0 0 0}
+ } \
+ -returnCodes error \
+ -result {-scale vector must be of same size as starting x vector}
+
+# Easy case - minimize in a paraboloid
+
+test nelderMead-2.1 "Nelder-Mead - easy" \
+ -setup {
+ proc f {x y} {
+ expr {($x-3.)*($x-3.) + ($y-2.)*($y-2.) + 1.}
+ }
+ } \
+ -body {
+ array set dd [::math::optimize::nelderMead f {1. 1.}]
+ foreach {x y} $dd(x) break
+ expr { abs($x-3.) < 0.001 && abs($y-2.) < 0.001 }
+ } \
+ -cleanup {
+ rename f {}; unset dd
+ } \
+ -result 1
+
+test nelderMead-2.2 "Nelder-Mead - easy" \
+ -setup {
+ proc f {x y} {
+ expr {($x-3.)*($x-3.) + ($y-2.)*($y-2.) + 1.}
+ }
+ } \
+ -body {
+ array set dd [::math::optimize::nelderMead f {0. 0.}]
+ foreach {x y} $dd(x) break
+ expr { abs($x-3.) < 0.001 && abs($y-2.) < 0.001 }
+ } \
+ -cleanup {
+ rename f {}; unset dd
+ } \
+ -result 1
+
+# Slalom down a sinuous valley - exercises most of the code
+
+test nelderMead-2.3 "Nelder-Mead - sinuous valley" \
+ -setup {
+ set pi 3.1415926535897932
+ proc f {x y} {
+ set xx [expr { $x - 3.1415926535897932 / 2. }]
+ set v1 [expr { 0.3 * exp( -$xx*$xx / 2. ) }]
+ set d [expr { 10. * $y - sin(9. * $x) }]
+ set v2 [expr { exp(-10.*$d*$d)}]
+ set rv [expr { -$v1 - $v2 }]
+ return $rv
+ }
+ } \
+ -body {
+ array set dd [::math::optimize::nelderMead f {1. 0.} -scale {0.1 0.01}]
+ foreach {x y} $dd(x) break
+ expr { abs($x-$pi/2) < 0.001 && abs($y-0.1) < 0.001 }
+ } \
+ -cleanup {rename f {}; unset dd} \
+ -result 1
+
+# Exercise the difficult case where the simplex has to contract about the
+# low point because all else has failed.
+
+test nelderMead-2.4 "Nelder-Mead - simplex contracts about the minimum" \
+ -setup {
+ proc g {a b} {
+ set x1 [expr {0.1 - $a + $b}]
+ set x2 [expr {$a + $b - 1.}]
+ set x3 [expr {3.-8.*$a+8.*$a*$a-8.*$b+8.*$b*$b}]
+ set x4 [expr {$a/10. + $b/10. + $x1*$x1/3. + $x2*$x2
+ - $x2 * exp(1-$x3*$x3)}]
+ return $x4
+ }
+ } \
+ -body {
+ array set dd [::math::optimize::nelderMead g {0. 0.} \
+ -scale {1. 1.} -ftol 1e-10]
+ foreach {x y} $dd(x) break
+ expr { abs($x-0.774561) < 0.00005 && abs($y-0.755644) < 0.00005 }
+ } \
+ -cleanup {
+ rename g {}; unset dd
+ } \
+ -result 1
+
+# Make sure the method deals gracefully with a "valley"
+# (Ticket UUID: 3193459)
+
+test nelderMead-2.5 "Nelder-Mead - indeterminate minimum (valley)" \
+ -setup {
+ proc h {a b} {
+ return [expr {abs($a-$b)}]
+ }
+ } \
+ -body {
+ array set dd [::math::optimize::nelderMead h {1. 1.}]
+ foreach {x y} $dd(x) break
+ expr { abs($x-1.) < 0.00005 && abs($y-1.) < 0.00005 }
+ } \
+ -cleanup {
+ rename h {}; unset dd
+ } \
+ -result 1
+
+testsuiteCleanup
+
+# Restore precision
+set ::tcl_precision $old_precision
+
+# Local Variables:
+# mode: tcl
+# End:
+
+} ;# End of optimizetest namespace
+
+