Files
OpenSTA/dcalc/FindRoot.cc
T

102 lines
2.6 KiB
C++
Raw Normal View History

2024-01-21 10:19:52 -07:00
// OpenSTA, Static Timing Analyzer
2026-03-10 13:21:17 -07:00
// Copyright (c) 2026, Parallax Software, Inc.
2024-01-21 10:19:52 -07:00
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
2025-01-21 18:54:33 -07:00
//
// The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software.
//
// Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// This notice may not be removed or altered from any source distribution.
2024-01-21 10:19:52 -07:00
#include "FindRoot.hh"
#include <cmath> // abs
2024-01-27 16:12:53 -07:00
2024-01-21 10:19:52 -07:00
namespace sta {
2026-04-03 16:46:05 -07:00
std::pair<double, bool>
2026-04-15 09:38:10 -07:00
findRoot(const FindRootFunc &func,
2026-01-03 16:59:35 -08:00
double x1,
double x2,
double x_tol,
2026-04-03 16:46:05 -07:00
int max_iter)
2024-01-21 10:19:52 -07:00
{
double y1, y2, dy1;
func(x1, y1, dy1);
func(x2, y2, dy1);
2026-04-03 16:46:05 -07:00
return findRoot(func, x1, y1, x2, y2, x_tol, max_iter);
2024-01-21 10:19:52 -07:00
}
2026-04-03 16:46:05 -07:00
std::pair<double, bool>
2026-04-15 09:38:10 -07:00
findRoot(const FindRootFunc &func,
2026-01-03 16:59:35 -08:00
double x1,
double y1,
2024-01-21 10:19:52 -07:00
double x2,
2026-01-03 16:59:35 -08:00
double y2,
2024-01-21 10:19:52 -07:00
double x_tol,
2026-04-03 16:46:05 -07:00
int max_iter)
2024-01-21 10:19:52 -07:00
{
if ((y1 > 0.0 && y2 > 0.0) || (y1 < 0.0 && y2 < 0.0)) {
// Initial bounds do not surround a root.
2026-04-03 16:46:05 -07:00
return {0.0, true};
2024-01-21 10:19:52 -07:00
}
2026-04-03 16:46:05 -07:00
if (y1 == 0.0)
return {x1, false};
2024-01-21 10:19:52 -07:00
2026-04-03 16:46:05 -07:00
if (y2 == 0.0)
return {x2, false};
2024-01-21 10:19:52 -07:00
if (y1 > 0.0)
// Swap x1/x2 so func(x1) < 0.
std::swap(x1, x2);
double root = (x1 + x2) * 0.5;
2026-03-02 12:13:13 -08:00
double dx_prev = std::abs(x2 - x1);
2024-01-21 10:19:52 -07:00
double dx = dx_prev;
double y, dy;
func(root, y, dy);
for (int iter = 0; iter < max_iter; iter++) {
// Newton/raphson out of range.
if ((((root - x2) * dy - y) * ((root - x1) * dy - y) > 0.0)
2026-01-03 16:59:35 -08:00
// Not decreasing fast enough.
2026-03-02 12:13:13 -08:00
|| (std::abs(2.0 * y) > std::abs(dx_prev * dy))) {
2024-01-21 10:19:52 -07:00
// Bisect x1/x2 interval.
dx_prev = dx;
dx = (x2 - x1) * 0.5;
root = x1 + dx;
}
else {
dx_prev = dx;
dx = y / dy;
root -= dx;
}
2026-03-02 12:13:13 -08:00
if (std::abs(dx) <= x_tol * std::abs(root)) {
2024-01-21 10:19:52 -07:00
// Converged.
2026-04-03 16:46:05 -07:00
return {root, false};
2024-01-21 10:19:52 -07:00
}
func(root, y, dy);
if (y < 0.0)
x1 = root;
else
x2 = root;
}
2026-04-03 16:46:05 -07:00
return {root, true};
2024-01-21 10:19:52 -07:00
}
2026-04-13 14:58:16 -07:00
} // namespace sta