30#ifdef MX_INCLUDE_BOOST
31#include <boost/math/tools/minima.hpp>
43#include "../../math/constants.hpp"
57template <
typename realT>
67template <
typename _realT>
92 realT gain{ std::numeric_limits<realT>::quiet_NaN() };
139 Eigen::Array<realT, -1, -1> m_cs;
140 Eigen::Array<realT, -1, -1> m_ss;
142 std::vector<std::complex<realT>> m_H_dm;
143 std::vector<std::complex<realT>> m_H_wfs;
144 std::vector<std::complex<realT>> m_H_ma;
145 std::vector<std::complex<realT>> m_H_del;
146 std::vector<std::complex<realT>> m_H_con;
218 void b(
const std::vector<realT> &newB );
223 void b(
const Eigen::Array<realT, -1, -1>
239 const std::vector<realT> &
b()
244 void bScale( realT scale );
249 void a(
const std::vector<realT> &newA );
254 void a(
const Eigen::Array<realT, -1, -1> &newA
268 const std::vector<realT> &
a()
273 void aScale( realT scale );
298 void f(
const std::vector<realT> &newF );
401 const std::vector<realT> &PSDerr,
402 const std::vector<realT> &PSDnoise,
414 const std::vector<realT> &PSDnoise,
427 maxStableGainReport *report =
nullptr );
435 const std::vector<realT> &PSDerr,
436 const std::vector<realT> &PSDnoise,
438 optGainReport *report =
nullptr
447 const std::vector<realT> &PSDerr,
448 const std::vector<realT> &PSDnoise,
451 optGainReport *report =
nullptr
463 int nyquist( std::vector<realT> &re, std::vector<realT> &im,
realT g );
466template <
typename realT>
472template <
typename realT>
481template <
typename realT>
502template <
typename realT>
508template <
typename realT>
520template <
typename realT>
526template <
typename realT>
539template <
typename realT>
545template <
typename realT>
548 if(
m_tau == newTau )
557template <
typename realT>
560 if( newB.size() > (
size_t)m_cs.cols() )
569template <
typename realT>
572 if( newB.cols() > m_cs.cols() )
577 m_b.resize( newB.cols() );
579 for(
size_t i = 0; i <
m_b.size(); ++i )
581 m_b[i] = newB( 0, i );
587template <
typename realT>
588void clGainOpt<realT>::bScale( realT scale )
590 for(
size_t n = 0; n < m_b.size(); ++n )
598template <
typename realT>
601 if( newA.size() + 1 > (
size_t)m_cs.cols() )
610template <
typename realT>
613 if( newA.cols() + 1 > m_cs.cols() )
618 m_a.resize( newA.cols() );
620 for(
size_t i = 0; i <
m_a.size(); ++i )
622 m_a[i] = newA( 0, i );
628template <
typename realT>
629void clGainOpt<realT>::aScale( realT scale )
631 for(
size_t n = 0; n < m_a.size(); ++n )
639template <
typename realT>
650template <
typename realT>
656template <
typename realT>
661 if(
m_b.size() != 1 )
669 if(
m_a.size() != 1 )
683template <
typename realT>
687 for(
int i = 0; i < nF; ++i )
696template <
typename realT>
705template <
typename realT>
712template <
typename realT>
719 return olXfer( fi, H_dm, H_del, H_con );
726template <
typename realT>
747 size_t jmax = std::max(
m_a.size() + 1,
m_b.size() );
749 m_cs.resize(
m_f.size(), jmax );
750 m_ss.resize(
m_f.size(), jmax );
752 for(
size_t i = 0; i <
m_f.size(); ++i )
757 for(
size_t j = 1; j < jmax; ++j )
770 m_H_dm.resize(
m_f.size(), 0 );
771 m_H_wfs.resize(
m_f.size(), 0 );
772 m_H_ma.resize(
m_f.size(), 0 );
773 m_H_del.resize(
m_f.size(), 0 );
774 m_H_con.resize(
m_f.size(), 0 );
776 size_t jmax = std::min(
m_a.size(),
m_b.size() );
779 for(
size_t i = 0; i <
m_f.size(); ++i )
800 m_H_dm[i] = std::complex<realT>( 1, 0 );
804 m_H_dm[i] = (
realT( 1 ) - expsT ) / ( s *
m_Ti );
807 m_H_wfs[i] = m_H_dm[i];
811 m_H_del[i] = exp( -s *
m_tau );
816 for(
size_t j = 1; j < jmax; ++j )
819 realT cs = m_cs( i, j );
820 realT ss = m_ss( i, j );
825 FIR +=
m_b[j] * expZ;
830 for(
size_t jj = jmax; jj <
m_a.size() + 1; ++jj )
834 realT cs = m_cs( i, jj );
835 realT ss = m_ss( i, jj );
843 for(
size_t jj = jmax; jj <
m_b.size(); ++jj )
846 realT cs = m_cs( i, jj );
847 realT ss = m_ss( i, jj );
851 FIR +=
m_b[jj] * expZ;
855 m_H_con[i] = FIR / (
realT( 1.0 ) - IIR );
873 return ( m_H_dm[fi] * m_H_wfs[fi] * m_H_del[fi] * m_H_con[fi] );
876template <
typename realT>
890template <
typename realT>
904template <
typename realT>
918template <
typename realT>
933 return -( H_dm * H_del * g * H_con ) / (
realT( 1 ) + g * olX );
936template <
typename realT>
951 complexT NTF = -( H_dm * H_del * g * H_con ) / (
realT( 1 ) + g * olX );
956template <
typename realT>
978 ETF = norm(
realT( 1 ) / (
realT( 1 ) + g * olX ) );
979 NTF = norm( -( H_dm * H_del * g * H_con ) / (
realT( 1 ) + g * olX ) );
987template <
typename realT>
989 realT &varErr,
realT &varNoise,
const std::vector<realT> &PSDerr,
const std::vector<realT> &PSDnoise,
realT g )
991 if(
m_f.size() != PSDerr.size() ||
m_f.size() != PSDnoise.size() )
993 std::cerr <<
"clVariance: Frequency grid and PSDs must be same size." << std::endl;
1004 for(
size_t i = 0; i < PSDerr.size(); ++i )
1013 clTF2( ETF, NTF, i, g );
1015 varErr += ETF * PSDerr[i] * df;
1016 varNoise += NTF * PSDnoise[i] * df;
1019 return varErr + varNoise;
1022template <
typename realT>
1028 return clVariance( varErr, varNoise, PSDerr, PSDnoise, g );
1031template <
typename realT>
1037 gain = std::numeric_limits<realT>::quiet_NaN();
1039 if(
m_f.size() < 2 )
1045 for(
size_t index = 0; index <
m_f.size(); ++index )
1054 std::vector<realT> re, im;
1056 nyquist( re, im, 1.0 );
1058 for(
size_t index = 0; index < re.size(); ++index )
1067 bool crossingFound =
false;
1068 for(
size_t index = 0; index + 1 < re.size(); ++index )
1070 if( !( im[index] < 0 && im[index + 1] >= 0 ) )
1075 const realT fraction = -im[index] / ( im[index + 1] - im[index] );
1076 const realT crossingReal = re[index] + fraction * ( re[index + 1] - re[index] );
1077 const realT crossingGain = -
realT( 1 ) / crossingReal;
1083 if( crossingFound && crossingReal >= activeReport.
crossingReal )
1088 crossingFound =
true;
1095 activeReport.
gain = crossingGain;
1098 if( !crossingFound )
1105 gain = activeReport.
gain;
1113template <
typename realT>
1118 const realT &minimumGain,
1119 const realT &maximumGain,
1121 uintmax_t minFindMaxIter,
1125#ifdef MX_INCLUDE_BOOST
1126 gain = std::numeric_limits<realT>::quiet_NaN();
1127 var = std::numeric_limits<realT>::quiet_NaN();
1131 std::pair<realT, realT> brack;
1132 brack = boost::math::tools::brentm_findm_minima<clGainOptOptGain_OL<realT>, realT>( olgo,
1146 if( iters >= minFindMaxIter )
1153 static_assert( std::is_fundamental<realT>::value || !std::is_fundamental<realT>::value,
1154 "impl::optGainOpenLoop<realT> is not specialized for type realT, and MX_INCLUDE_BOOST is not "
1155 "defined, so I can't just use boost." );
1165 const float &minimumGain,
1166 const float &maximumGain,
1168 uintmax_t minFindMaxIter,
1177 const double &minimumGain,
1178 const double &maximumGain,
1180 uintmax_t minFindMaxIter,
1190 const long double &minimumGain,
1191 const long double &maximumGain,
1193 uintmax_t minFindMaxIter,
1204 const _m_float128 &minimumGain,
1205 const _m_float128 &maximumGain,
1207 uintmax_t minFindMaxIter,
1214template <
typename realT>
1217 const std::vector<realT> &PSDerr,
1218 const std::vector<realT> &PSDnoise,
1227 gain = std::numeric_limits<realT>::quiet_NaN();
1228 var = std::numeric_limits<realT>::quiet_NaN();
1229 if( report !=
nullptr )
1239 rv =
optGainOpenLoop( gain, var, PSDerr, PSDnoise, maximumGain, gridSearch, &optimizationReport );
1240 optimizationReport.
stability = stabilityReport;
1241 if( report !=
nullptr )
1243 *report = optimizationReport;
1248template <
typename realT>
1251 const std::vector<realT> &PSDerr,
1252 const std::vector<realT> &PSDnoise,
1258 optGainReport &activeReport = report ==
nullptr ? localReport : *report;
1261 gain = std::numeric_limits<realT>::quiet_NaN();
1262 var = std::numeric_limits<realT>::quiet_NaN();
1264 if(
m_f.size() < 2 || PSDerr.size() !=
m_f.size() || PSDnoise.size() !=
m_f.size() )
1280 if( !
math::isFinite( requestedMaximum ) || requestedMaximum <= requestedMinimum )
1291 realT minimumGain = requestedMinimum;
1292 realT maximumSearchGain = requestedMaximum;
1293 bool searchBoundarySelected =
false;
1297 const realT gainStep = std::min(
realT( 0.05 ), requestedMaximum - requestedMinimum );
1298 realT currentGain = requestedMaximum;
1299 realT minimumVariance = olgo( currentGain );
1300 realT gainAtMinimum = currentGain;
1302 while( currentGain > requestedMinimum )
1304 const realT nextGain = std::max( requestedMinimum, currentGain - gainStep );
1305 if( nextGain >= currentGain )
1310 currentGain = nextGain;
1311 const realT candidateVariance = olgo( currentGain );
1313 if( candidateVariance < minimumVariance )
1315 minimumVariance = candidateVariance;
1316 gainAtMinimum = currentGain;
1320 minimumGain = std::max( requestedMinimum, gainAtMinimum - gainStep );
1321 maximumSearchGain = std::min( requestedMaximum, gainAtMinimum + gainStep );
1322 searchBoundarySelected = gainAtMinimum == requestedMinimum || gainAtMinimum == requestedMaximum;
1342 activeReport.
gain = gain;
1354 gain = std::numeric_limits<realT>::quiet_NaN();
1355 var = std::numeric_limits<realT>::quiet_NaN();
1356 activeReport.
gain = gain;
1361 if( searchBoundarySelected || gain <= minimumGain || gain >= maximumSearchGain )
1373template <
typename realT>
1377 for(
int f = 0;
f <
m_f.size(); ++
f )
1382 PSD[
f] = PSD[
f] / e;
1388template <
typename realT>
1389int clGainOpt<realT>::nyquist( std::vector<realT> &re, std::vector<realT> &im, realT g )
1391 re.resize( m_f.size() );
1392 im.resize( m_f.size() );
1396 for(
size_t f = 0; f < m_f.size(); ++f )
1398 etf = g * olXfer( f );
1399 re[f] = real( etf );
1400 im[f] = imag( etf );
1409template <
typename realT>
1430extern template class clGainOpt<float>;
1432extern template class clGainOpt<double>;
1434extern template class clGainOpt<long double>;
1437extern template class clGainOpt<_m_float128>;
mx::error_t optGainOpenLoop(realT &gain, realT &var, clGainOptOptGain_OL< realT > &olgo, const realT &minimumGain, const realT &maximumGain, int minFindBits, uintmax_t minFindMaxIter, uintmax_t &iters)
Minimize an open-loop variance objective on a bounded gain interval.
The mxlib error_t type and utilities.
Floating-point classification utilities that remain reliable under fast-math optimization.
error_t
The mxlib error codes.
@ notimpl
A component or technique is not implemented.
@ noerror
No error has occurred.
@ sizeerr
A size was invalid or calculated incorrectly.
@ exception
An exception was thrown.
@ timeout
A timeout occurred.
@ invalidconfig
A config setting was invalid.
@ invalidarg
An argument was invalid.
@ notfound
An item was not found.
@ error
A general error has occurred.
bool isFinite(realT value)
Test whether a floating-point value is finite, including under finite-math-only optimization.
constexpr T two_pi()
Get the value of 2pi.
Bisection worker struct for finding optimum closed loop gain from open loop PSDs.
realT maximumEvaluatedGain
Largest gain evaluated.
size_t evaluations
Objective evaluations performed.
realT minimumEvaluatedGain
Smallest gain evaluated.
const std::vector< realT > * PSDnoise
Measurement-noise PSD.
realT operator()(const realT &g)
Evaluate closed-loop variance at a candidate gain and update diagnostics.
clGainOpt< realT > * go
Gain optimizer used to evaluate variance.
const std::vector< realT > * PSDerr
Open-loop disturbance PSD.
Diagnostic summary of a maximum-stable-gain search.
realT lowerFrequency
Frequency below the crossing.
realT crossingFrequency
Interpolated crossing frequency.
maxStableGainStatus status
Search termination state.
realT crossingReal
Interpolated real Nyquist value.
realT upperFrequency
Frequency above the crossing.
size_t upperIndex
Index above the selected crossing.
realT gain
Maximum stable gain at the crossing.
size_t lowerIndex
Index below the selected crossing.
Diagnostic summary of an open-loop optimum-gain search.
realT maximumEvaluatedGain
Largest evaluated gain.
realT searchMinimumGain
Final minimizer lower bound.
maxStableGainReport stability
Automatic stability-search diagnostics, when requested.
size_t evaluations
Objective evaluations performed.
realT searchMaximumGain
Final minimizer upper bound.
uintmax_t iterations
Minimizer iterations attempted.
realT gain
Best gain returned by the minimizer.
realT requestedMaximumGain
Caller-supplied gain limit.
optGainStatus status
Search termination state.
realT minimumEvaluatedGain
Smallest evaluated gain.
realT variance
Variance at the best gain.
A class to manage optimizing closed-loop gains.
void init()
Initialize this instance.
void a(const std::vector< realT > &newA)
Set the vector of IIR coefficients.
realT a(size_t i)
Get a single IIR coefficient.
int pseudoOpenLoop(std::vector< realT > &PSD, realT g)
Calculate the pseudo open-loop PSD given a closed loop PSD.
realT b(size_t i)
Get a single FIR coefficient.
complexT clNTF(int fi, realT g)
Return the closed loop noise transfer function (NTF) at frequency f for gain g.
bool m_trigCacheChanged
True when frequency, sampling interval, or required controller tap count invalidates m_cs and m_ss.
std::vector< realT > m_f
Vector of frequencies.
void b(const std::vector< realT > &newB)
Set the vector of FIR coefficients.
realT clNTF2(int fi, realT g)
Return the norm of the closed loop noise transfer function (NTF) at frequency f for gain g.
std::vector< realT > m_b
Vector of FIR coefficients.
realT m_tau
The loop delay.
int N()
Get the number of integrations in the (optional) moving average.
realT remember()
Get the remember factor.
const std::vector< realT > & b()
Get the vector of FIR coefficients.
realT clVariance(realT &varErr, realT &varNoise, const std::vector< realT > &PSDerr, const std::vector< realT > &PSDnoise, realT g)
Calculate the closed loop variance given open-loop PSDs and gain.
mx::error_t maxStableGain(realT &gain, maxStableGainReport *report=nullptr)
Find the maximum stable gain for the loop parameters.
bool m_changed
True if any of the members which make up the basic transfer functions are changed.
realT m_minFindMaxFact
The maximum value, as a multiplicative factor of maximum gain.
size_t f_size()
Get the size of the frequency vector.
realT m_maxFindMin
The Minimum value for the maximum stable gain finding algorithm.
void remember(const realT &rem)
Set the remember factor for a leaky integrator.
complexT clETF(int fi, realT g)
Return the closed loop error transfer function (ETF) at frequency f for gain g.
clGainOpt()
Default c'tor.
complexT olXfer(int fi, complexT &H_dm, complexT &H_del, complexT &H_con)
Calculate the open-loop transfer function.
_realT realT
The real data type.
void f(realT *newF, size_t nF)
Set the vector of frequencies.
std::complex< _realT > complexT
The complex data type.
uintmax_t m_minFindMaxIter
The maximum iterations allowed for minimization.
realT clETFPhase(int fi, realT g)
Return the closed loop error transfer function (ETF) phase at frequency f for gain g.
int m_N
Number of integrations in the (optional) moving average. Default is 1.
realT m_Ti
The loop sampling interval.
optGainStatus
Termination state of an open-loop optimum-gain search.
@ boundaryLimited
The reported minimum lies on a search boundary.
@ notRun
No search has been attempted.
@ invalidInput
The PSDs, search controls, or requested interval were invalid.
@ converged
The minimizer converged inside the search interval.
@ stabilityFailure
The automatic maximum-stable-gain search failed.
@ iterationLimit
The minimizer exhausted its iteration limit.
@ calculationFailure
The minimizer threw or returned invalid output.
const std::vector< realT > & a()
Get the vector of IIR coefficients.
maxStableGainStatus
Termination state of a maximum-stable-gain search.
@ crossingFound
A qualifying Nyquist crossing was found.
@ noCrossing
No qualifying Nyquist crossing was found.
@ notRun
No search has been attempted.
@ invalidInput
The frequency grid or derived Nyquist values were invalid.
void setLeakyIntegrator(realT remember)
Set the FIR and IIR coefficients so that the control law is a leaky integrator.
realT m_remember
The leaky integrator forget factor.
realT m_minFindMin
The Minimum value for the minimum finding algorithm.
realT clETF2(int fi, realT g)
Return the norm of the closed loop error transfer function (ETF) at frequency f for gain g.
void clTF2(realT &ETF, realT &NTF, int fi, realT g)
Return the norm of the closed loop transfer functions at frequency f for gain g.
std::vector< realT > m_a
Vector of IIR coefficients.
mx::error_t optGainOpenLoop(realT &gain, realT &var, const std::vector< realT > &PSDerr, const std::vector< realT > &PSDnoise, bool gridSearch, optGainReport *report=nullptr)
Return the optimum closed loop gain given an open loop PSD.
Utilities for working with time.