Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:40:42

0001 //  (C) Copyright Nick Thompson 2020.
0002 //  Use, modification and distribution are subject to the
0003 //  Boost Software License, Version 1.0. (See accompanying file
0004 //  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
0005 #ifndef BOOST_MATH_TOOLS_ULP_PLOT_HPP
0006 #define BOOST_MATH_TOOLS_ULP_PLOT_HPP
0007 #include <algorithm>
0008 #include <iostream>
0009 #include <iomanip>
0010 #include <cassert>
0011 #include <vector>
0012 #include <utility>
0013 #include <fstream>
0014 #include <string>
0015 #include <list>
0016 #include <random>
0017 #include <limits>
0018 #include <stdexcept>
0019 #include <boost/math/tools/is_standalone.hpp>
0020 #include <boost/math/tools/condition_numbers.hpp>
0021 
0022 #ifndef BOOST_MATH_STANDALONE
0023 #include <boost/random/uniform_real_distribution.hpp>
0024 #endif
0025 
0026 // Design of this function comes from:
0027 // https://blogs.mathworks.com/cleve/2017/01/23/ulps-plots-reveal-math-function-accurary/
0028 
0029 // The envelope is the maximum of 1/2 and half the condition number of function evaluation.
0030 
0031 namespace boost::math::tools {
0032 
0033 namespace detail {
0034 template<class F1, class F2, class CoarseReal, class PreciseReal>
0035 void write_gridlines(std::ostream& fs, int horizontal_lines, int vertical_lines,
0036                      F1 x_scale, F2 y_scale, CoarseReal min_x, CoarseReal max_x, PreciseReal min_y, PreciseReal max_y,
0037                      int graph_width, int graph_height, int margin_left, std::string const & font_color)
0038 {
0039   // Make a grid:
0040   for (int i = 1; i <= horizontal_lines; ++i) {
0041       PreciseReal y_cord_dataspace = min_y +  ((max_y - min_y)*i)/horizontal_lines;
0042       auto y = y_scale(y_cord_dataspace);
0043       fs << "<line x1='0' y1='" << y << "' x2='" << graph_width
0044          << "' y2='" << y
0045          << "' stroke='gray' stroke-width='1' opacity='0.5' stroke-dasharray='4' />\n";
0046 
0047       fs << "<text x='" <<  -margin_left/4 + 5 << "' y='" << y - 3
0048          << "' font-family='times' font-size='10' fill='" << font_color << "' transform='rotate(-90 "
0049          << -margin_left/4 + 8 << " " << y + 5 << ")'>"
0050          << std::setprecision(4) << y_cord_dataspace << "</text>\n";
0051    }
0052 
0053     for (int i = 1; i <= vertical_lines; ++i) {
0054         CoarseReal x_cord_dataspace = min_x +  ((max_x - min_x)*i)/vertical_lines;
0055         CoarseReal x = x_scale(x_cord_dataspace);
0056         fs << "<line x1='" << x << "' y1='0' x2='" << x
0057            << "' y2='" << graph_height
0058            << "' stroke='gray' stroke-width='1' opacity='0.5' stroke-dasharray='4' />\n";
0059 
0060         fs << "<text x='" <<  x - 10  << "' y='" << graph_height + 10
0061            << "' font-family='times' font-size='10' fill='" << font_color << "'>"
0062            << std::setprecision(4) << x_cord_dataspace << "</text>\n";
0063     }
0064 }
0065 }
0066 
0067 template<class F, typename PreciseReal, typename CoarseReal>
0068 class ulps_plot {
0069 public:
0070     ulps_plot(F hi_acc_impl, CoarseReal a, CoarseReal b,
0071              size_t samples = 1000, bool perturb_abscissas = false, int random_seed = -1);
0072 
0073     ulps_plot& clip(PreciseReal clip);
0074 
0075     ulps_plot& width(int width);
0076 
0077     ulps_plot& envelope_color(std::string const & color);
0078 
0079     ulps_plot& title(std::string const & title);
0080 
0081     ulps_plot& background_color(std::string const & background_color);
0082 
0083     ulps_plot& font_color(std::string const & font_color);
0084 
0085     ulps_plot& crop_color(std::string const & color);
0086 
0087     ulps_plot& nan_color(std::string const & color);
0088 
0089     ulps_plot& ulp_envelope(bool write_ulp);
0090 
0091     template<class G>
0092     ulps_plot& add_fn(G g, std::string const & color = "steelblue");
0093 
0094     ulps_plot& horizontal_lines(int horizontal_lines);
0095 
0096     ulps_plot& vertical_lines(int vertical_lines);
0097 
0098     void write(std::string const & filename) const;
0099 
0100     friend std::ostream& operator<<(std::ostream& fs, ulps_plot const & plot)
0101     {
0102         using std::abs;
0103         using std::floor;
0104         using std::isnan;
0105         if (plot.ulp_list_.size() == 0)
0106         {
0107             throw std::domain_error("No functions added for comparison.");
0108         }
0109         if (plot.width_ <= 1)
0110         {
0111             throw std::domain_error("Width = " + std::to_string(plot.width_) + ", which is too small.");
0112         }
0113 
0114         PreciseReal worst_ulp_distance = 0;
0115         PreciseReal min_y = (std::numeric_limits<PreciseReal>::max)();
0116         PreciseReal max_y = std::numeric_limits<PreciseReal>::lowest();
0117         for (auto const & ulp_vec : plot.ulp_list_)
0118         {
0119             for (auto const & ulp : ulp_vec)
0120             {
0121                 if (static_cast<PreciseReal>(abs(ulp)) > worst_ulp_distance)
0122                 {
0123                     worst_ulp_distance = static_cast<PreciseReal>(abs(ulp));
0124                 }
0125                 if (static_cast<PreciseReal>(ulp) < min_y)
0126                 {
0127                     min_y = static_cast<PreciseReal>(ulp);
0128                 }
0129                 if (static_cast<PreciseReal>(ulp) > max_y)
0130                 {
0131                     max_y = static_cast<PreciseReal>(ulp);
0132                 }
0133             }
0134         }
0135 
0136         // half-ulp accuracy is the best that can be expected; sometimes we can get less, but barely less.
0137         // then the axes don't show up; painful!
0138         if (max_y < 0.5) {
0139             max_y = 0.5;
0140         }
0141         if (min_y > -0.5) {
0142             min_y = -0.5;
0143         }
0144 
0145         if (plot.clip_ > 0)
0146         {
0147             if (max_y > plot.clip_)
0148             {
0149                 max_y = plot.clip_;
0150             }
0151             if (min_y < -plot.clip_)
0152             {
0153                 min_y = -plot.clip_;
0154             }
0155         }
0156 
0157         int height = static_cast<int>(floor(static_cast<double>(plot.width_)/1.61803));
0158         int margin_top = 40;
0159         int margin_left = 25;
0160         if (plot.title_.size() == 0)
0161         {
0162             margin_top = 10;
0163             margin_left = 15;
0164         }
0165         int margin_bottom = 20;
0166         int margin_right = 20;
0167         int graph_height = height - margin_bottom - margin_top;
0168         int graph_width = plot.width_ - margin_left - margin_right;
0169 
0170         // Maps [a,b] to [0, graph_width]
0171         auto x_scale = [&](CoarseReal x)->CoarseReal
0172         {
0173             return ((x-plot.a_)/(plot.b_ - plot.a_))*static_cast<CoarseReal>(graph_width);
0174         };
0175 
0176         auto y_scale = [&](PreciseReal y)->PreciseReal
0177         {
0178             return ((max_y - y)/(max_y - min_y) )*static_cast<PreciseReal>(graph_height);
0179         };
0180 
0181         fs << "<?xml version=\"1.0\" encoding='UTF-8' ?>\n"
0182            << "<svg xmlns='http://www.w3.org/2000/svg' width='"
0183            << plot.width_ << "' height='"
0184            << height << "'>\n"
0185            << "<style>\nsvg { background-color:" << plot.background_color_ << "; }\n"
0186            << "</style>\n";
0187         if (plot.title_.size() > 0)
0188         {
0189             fs << "<text x='" << floor(plot.width_/2)
0190                << "' y='" << floor(margin_top/2)
0191                << "' font-family='Palatino' font-size='25' fill='"
0192                << plot.font_color_  << "'  alignment-baseline='middle' text-anchor='middle'>"
0193                << plot.title_
0194                << "</text>\n";
0195         }
0196 
0197         // Construct SVG group to simplify the calculations slightly:
0198         fs << "<g transform='translate(" << margin_left << ", " << margin_top << ")'>\n";
0199             // y-axis:
0200         fs  << "<line x1='0' y1='0' x2='0' y2='" << graph_height
0201             << "' stroke='gray' stroke-width='1'/>\n";
0202         PreciseReal x_axis_loc = y_scale(static_cast<PreciseReal>(0));
0203         fs << "<line x1='0' y1='" << x_axis_loc
0204             << "' x2='" << graph_width << "' y2='" << x_axis_loc
0205             << "' stroke='gray' stroke-width='1'/>\n";
0206 
0207         if (worst_ulp_distance > 3)
0208         {
0209             detail::write_gridlines(fs, plot.horizontal_lines_, plot.vertical_lines_, x_scale, y_scale, plot.a_, plot.b_,
0210                                     min_y, max_y, graph_width, graph_height, margin_left, plot.font_color_);
0211         }
0212         else
0213         {
0214             std::vector<double> ys{-3.0, -2.5, -2.0, -1.5, -1.0, -0.5, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0};
0215             for (double & i : ys)
0216             {
0217                 if (min_y <= i && i <= max_y)
0218                 {
0219                     PreciseReal y_cord_dataspace = i;
0220                     PreciseReal y = y_scale(y_cord_dataspace);
0221                     fs << "<line x1='0' y1='" << y << "' x2='" << graph_width
0222                        << "' y2='" << y
0223                        << "' stroke='gray' stroke-width='1' opacity='0.5' stroke-dasharray='4' />\n";
0224 
0225                     fs << "<text x='" <<  -margin_left/2 << "' y='" << y - 3
0226                        << "' font-family='times' font-size='10' fill='" << plot.font_color_ << "' transform='rotate(-90 "
0227                        << -margin_left/2 + 7 << " " << y << ")'>"
0228                        <<  std::setprecision(4) << y_cord_dataspace << "</text>\n";
0229                 }
0230             }
0231             for (int i = 1; i <= plot.vertical_lines_; ++i)
0232             {
0233                 CoarseReal x_cord_dataspace = plot.a_ +  ((plot.b_ - plot.a_)*i)/plot.vertical_lines_;
0234                 CoarseReal x = x_scale(x_cord_dataspace);
0235                 fs << "<line x1='" << x << "' y1='0' x2='" << x
0236                    << "' y2='" << graph_height
0237                    << "' stroke='gray' stroke-width='1' opacity='0.5' stroke-dasharray='4' />\n";
0238 
0239                 fs << "<text x='" <<  x - 10  << "' y='" << graph_height + 10
0240                    << "' font-family='times' font-size='10' fill='" << plot.font_color_ << "'>"
0241                    << std::setprecision(4) << x_cord_dataspace << "</text>\n";
0242             }
0243         }
0244 
0245         int color_idx = 0;
0246         for (auto const & ulp : plot.ulp_list_)
0247         {
0248             std::string color = plot.colors_[color_idx++];
0249             for (size_t j = 0; j < ulp.size(); ++j)
0250             {
0251                 if (isnan(ulp[j]))
0252                 {
0253                     if(plot.nan_color_ == "")
0254                         continue;
0255                     CoarseReal x = x_scale(plot.coarse_abscissas_[j]);
0256                     PreciseReal y = y_scale(static_cast<PreciseReal>(plot.clip_));
0257                     fs << "<circle cx='" << x << "' cy='" << y << "' r='1' fill='" << plot.nan_color_ << "'/>\n";
0258                     y = y_scale(static_cast<PreciseReal>(-plot.clip_));
0259                     fs << "<circle cx='" << x << "' cy='" << y << "' r='1' fill='" << plot.nan_color_ << "'/>\n";
0260                 }
0261                 if (plot.clip_ > 0 && static_cast<PreciseReal>(abs(ulp[j])) > plot.clip_)
0262                 {
0263                    if (plot.crop_color_ == "")
0264                       continue;
0265                    CoarseReal x = x_scale(plot.coarse_abscissas_[j]);
0266                    PreciseReal y = y_scale(static_cast<PreciseReal>(ulp[j] < 0 ? -plot.clip_ : plot.clip_));
0267                    fs << "<circle cx='" << x << "' cy='" << y << "' r='1' fill='" << plot.crop_color_ << "'/>\n";
0268                 }
0269                 else
0270                 {
0271                    CoarseReal x = x_scale(plot.coarse_abscissas_[j]);
0272                    PreciseReal y = y_scale(static_cast<PreciseReal>(ulp[j]));
0273                    fs << "<circle cx='" << x << "' cy='" << y << "' r='1' fill='" << color << "'/>\n";
0274                 }
0275             }
0276         }
0277 
0278         if (plot.ulp_envelope_)
0279         {
0280             std::string close_path = "' stroke='"  + plot.envelope_color_ + "' stroke-width='1' fill='none'></path>\n";
0281             size_t jstart = 0;
0282             while (plot.cond_[jstart] > max_y)
0283             {
0284                 ++jstart;
0285                 if (jstart >= plot.cond_.size())
0286                 {
0287                     goto done;
0288                 }
0289             }
0290 
0291             size_t jmin = jstart;
0292         new_top_path:
0293             if (jmin >= plot.cond_.size())
0294             {
0295                 goto start_bottom_paths;
0296             }
0297             fs << "<path d='M" << x_scale(plot.coarse_abscissas_[jmin]) << " " << y_scale(plot.cond_[jmin]);
0298 
0299             for (size_t j = jmin + 1; j < plot.coarse_abscissas_.size(); ++j)
0300             {
0301                 bool bad = isnan(plot.cond_[j]) || (plot.cond_[j] > max_y);
0302                 if (bad)
0303                 {
0304                     ++j;
0305                     while ( (j < plot.coarse_abscissas_.size() - 2) && bad)
0306                     {
0307                         bad = isnan(plot.cond_[j]) || (plot.cond_[j] > max_y);
0308                         ++j;
0309                     }
0310                     jmin = j;
0311                     fs << close_path;
0312                     goto new_top_path;
0313                 }
0314 
0315                 CoarseReal t = x_scale(plot.coarse_abscissas_[j]);
0316                 PreciseReal y = y_scale(plot.cond_[j]);
0317                 fs << " L" << t << " " << y;
0318             }
0319             fs << close_path;
0320         start_bottom_paths:
0321             jmin = jstart;
0322         new_bottom_path:
0323             if (jmin >= plot.cond_.size())
0324             {
0325                 goto done;
0326             }
0327             fs << "<path d='M" << x_scale(plot.coarse_abscissas_[jmin]) << " " << y_scale(-plot.cond_[jmin]);
0328 
0329             for (size_t j = jmin + 1; j < plot.coarse_abscissas_.size(); ++j)
0330             {
0331                 bool bad = isnan(plot.cond_[j]) || (-plot.cond_[j] < min_y);
0332                 if (bad)
0333                 {
0334                     ++j;
0335                     while ( (j < plot.coarse_abscissas_.size() - 2) && bad)
0336                     {
0337                         bad = isnan(plot.cond_[j]) || (-plot.cond_[j] < min_y);
0338                         ++j;
0339                     }
0340                     jmin = j;
0341                     fs << close_path;
0342                     goto new_bottom_path;
0343                 }
0344                 CoarseReal t = x_scale(plot.coarse_abscissas_[j]);
0345                 PreciseReal y = y_scale(-plot.cond_[j]);
0346                 fs << " L" << t << " " << y;
0347             }
0348             fs << close_path;
0349         }
0350     done:
0351         fs << "</g>\n"
0352            << "</svg>\n";
0353         return fs;
0354     }
0355 
0356 private:
0357     std::vector<PreciseReal> precise_abscissas_;
0358     std::vector<CoarseReal> coarse_abscissas_;
0359     std::vector<PreciseReal> precise_ordinates_;
0360     std::vector<PreciseReal> cond_;
0361     std::list<std::vector<CoarseReal>> ulp_list_;
0362     std::vector<std::string> colors_;
0363     CoarseReal a_;
0364     CoarseReal b_;
0365     PreciseReal clip_;
0366     int width_;
0367     std::string envelope_color_;
0368     bool ulp_envelope_;
0369     int horizontal_lines_;
0370     int vertical_lines_;
0371     std::string title_;
0372     std::string background_color_;
0373     std::string font_color_;
0374     std::string crop_color_;
0375     std::string nan_color_;
0376 };
0377 
0378 template<class F, typename PreciseReal, typename CoarseReal>
0379 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::envelope_color(std::string const & color)
0380 {
0381     envelope_color_ = color;
0382     return *this;
0383 }
0384 
0385 template<class F, typename PreciseReal, typename CoarseReal>
0386 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::clip(PreciseReal clip)
0387 {
0388     clip_ = clip;
0389     return *this;
0390 }
0391 
0392 template<class F, typename PreciseReal, typename CoarseReal>
0393 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::width(int width)
0394 {
0395     width_ = width;
0396     return *this;
0397 }
0398 
0399 template<class F, typename PreciseReal, typename CoarseReal>
0400 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::horizontal_lines(int horizontal_lines)
0401 {
0402     horizontal_lines_ = horizontal_lines;
0403     return *this;
0404 }
0405 
0406 template<class F, typename PreciseReal, typename CoarseReal>
0407 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::vertical_lines(int vertical_lines)
0408 {
0409     vertical_lines_ = vertical_lines;
0410     return *this;
0411 }
0412 
0413 template<class F, typename PreciseReal, typename CoarseReal>
0414 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::title(std::string const & title)
0415 {
0416     title_ = title;
0417     return *this;
0418 }
0419 
0420 template<class F, typename PreciseReal, typename CoarseReal>
0421 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::background_color(std::string const & background_color)
0422 {
0423     background_color_ = background_color;
0424     return *this;
0425 }
0426 
0427 template<class F, typename PreciseReal, typename CoarseReal>
0428 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::font_color(std::string const & font_color)
0429 {
0430     font_color_ = font_color;
0431     return *this;
0432 }
0433 
0434 template<class F, typename PreciseReal, typename CoarseReal>
0435 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::crop_color(std::string const & color)
0436 {
0437     crop_color_ = color;
0438     return *this;
0439 }
0440 
0441 template<class F, typename PreciseReal, typename CoarseReal>
0442 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::nan_color(std::string const & color)
0443 {
0444     nan_color_ = color;
0445     return *this;
0446 }
0447 
0448 template<class F, typename PreciseReal, typename CoarseReal>
0449 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::ulp_envelope(bool write_ulp_envelope)
0450 {
0451     ulp_envelope_ = write_ulp_envelope;
0452     return *this;
0453 }
0454 
0455 namespace detail{
0456 bool ends_with(std::string const& filename, std::string const& suffix)
0457 {
0458     if(filename.size() < suffix.size())
0459     {
0460         return false;
0461     }
0462 
0463     return std::equal(std::begin(suffix), std::end(suffix), std::end(filename) - suffix.size());
0464 }
0465 }
0466 
0467 template<class F, typename PreciseReal, typename CoarseReal>
0468 void ulps_plot<F, PreciseReal, CoarseReal>::write(std::string const & filename) const
0469 {
0470     if(!boost::math::tools::detail::ends_with(filename, ".svg"))
0471     {
0472         throw std::logic_error("Only svg files are supported at this time.");
0473     }
0474     std::ofstream fs(filename);
0475     fs << *this;
0476     fs.close();
0477 }
0478 
0479 
0480 template<class F, typename PreciseReal, typename CoarseReal>
0481 ulps_plot<F, PreciseReal, CoarseReal>::ulps_plot(F hi_acc_impl, CoarseReal a, CoarseReal b,
0482              size_t samples, bool perturb_abscissas, int random_seed) : crop_color_("red")
0483 {
0484     // Use digits10 for this comparison in case the two types have differeing radixes:
0485     static_assert(std::numeric_limits<PreciseReal>::digits10 >= std::numeric_limits<CoarseReal>::digits10, "PreciseReal must have higher precision that CoarseReal");
0486     if (samples < 10)
0487     {
0488         throw std::domain_error("Must have at least 10 samples, samples = " + std::to_string(samples));
0489     }
0490     if (b <= a)
0491     {
0492         throw std::domain_error("On interval [a,b], b > a is required.");
0493     }
0494     a_ = a;
0495     b_ = b;
0496 
0497     std::mt19937_64 gen;
0498     if (random_seed == -1)
0499     {
0500         std::random_device rd;
0501         gen.seed(rd());
0502     }
0503 
0504     // Boost's uniform_real_distribution can generate quad and multiprecision random numbers; std's cannot:
0505     #ifndef BOOST_MATH_STANDALONE
0506     boost::random::uniform_real_distribution<PreciseReal> dis(static_cast<PreciseReal>(a), static_cast<PreciseReal>(b));
0507     #else
0508     // Use std::random in standalone mode if it is a type that the standard library can support (float, double, or long double)
0509     static_assert(std::numeric_limits<PreciseReal>::digits10 <= std::numeric_limits<long double>::digits10, "Standalone mode does not support types with precision that exceeds long double");
0510     std::uniform_real_distribution<PreciseReal> dis(static_cast<PreciseReal>(a), static_cast<PreciseReal>(b));
0511     #endif
0512 
0513     precise_abscissas_.resize(samples);
0514     coarse_abscissas_.resize(samples);
0515 
0516     if (perturb_abscissas)
0517     {
0518         for(size_t i = 0; i < samples; ++i)
0519         {
0520             precise_abscissas_[i] = dis(gen);
0521         }
0522         std::sort(precise_abscissas_.begin(), precise_abscissas_.end());
0523         for (size_t i = 0; i < samples; ++i)
0524         {
0525             coarse_abscissas_[i] = static_cast<CoarseReal>(precise_abscissas_[i]);
0526         }
0527     }
0528     else
0529     {
0530         for(size_t i = 0; i < samples; ++i)
0531         {
0532             coarse_abscissas_[i] = static_cast<CoarseReal>(dis(gen));
0533         }
0534         std::sort(coarse_abscissas_.begin(), coarse_abscissas_.end());
0535         for (size_t i = 0; i < samples; ++i)
0536         {
0537             precise_abscissas_[i] = static_cast<PreciseReal>(coarse_abscissas_[i]);
0538         }
0539     }
0540 
0541     precise_ordinates_.resize(samples);
0542     for (size_t i = 0; i < samples; ++i)
0543     {
0544         precise_ordinates_[i] = hi_acc_impl(precise_abscissas_[i]);
0545     }
0546 
0547     cond_.resize(samples, std::numeric_limits<PreciseReal>::quiet_NaN());
0548     for (size_t i = 0 ; i < samples; ++i)
0549     {
0550         PreciseReal y = precise_ordinates_[i];
0551         if (y != 0)
0552         {
0553             // Maybe cond_ is badly names; should it be half_cond_?
0554             cond_[i] = boost::math::tools::evaluation_condition_number(hi_acc_impl, precise_abscissas_[i])/2;
0555             // Half-ULP accuracy is the correctly rounded result, so make sure the envelop doesn't go below this:
0556             if (cond_[i] < 0.5)
0557             {
0558                 cond_[i] = 0.5;
0559             }
0560         }
0561         // else leave it as nan.
0562     }
0563     clip_ = -1;
0564     width_ = 1100;
0565     envelope_color_ = "chartreuse";
0566     ulp_envelope_ = true;
0567     horizontal_lines_ = 8;
0568     vertical_lines_ = 10;
0569     title_ = "";
0570     background_color_ = "black";
0571     font_color_ = "white";
0572 }
0573 
0574 template<class F, typename PreciseReal, typename CoarseReal>
0575 template<class G>
0576 ulps_plot<F, PreciseReal, CoarseReal>& ulps_plot<F, PreciseReal, CoarseReal>::add_fn(G g, std::string const & color)
0577 {
0578     using std::abs;
0579     size_t samples = precise_abscissas_.size();
0580     std::vector<CoarseReal> ulps(samples);
0581     for (size_t i = 0; i < samples; ++i)
0582     {
0583         PreciseReal y_hi_acc = precise_ordinates_[i];
0584         PreciseReal y_lo_acc = static_cast<PreciseReal>(g(coarse_abscissas_[i]));
0585         PreciseReal absy = abs(y_hi_acc);
0586         PreciseReal dist = static_cast<PreciseReal>(nextafter(static_cast<CoarseReal>(absy), (std::numeric_limits<CoarseReal>::max)()) - static_cast<CoarseReal>(absy));
0587         ulps[i] = static_cast<CoarseReal>((y_lo_acc - y_hi_acc)/dist);
0588     }
0589     ulp_list_.emplace_back(ulps);
0590     colors_.emplace_back(color);
0591     return *this;
0592 }
0593 
0594 
0595 
0596 
0597 } // namespace boost::math::tools
0598 #endif