package org.matheclipse.core.reflection.system;

import org.apache.commons.math3.analysis.UnivariateFunction;
import org.apache.commons.math3.analysis.solvers.BaseAbstractUnivariateSolver;
import org.apache.commons.math3.analysis.solvers.BisectionSolver;
import org.apache.commons.math3.analysis.solvers.BrentSolver;
import org.apache.commons.math3.analysis.solvers.IllinoisSolver;
import org.apache.commons.math3.analysis.solvers.MullerSolver;
import org.apache.commons.math3.analysis.solvers.PegasusSolver;
import org.apache.commons.math3.analysis.solvers.RegulaFalsiSolver;
import org.apache.commons.math3.analysis.solvers.RiddersSolver;
import org.apache.commons.math3.analysis.solvers.SecantSolver;
import org.matheclipse.commons.math.analysis.solvers.DifferentiableUnivariateFunction;
import org.matheclipse.commons.math.analysis.solvers.NewtonSolver;
import org.matheclipse.core.basic.Config;
import org.matheclipse.core.eval.EvalEngine;
import org.matheclipse.core.eval.exception.Validate;
import org.matheclipse.core.eval.interfaces.AbstractFunctionEvaluator;
import org.matheclipse.core.eval.util.Options;
import org.matheclipse.core.expression.F;
import org.matheclipse.core.expression.Num;
import org.matheclipse.core.generic.UnaryNumerical;
import org.matheclipse.core.interfaces.IAST;
import org.matheclipse.core.interfaces.IExpr;
import org.matheclipse.core.interfaces.ISignedNumber;
import org.matheclipse.core.interfaces.ISymbol;

/**
 * Function for numerically finding roots of a univariate real
 * function.
 * 
 * Uses the Commons math BisectionSolver, BrentSolver, MullerSolver, NewtonSolver, RiddersSolver, SecantSolver implementations.
 */
public class FindRoot extends AbstractFunctionEvaluator {

	public final static ISymbol Newton = F.initFinalSymbol(Config.PARSER_USE_LOWERCASE_SYMBOLS ? "newton" : "Newton");

	public FindRoot() {
	}

	@Override
	public IExpr evaluate(final IAST ast) {
		Validate.checkRange(ast, 3);

		ISymbol method = Newton;
		int maxIterations = 100;
		if (ast.size() >= 4) {
			final Options options = new Options(ast.topHead(), ast, 3);
			IExpr optionMaxIterations = options.getOption("MaxIterations");
			if (optionMaxIterations != null && optionMaxIterations.isSignedNumber()) {
				maxIterations = ((ISignedNumber) optionMaxIterations).toInt();
			}
			IExpr optionMethod = options.getOption("Method");
			if (optionMethod != null && optionMethod.isSymbol()) {
				method = ((ISymbol) optionMethod);
			} else {
				if (ast.get(3).isSymbol()) {
					method = (ISymbol) ast.arg3();
				}
			}
		}
		if ((ast.arg2().isList())) {
			IAST list = (IAST) ast.arg2();
			IExpr function = ast.arg1();
			if (list.size() >= 3 && list.arg1().isSymbol() && list.arg2().isSignedNumber()) {
				if (function.isAST(F.Equal, 3)) {
					function = F.Plus(((IAST) function).arg1(), F.Times(F.CN1, ((IAST) function).arg2()));
				}
				return F.List(F.Rule(list.arg1(), Num.valueOf(findRoot(method, maxIterations, list, function))));
			}
		}
		return null;
	}

	private double findRoot(ISymbol method, int maxIterations, IAST list, IExpr function) {
		ISymbol xVar = (ISymbol) list.arg1();
		ISignedNumber min = (ISignedNumber) list.arg2();
		ISignedNumber max = null;
		if (list.size() > 3 && list.get(3) instanceof ISignedNumber) {
			max = (ISignedNumber) list.get(3);
		}
		final EvalEngine engine = EvalEngine.get();
		function = F.eval(function);
		UnivariateFunction f = new UnaryNumerical(function, xVar, engine);
		BaseAbstractUnivariateSolver solver = null;
		if (method.isSymbolName("Bisection")) {
			solver = new BisectionSolver();
		} else if (method.isSymbolName("Brent")) {
			solver = new BrentSolver();
			// } else if (method.isSymbolName("Laguerre")) {
			// solver = new LaguerreSolver();
		} else if (method.isSymbolName("Muller")) {
			solver = new MullerSolver();
		} else if (method.isSymbolName("Ridders")) {
			solver = new RiddersSolver();
		} else if (method.isSymbolName("Secant")) {
			solver = new SecantSolver();
		} else if (method.isSymbolName("RegulaFalsi")) {
			solver = new RegulaFalsiSolver();
		} else if (method.isSymbolName("Illinois")) {
			solver = new IllinoisSolver();
		} else if (method.isSymbolName("Pegasus")) {
			solver = new PegasusSolver();
		} else { 
			// default: NewtonSolver
			DifferentiableUnivariateFunction fNewton = new UnaryNumerical(function, xVar, engine);
			BaseAbstractUnivariateSolver solver2 = new NewtonSolver();
			if (max == null) {
				return solver2.solve(maxIterations, fNewton, min.doubleValue());
			}
			return solver2.solve(maxIterations, fNewton, min.doubleValue(), max.doubleValue());
		}
		if (max == null) {
			return solver.solve(maxIterations, f, min.doubleValue());
		}
		return solver.solve(maxIterations, f, min.doubleValue(), max.doubleValue());

	}

	@Override
	public void setUp(final ISymbol symbol) {
		symbol.setAttributes(ISymbol.HOLDFIRST);
	}
}
 

Ads help maintain this website.