mxlib
c++ tools for analyzing astronomical data and other tasks by Jared R. Males. [git repo]
Loading...
Searching...
No Matches
clAOLinearPredictor_test.cpp
Go to the documentation of this file.
1/** \file clAOLinearPredictor_test.cpp
2 * \brief Tests of closed-loop linear-predictor regularization.
3 */
4
5#include "../../../catch2/catch.hpp"
6
7#define MX_NO_ERROR_REPORTS
8
10
11#include <cmath>
12#include <limits>
13#include <vector>
14
15namespace
16{
17
20
21/// Construct a compact, valid PSD fixture for regularization tests.
22void makeFixture( optimizerT &optimizer, /**< [out] configured gain optimizer */
23 std::vector<double> &disturbance, /**< [out] positive disturbance PSD */
24 std::vector<double> &noise /**< [out] nonnegative noise PSD */ )
25{
26 constexpr std::size_t sampleCount = 64;
27 std::vector<double> frequency( sampleCount );
28 disturbance.resize( sampleCount );
29 noise.assign( sampleCount, 1e-3 );
30
31 for( std::size_t index = 0; index < sampleCount; ++index )
32 {
33 frequency[index] = 0.5 * static_cast<double>( index + 1 ) / static_cast<double>( sampleCount );
34 disturbance[index] = 1.0 / ( 1.0 + 100.0 * frequency[index] * frequency[index] );
35 }
36
37 optimizer.f( frequency );
38}
39
40/// Run one regularization search with telemetry enabled.
41mx::error_t runSearch( predictorT &predictor, /**< [in,out] predictor under test */
42 optimizerT &optimizer, /**< [in,out] configured gain optimizer */
43 std::vector<double> &disturbance, /**< [in] disturbance PSD */
44 std::vector<double> &noise /**< [in] noise PSD */ )
45{
46 double maximumGain = 0;
47 double optimalGain = 0;
48 double variance = 0;
49 double scale = 0;
50 return predictor
51 .regularizeCoefficients<true>( maximumGain, optimalGain, variance, scale, optimizer, disturbance, noise, 4 );
52}
53
54} // namespace
55
56/// Verify that invalid linear-predictor regularization controls are rejected before evaluation.
57/** Exercises validation of the regularization interval, spacing, refinement divisor, and iteration limit. */
58/**
59 * \ingroup clAOLinearPredictor_unit_tests
60 */
61TEST_CASE( "Linear-predictor regularization rejects invalid search controls", "[ao::analysis::clAOLinearPredictor]" )
62{
63 // clang-format off
64#ifdef __DOXY_ONLY__
66#endif
67 // clang-format on
68
69 optimizerT optimizer( 1.0, 1.5 );
70 std::vector<double> disturbance;
71 std::vector<double> noise;
72 makeFixture( optimizer, disturbance, noise );
73
74 GIVEN( "a fresh predictor" )
75 {
76 predictorT predictor;
77
78 WHEN( "the initial precision is below the minimum" )
79 {
80 predictor.m_precision0 = 0.5 * predictor.m_minPrecision;
81 REQUIRE( runSearch( predictor, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
82 }
83
84 WHEN( "the initial precision equals the minimum" )
85 {
86 predictor.m_precision0 = predictor.m_minPrecision;
87 REQUIRE( runSearch( predictor, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
88 }
89
90 WHEN( "the initial precision is zero, negative, NaN, or infinite" )
91 {
92 const std::vector<double> invalidValues{ 0,
93 -1,
94 std::numeric_limits<double>::quiet_NaN(),
95 std::numeric_limits<double>::infinity() };
96 for( const double value : invalidValues )
97 {
98 predictorT invalidPredictor;
99 invalidPredictor.m_precision0 = value;
100 REQUIRE( runSearch( invalidPredictor, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
101 REQUIRE( invalidPredictor.m_regularizationReport.status ==
102 predictorT::regularizationStatus::invalidControls );
103 REQUIRE( invalidPredictor.m_regularizationReport.evaluations == 0 );
104 REQUIRE( invalidPredictor.m_regResults.empty() );
105 }
106 }
107
108 WHEN( "the initial precision is wider than the initial interval" )
109 {
110 predictor.m_precision0 = predictor.m_max_sc0 - predictor.m_min_sc0 + 1;
111 REQUIRE( runSearch( predictor, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
112 }
113
114 WHEN( "the scale interval, refinement divisor, or iteration limit is invalid" )
115 {
116 predictorT reversedInterval;
117 reversedInterval.m_max_sc0 = reversedInterval.m_min_sc0;
118 REQUIRE( runSearch( reversedInterval, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
119
120 predictorT invalidDivisor;
121 invalidDivisor.m_dPrecision = 1;
122 REQUIRE( runSearch( invalidDivisor, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
123
124 predictorT invalidIterations;
125 invalidIterations.m_maxIts = 0;
126 REQUIRE( runSearch( invalidIterations, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
127 }
128
129 WHEN( "another floating-point search control is nonfinite or nonpositive" )
130 {
131 predictorT nonfiniteMinimumScale;
132 nonfiniteMinimumScale.m_min_sc0 = std::numeric_limits<double>::quiet_NaN();
133 REQUIRE( runSearch( nonfiniteMinimumScale, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
134
135 predictorT nonfiniteMaximumScale;
136 nonfiniteMaximumScale.m_max_sc0 = std::numeric_limits<double>::infinity();
137 REQUIRE( runSearch( nonfiniteMaximumScale, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
138
139 predictorT zeroMinimumPrecision;
140 zeroMinimumPrecision.m_minPrecision = 0;
141 REQUIRE( runSearch( zeroMinimumPrecision, optimizer, disturbance, noise ) == mx::error_t::invalidconfig );
142
143 predictorT nonfiniteRefinementDivisor;
144 nonfiniteRefinementDivisor.m_dPrecision = std::numeric_limits<double>::infinity();
145 REQUIRE( runSearch( nonfiniteRefinementDivisor, optimizer, disturbance, noise ) ==
147 }
148 }
149}
150
151/// Verify that linear-predictor regularization reports how its search terminated.
152/** Exercises completed, boundary-limited, and iteration-limited regularization searches. */
153/**
154 * \ingroup clAOLinearPredictor_unit_tests
155 */
156TEST_CASE( "Linear-predictor regularization reports search termination", "[ao::analysis::clAOLinearPredictor]" )
157{
158 // clang-format off
159#ifdef __DOXY_ONLY__
161#endif
162 // clang-format on
163
164 optimizerT optimizer( 1.0, 1.5 );
165 std::vector<double> disturbance;
166 std::vector<double> noise;
167 makeFixture( optimizer, disturbance, noise );
168
169 GIVEN( "a precision immediately above the minimum and one allowed iteration" )
170 {
171 predictorT predictor;
172 predictor.m_min_sc0 = 10;
173 predictor.m_max_sc0 = 10.0025;
174 predictor.m_precision0 = std::nextafter( predictor.m_minPrecision, std::numeric_limits<double>::infinity() );
175 predictor.m_maxIts = 1;
176
177 const mx::error_t result = runSearch( predictor, optimizer, disturbance, noise );
178
179 REQUIRE( result != mx::error_t::invalidconfig );
180 REQUIRE( predictor.m_regularizationReport.status != predictorT::regularizationStatus::invalidControls );
181 REQUIRE( predictor.m_regularizationReport.iterations == 1 );
182 REQUIRE( predictor.m_regularizationReport.evaluations > 0 );
183 REQUIRE_FALSE( predictor.m_regResults.empty() );
184 }
185
186 GIVEN( "a valid search forced to exhaust its iteration limit" )
187 {
188 predictorT predictor;
189 predictor.m_min_sc0 = 10;
190 predictor.m_max_sc0 = 12;
191 predictor.m_precision0 = 1;
192 predictor.m_minPrecision = 1e-9;
193 predictor.m_maxIts = 1;
194
195 REQUIRE( runSearch( predictor, optimizer, disturbance, noise ) == mx::error_t::timeout );
196 REQUIRE( predictor.m_regularizationReport.status == predictorT::regularizationStatus::iterationLimit );
197 REQUIRE( predictor.m_regularizationReport.iterations == 1 );
198 REQUIRE( predictor.m_regularizationReport.evaluations > 0 );
199 }
200
201 GIVEN( "a valid search whose initial precision equals the interval width" )
202 {
203 predictorT predictor;
204 predictor.m_min_sc0 = 10;
205 predictor.m_max_sc0 = 12;
206 predictor.m_precision0 = 2;
207 predictor.m_minPrecision = 0.1;
208
209 REQUIRE( runSearch( predictor, optimizer, disturbance, noise ) == mx::error_t::noerror );
210 REQUIRE( ( predictor.m_regularizationReport.status == predictorT::regularizationStatus::converged ||
211 predictor.m_regularizationReport.status == predictorT::regularizationStatus::boundaryLimited ) );
212 REQUIRE( predictor.m_regularizationReport.iterations > 0 );
213 REQUIRE( predictor.m_regularizationReport.evaluations > 0 );
214 }
215}
Provides a class to manage closed loop gain linear predictor determination.
TEST_CASE("Linear-predictor regularization rejects invalid search controls", "[ao::analysis::clAOLinearPredictor]")
Verify that invalid linear-predictor regularization controls are rejected before evaluation.
error_t
The mxlib error codes.
Definition error_t.hpp:26
@ noerror
No error has occurred.
Definition error_t.hpp:27
@ timeout
A timeout occurred.
Definition error_t.hpp:49
@ invalidconfig
A config setting was invalid.
Definition error_t.hpp:30
Class to manage the calculation of linear predictor coefficients for a closed-loop AO system.
mx::error_t regularizeCoefficients(realT &gmax_lp, realT &gopt_lp, realT &var_lp, realT &min_sc, clGainOpt< realT > &go_lp, std::vector< realT > &PSDt, std::vector< realT > &PSDn, int Nc)
Regularize the PSD and calculate the associated LP coefficients.
A class to manage optimizing closed-loop gains.
Definition clGainOpt.hpp:69