summaryrefslogtreecommitdiff
path: root/ext/bigdecimal/lib/bigdecimal/jacobian.rb
blob: 34a60ae67ab059a8405b831750262ac679f84e2a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#
# jacobian.rb
#
# Computes Jacobian matrix of f at x
#
module Jacobian
  def isEqual(a,b,zero=0.0,e=1.0e-8)
    aa = a.abs
    bb = b.abs
    if aa == zero &&  bb == zero then
          true
    else
          if ((a-b)/(aa+bb)).abs < e then
             true
          else
             false
          end
    end
  end

  def dfdxi(f,fx,x,i)
    nRetry = 0
    n = x.size
    xSave = x[i]
    ok = 0
    ratio = f.ten*f.ten*f.ten
    dx = x[i].abs/ratio
    dx = fx[i].abs/ratio if isEqual(dx,f.zero,f.zero,f.eps)
    dx = f.one/f.ten     if isEqual(dx,f.zero,f.zero,f.eps)
    until ok>0 do
      s = f.zero
      deriv = []
      if(nRetry>100) then
         raize "Singular Jacobian matrix. No change at x[" + i.to_s + "]"
      end
      dx = dx*f.two
      x[i] += dx
      fxNew = f.values(x)
      for j in 0...n do
        if !isEqual(fxNew[j],fx[j],f.zero,f.eps) then
           ok += 1
           deriv <<= (fxNew[j]-fx[j])/dx
        else
           deriv <<= f.zero
        end
      end
      x[i] = xSave
    end
    deriv
  end

  def jacobian(f,fx,x)
    n = x.size
    dfdx = Array::new(n*n)
    for i in 0...n do
      df = dfdxi(f,fx,x,i)
      for j in 0...n do
         dfdx[j*n+i] = df[j]
      end
    end
    dfdx
  end
end