All Downloads are FREE. Search and download functionalities are using the official Maven repository.

finj.root_finding.clj Maven / Gradle / Ivy

The newest version!
;
; Copyright © 2013 Sebastian Hoß 
; This work is free. You can redistribute it and/or modify it under the
; terms of the Do What The Fuck You Want To Public License, Version 2,
; as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
;

(ns finj.root-finding
  "Root finding algorithms"
  (:require [com.github.sebhoss.def :refer :all]
            [com.github.sebhoss.math :refer :all]))

(defrecord SearchResult [estimate estimation-error number-of-iterations first-value second-value])

(defnk iter-root-search
  "Iterative search for a root using two values as inputs."
  [:function :first-value :second-value :next-estimate :next-first :next-second
   :opt-def :tolerance 0.00001
            :max-iterations 100]
  {:pre [(pos? tolerance)
         (pos? max-iterations)]
   :post [(< (abs (:estimation-error %)) tolerance)
          (pos? (:number-of-iterations %))]}
  (loop [first-value first-value
         second-value second-value
         iteration 1]
    (if (<= iteration max-iterations)
      (let [estimate (next-estimate first-value second-value)
            estimation-error (function estimate)
            result (SearchResult. estimate estimation-error iteration first-value second-value)]
        (if (< (abs estimation-error) tolerance)
          result
          (recur (next-first result) (next-second result) (inc iteration))))
      (throw (IllegalArgumentException. "max number of iterations exceeded - could not satisfy tolerance")))))

(defnk bisect
  "Root-finding method which repeatedly bisects an interval and then selects a subinterval in which a root must lie for
   further processing. It is a very simple and robust method, but it is also relatively slow. Because of this, it is
   often used to obtain a rough approximation to a solution which is then used as a starting point for more rapidly
   converging methods. The method is also called the binary search method or the dichotomy method.

   Parameters:
     * function         - The function to apply to each possible root.
     * lower-startpoint - The lower bound of the initial search-interval.
     * upper-startpoint - The upper bound of the initial search-interval.
     * tolerance        - The maximum tolerance allowed for a possible root to become a root.
     * max-iterations   - The maximum numbers of iterations to run before throwing an exception.

   References:
     * http://en.wikipedia.org/wiki/Bisection_method"
  [:function :lower-startpoint :upper-startpoint
   :opt-def :tolerance 0.00001
            :max-iterations 100]
  {:pre [(< lower-startpoint upper-startpoint)
         (sgn-different? (function lower-startpoint) (function upper-startpoint))]
   :post [(< (:first-value %) (:second-value %))]}
  (iter-root-search
    :function function
    :first-value lower-startpoint
    :second-value upper-startpoint
    :tolerance tolerance
    :max-iterations max-iterations
    :next-estimate #(mean %1 %2)
    :next-first (fn [interim]
                  (if (pos? (:estimation-error interim))
                    (:first-value interim)
                    (:estimate interim)))
    :next-second (fn [interim]
                   (if (pos? (:estimation-error interim))
                     (:estimate interim)
                     (:second-value interim)))))

(defnk secant
  "The secant method is a root-finding algorithm that uses a succession of roots of secant lines to better approximate
   a root of a function f.

   References:
     * http://en.wikipedia.org/wiki/Secant_method"
  [:function :first :second
   :opt-def :tolerance 0.00001
            :max-iterations 100]
  (iter-root-search
    :function function
    :first-value first
    :second-value second
    :tolerance tolerance
    :max-iterations max-iterations
    :next-estimate (fn [first-value second-value]
                     (let [fa (function first-value)
                           fb (function second-value)]
                       (- first-value (* (/ (- first-value second-value)
                                            (- fa fb))
                                         fa))))
    :next-first (fn [interim]
                  (:estimate interim))
    :next-second (fn [interim]
                   (:first-value interim))))

(defnk newton
  "The newton method is a method for finding successively better approximations to the roots (or zeroes) of a
   real-valued function.

   References:
     * http://en.wikipedia.org/wiki/Newton%27s_method"
  [:function :derivative :min-denominator :start-value
   :opt-def :tolerance 0.00001
            :max-iterations 100]
  (iter-root-search 
    :function function
    :first-value start-value
    :second-value nil
    :tolerance tolerance
    :max-iterations max-iterations
    :next-estimate (fn [first-value second-value]
                     (let [denominator (derivative first-value)]
                       (if (< (abs denominator) min-denominator)
                         (throw (IllegalArgumentException. "denominator too small"))
                         (- first-value (/ (function first-value)
                                           denominator)))))
    :next-first (fn [interim] (:estimate interim))
    :next-second (fn [_] nil)))

(defnk regula-falsi
  "The false position method or regula falsi method is a term for problem-solving methods in arithmetic, algebra, and
   calculus.

   References:
     * http://en.wikipedia.org/wiki/False_position_method"
  [:function :lower-startpoint :upper-startpoint
   :opt-def :tolerance 0.00001
            :max-iterations 100]
  {:pre [(< lower-startpoint upper-startpoint)
         (sgn-different? (function lower-startpoint) (function upper-startpoint))]
   :post [(< (:first-value %) (:second-value %))]}
  (iter-root-search
    :function function
    :first-value lower-startpoint
    :second-value upper-startpoint
    :tolerance tolerance
    :max-iterations max-iterations
    :next-estimate (fn [first-value second-value]
                     (let [fa (function first-value)
                           fb (function second-value)]
                       (- first-value (/ (* fa (- second-value first-value))
                              (- fb fa)))))
    :next-first (fn [interim]
                  (if (pos? (:estimation-error interim))
                    (:first-value interim)
                    (:estimate interim)))
    :next-second (fn [interim]
                   (if (pos? (:estimation-error interim))
                     (:estimate interim)
                     (:second-value interim)))))




© 2015 - 2025 Weber Informatics LLC | Privacy Policy