appmath 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/kepler_2d_app.rb +130 -0
- data/bin/linalg_app.rb +193 -0
- data/bin/rnum_app.rb +199 -0
- data/gpl-3.0.txt +674 -0
- data/lib/appmath_basics.rb +118 -0
- data/lib/cnum.rb +615 -0
- data/lib/float_ext.rb +223 -0
- data/lib/graph.rb +415 -0
- data/lib/interval.rb +282 -0
- data/lib/kepler_2d.rb +162 -0
- data/lib/linalg.rb +1309 -0
- data/lib/random.rb +88 -0
- data/lib/rnum.rb +1648 -0
- data/readme.txt +126 -0
- metadata +72 -0
data/lib/interval.rb
ADDED
@@ -0,0 +1,282 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
ruby
|
3
|
+
|
4
|
+
Ulrich Mutze, www.ulrichutze.de
|
5
|
+
|
6
|
+
Started 2008-11-01 by modifying cpminterval.h and cpminterval.cpp.
|
7
|
+
|
8
|
+
Defines class AppMath::Iv.
|
9
|
+
|
10
|
+
Requires file appmath_basics.
|
11
|
+
|
12
|
+
Copyright (C) 2008 Ulrich Mutze
|
13
|
+
|
14
|
+
This program is free software: you can redistribute it and/or modify
|
15
|
+
it under the terms of the GNU General Public License as published by
|
16
|
+
the Free Software Foundation, either version 3 of the License, or
|
17
|
+
(at your option) any later version.
|
18
|
+
|
19
|
+
This program is distributed in the hope that it will be useful,
|
20
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
21
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
22
|
+
GNU General Public License for more details.
|
23
|
+
|
24
|
+
You should have received a copy of the GNU General Public License
|
25
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
26
|
+
=end
|
27
|
+
|
28
|
+
require File.join(File.dirname(__FILE__), 'appmath_basics')
|
29
|
+
#require 'appmath_basics.rb'
|
30
|
+
|
31
|
+
module AppMath
|
32
|
+
|
33
|
+
=begin rdoc
|
34
|
+
Class of real closed intervals.
|
35
|
+
In most applications of intervals it is an advantage if single real
|
36
|
+
values can be considered as intervals of length zero. This is possible
|
37
|
+
only for closed intervals, and this is the rational for defining those
|
38
|
+
in preference to open intervals.
|
39
|
+
With the attributes @low and @upp, the values x which belong to interval
|
40
|
+
are characterized by the formula
|
41
|
+
@low <= x <= @upp .
|
42
|
+
Hence the interval is empty (void) iff @low > @upp.
|
43
|
+
=end
|
44
|
+
|
45
|
+
class Iv
|
46
|
+
|
47
|
+
attr_reader :low, :upp
|
48
|
+
|
49
|
+
# Allowed are 0, 1, 2 arguments, which all have to be convertible to R.
|
50
|
+
# For no argument the empty interval is created. For one argument, an
|
51
|
+
# second argument 0 is understood. For two arguments, these are the
|
52
|
+
# boundaries of the interval and the order on input does not matter:
|
53
|
+
# iv1 = Iv.new(2,3); iv2 = Iv.new(3,2)
|
54
|
+
# are thus the same, non-empty, interval, which in mathematical notation
|
55
|
+
# would be
|
56
|
+
# [2,3]
|
57
|
+
# Finally
|
58
|
+
# iv3 = Iv.new
|
59
|
+
# is an empty interval.
|
60
|
+
def initialize(*arg)
|
61
|
+
n = arg.size
|
62
|
+
case n
|
63
|
+
when 0
|
64
|
+
@low = R.c1
|
65
|
+
@upp = R.c0 # notice: epmpty since @low > @upp
|
66
|
+
when 1
|
67
|
+
x = R.c arg[0]
|
68
|
+
zero = R.c0
|
69
|
+
if x < zero
|
70
|
+
@low = x
|
71
|
+
@upp = zero
|
72
|
+
else
|
73
|
+
@upp = x
|
74
|
+
@low = zero
|
75
|
+
end
|
76
|
+
when 2
|
77
|
+
x = R.c arg[0]
|
78
|
+
y = R.c arg[1]
|
79
|
+
if x < y
|
80
|
+
@low = x
|
81
|
+
@upp = y
|
82
|
+
else
|
83
|
+
@low = y
|
84
|
+
@upp = x
|
85
|
+
end
|
86
|
+
else
|
87
|
+
fail "Iv.new takes 0 or 1 or 2 arguments, but not " + n.to_s
|
88
|
+
end # case n
|
89
|
+
end
|
90
|
+
|
91
|
+
=begin rdoc
|
92
|
+
using max and min of an array for defining an interval.
|
93
|
+
The values of the array components are then known to belong to
|
94
|
+
to a closed interval. The smallest such interval is what
|
95
|
+
his method returns.
|
96
|
+
=end
|
97
|
+
def Iv.from_array(anArray)
|
98
|
+
n = anArray.size
|
99
|
+
case n
|
100
|
+
when 0
|
101
|
+
Iv.new
|
102
|
+
when 1
|
103
|
+
a0 = anArray[0]
|
104
|
+
Iv.new(a0)
|
105
|
+
else
|
106
|
+
x0 = anArray[0]
|
107
|
+
x1 = x0
|
108
|
+
anArray.each{ |x|
|
109
|
+
if x < x0
|
110
|
+
x0 = x
|
111
|
+
elsif x > x1
|
112
|
+
x1 = x
|
113
|
+
else
|
114
|
+
end
|
115
|
+
}
|
116
|
+
Iv.new(x0,x1)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns an ordered equidistant array of n numbers, where the first one
|
121
|
+
# is @low and the last one is @upp. The positive integer n is made from
|
122
|
+
# the argument a in a way which depends on whether this argument is integer
|
123
|
+
# or not. In the first case it is taken as n and in the second case, the
|
124
|
+
# argment is taken as a proposal for the lattice spacing. The actual
|
125
|
+
# lattice spacing will be chosen as a fraction of size.
|
126
|
+
def to_array(a)
|
127
|
+
return nil if empty?
|
128
|
+
if a.integer? # then we interpret the argument as an intended number
|
129
|
+
# of lattice points
|
130
|
+
n = a
|
131
|
+
else # then we interprete the argument as an intended lattice spacing
|
132
|
+
d = a.abs
|
133
|
+
fail "can't build a lattice with spacing 0" if d.zero?
|
134
|
+
n = (size/d).round + 1
|
135
|
+
end
|
136
|
+
fail "number of lattice points must be larger than 1" if n < 2
|
137
|
+
res = Array.new
|
138
|
+
res << @low
|
139
|
+
if n > 2
|
140
|
+
na = n - 2
|
141
|
+
d = size / (n-1)
|
142
|
+
x = @low
|
143
|
+
for i in 1..na
|
144
|
+
x += d
|
145
|
+
res << x
|
146
|
+
end
|
147
|
+
end
|
148
|
+
res << @upp
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns true iff the interval is empty
|
152
|
+
def empty?; @low > @upp; end
|
153
|
+
|
154
|
+
# Returns the lower boundary if self is not empty
|
155
|
+
# and nil else.
|
156
|
+
def inf
|
157
|
+
return nil if empty?
|
158
|
+
@low
|
159
|
+
end
|
160
|
+
|
161
|
+
# Returns the upper boundary if self is not empty
|
162
|
+
# and nil else.
|
163
|
+
def sup
|
164
|
+
return nil if empty?
|
165
|
+
@upp
|
166
|
+
end
|
167
|
+
|
168
|
+
# Returns the midpoint of the interval.
|
169
|
+
def center
|
170
|
+
nil if empty?
|
171
|
+
(@low + @upp) * R.i2
|
172
|
+
end
|
173
|
+
|
174
|
+
# Returns the length of the interval, 0 for the empty one
|
175
|
+
def size
|
176
|
+
s = @upp - @low
|
177
|
+
s >= R.c0 ? s : R.c0
|
178
|
+
end
|
179
|
+
|
180
|
+
# Indicator function. Returns true if the point x belongs to self
|
181
|
+
# and false else.
|
182
|
+
def ind(x)
|
183
|
+
return false if empty?
|
184
|
+
return false if x > @upp
|
185
|
+
return false if x < @low
|
186
|
+
true
|
187
|
+
end
|
188
|
+
|
189
|
+
# Metrical indicator function. Returns the distance of x from the set
|
190
|
+
# self if x is outside of self. If it is inside, the return value is
|
191
|
+
# minus the distance to the complement of self (which is not an
|
192
|
+
# interval but a well-defined set).
|
193
|
+
def met_ind(x)
|
194
|
+
return nil if empty?
|
195
|
+
y = R.c x
|
196
|
+
dc = (y - center).abs
|
197
|
+
dc - size * R.i2
|
198
|
+
end
|
199
|
+
|
200
|
+
# Returns a real number, which is self's lower end for p == 0.0, self's
|
201
|
+
# center for p == 0.5, and self's upper end for p == 1.0 .
|
202
|
+
def put(p)
|
203
|
+
inf + size * p
|
204
|
+
end
|
205
|
+
|
206
|
+
# minimum closed interval that contains the union
|
207
|
+
# join or l.u.b. (lowest upper bound) in lattice terminology
|
208
|
+
def |(anIv)
|
209
|
+
return anIv if empty?
|
210
|
+
return self if anIv.empty?
|
211
|
+
Iv.from_array [@low, @upp, anIv.low, anIv.upp]
|
212
|
+
end
|
213
|
+
|
214
|
+
#shifting by a
|
215
|
+
def +(a); Iv.new(@low + a,@upp + a); end
|
216
|
+
|
217
|
+
# multiplying size by a, while preserving the center
|
218
|
+
def *(a)
|
219
|
+
Iv.new(center-(center-@low)*a,center+(@upp-center)*a)
|
220
|
+
end
|
221
|
+
|
222
|
+
# section, intersection
|
223
|
+
# meet or g.l.b (greatest lower bound) in lattice terminology
|
224
|
+
def &(anIv)
|
225
|
+
return Iv.new if empty? || anIv.empty?
|
226
|
+
return Iv.new if @upp < anIv.low || @low > anIv.upp
|
227
|
+
amin = Basics.sup(@low, anIv.low)
|
228
|
+
amax = Basics.inf(@upp, anIv.upp)
|
229
|
+
Iv.new(amin, amax)
|
230
|
+
end
|
231
|
+
|
232
|
+
def to_s
|
233
|
+
"Iv (" + @low.to_s + "," + @upp.to_s + ")"
|
234
|
+
end
|
235
|
+
|
236
|
+
# The function creates a suitable axis sub-division for data ranging
|
237
|
+
# from @low to @upp. Let res be the return value of the function. Then
|
238
|
+
# res[0] is a proposal for the difference between adjacent axis tics
|
239
|
+
# and res[1] is an array of the values to which the proposed tics belong.
|
240
|
+
# Thus res[1].first <= @low and res[1].last >= @upp. All numbers are chosen
|
241
|
+
# such that they are simple when written down in normal scientific
|
242
|
+
# notation and the intention is to simulate the considerations that
|
243
|
+
# determine the axis subdivision of reasonable manually created diagrams.
|
244
|
+
# The argument of the function is a proposal for the number of tics to be
|
245
|
+
# used. Values from 5 to 10 are reasonable. To have a simple logic, we
|
246
|
+
# simply enforce that the interval between tics is a simple number.
|
247
|
+
# The initial and the final number of the axis division is chosen as
|
248
|
+
# an integer multiple of this inter-tic interval.
|
249
|
+
|
250
|
+
def axis_division(anPosInteger)
|
251
|
+
fail "can't divide an empty interval" if empty?
|
252
|
+
a = @low
|
253
|
+
b = @upp
|
254
|
+
n = anPosInteger.abs
|
255
|
+
n += 1 if n == 0
|
256
|
+
d = size/n
|
257
|
+
d_ = Basics.cut(d) # this is the essential point
|
258
|
+
fail "Zero division in function axis_division" if d_ == 0.0
|
259
|
+
d_inv=d_.inv;
|
260
|
+
epsilon = R.c 1e-6
|
261
|
+
k_b=(b*d_inv - epsilon).ceil
|
262
|
+
# without the epsilon correction it depends on roundoff
|
263
|
+
# errors whether b_ becomes too large
|
264
|
+
k_a=(a*d_inv + epsilon).floor
|
265
|
+
# without the epsilon correction it depends on roundoff
|
266
|
+
# errors whether a_ becomes too small
|
267
|
+
b_=k_b * d_
|
268
|
+
b__ = (k_b - 0.5) * d_
|
269
|
+
a_=k_a * d_
|
270
|
+
res=Array.new
|
271
|
+
while a_ < b__
|
272
|
+
res << a_
|
273
|
+
a_ += d_
|
274
|
+
end
|
275
|
+
res << b_ # we know the last item exactly, and should
|
276
|
+
# not spoil it by arithmetic errors
|
277
|
+
[ d_ , res ]
|
278
|
+
end
|
279
|
+
|
280
|
+
end # class Iv
|
281
|
+
|
282
|
+
end # AppMath
|
data/lib/kepler_2d.rb
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
ruby
|
3
|
+
|
4
|
+
Ulrich Mutze www.ulrichmutze.de
|
5
|
+
|
6
|
+
2008-12-07
|
7
|
+
|
8
|
+
Defines classes AppMath::R2 and AppMath::Kep2D.
|
9
|
+
|
10
|
+
Requires file appmath_basics.
|
11
|
+
|
12
|
+
Copyright (C) 2008 Ulrich Mutze
|
13
|
+
|
14
|
+
This program is free software: you can redistribute it and/or modify
|
15
|
+
it under the terms of the GNU General Public License as published by
|
16
|
+
the Free Software Foundation, either version 3 of the License, or
|
17
|
+
(at your option) any later version.
|
18
|
+
|
19
|
+
This program is distributed in the hope that it will be useful,
|
20
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
21
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
22
|
+
GNU General Public License for more details.
|
23
|
+
|
24
|
+
You should have received a copy of the GNU General Public License
|
25
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
26
|
+
=end
|
27
|
+
|
28
|
+
require File.join(File.dirname(__FILE__), 'appmath_basics')
|
29
|
+
#require 'appmath_basics.rb'
|
30
|
+
|
31
|
+
module AppMath
|
32
|
+
|
33
|
+
###################### classes R2 and Kep2D ##############################
|
34
|
+
|
35
|
+
# Real vector space of two dimensions
|
36
|
+
class R2
|
37
|
+
|
38
|
+
attr :x, true
|
39
|
+
attr :y, true
|
40
|
+
|
41
|
+
def initialize(x,y)
|
42
|
+
@x = x
|
43
|
+
@y = y
|
44
|
+
end
|
45
|
+
|
46
|
+
def clone
|
47
|
+
R.new(x,y)
|
48
|
+
end
|
49
|
+
|
50
|
+
def +(p)
|
51
|
+
R2.new(x + p.x, y + p.y)
|
52
|
+
end
|
53
|
+
|
54
|
+
def -(p)
|
55
|
+
R2.new(x - p.x, y - p.y)
|
56
|
+
end
|
57
|
+
|
58
|
+
def *(s)
|
59
|
+
R2.new(x * s, y * s)
|
60
|
+
end
|
61
|
+
|
62
|
+
def -@
|
63
|
+
R2.new(-x, -y)
|
64
|
+
end
|
65
|
+
|
66
|
+
def to_s
|
67
|
+
res = "R2(" + x.to_s + ", " + y.to_s + ")"
|
68
|
+
end
|
69
|
+
|
70
|
+
def abs
|
71
|
+
x.hypot(y)
|
72
|
+
end
|
73
|
+
|
74
|
+
# abs squared
|
75
|
+
def abs2
|
76
|
+
x * x + y * y
|
77
|
+
end
|
78
|
+
|
79
|
+
# scalar product
|
80
|
+
def spr(p)
|
81
|
+
x * p.x + y * p.y
|
82
|
+
end
|
83
|
+
|
84
|
+
# unit vector
|
85
|
+
def uv
|
86
|
+
r = abs
|
87
|
+
if r.zero?
|
88
|
+
clone
|
89
|
+
else
|
90
|
+
ri = r.inv
|
91
|
+
self * ri
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
# Kepler problem in 2 dimensions
|
98
|
+
# mass of the test particle is 1.
|
99
|
+
# space-fixed central mass times constant of gravity is @g
|
100
|
+
class Kep2D
|
101
|
+
|
102
|
+
def initialize(x,v,g)
|
103
|
+
@t = R.c0
|
104
|
+
@x = x
|
105
|
+
@v = v
|
106
|
+
@g = g
|
107
|
+
end
|
108
|
+
|
109
|
+
# acceleration
|
110
|
+
def acc
|
111
|
+
r = @x.abs
|
112
|
+
k = -@g * r**-3
|
113
|
+
@x * k
|
114
|
+
end
|
115
|
+
|
116
|
+
# time step of the direct midpoint integrator, highly symmetric !
|
117
|
+
def step!(dt)
|
118
|
+
h = dt * 0.5
|
119
|
+
@t += h
|
120
|
+
@x += @v * h
|
121
|
+
@v += acc * dt
|
122
|
+
@x += @v * h
|
123
|
+
@t += h
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
res = "x = " + @x.to_s + "\n" +
|
128
|
+
"v = " + @v.to_s + "\n" +
|
129
|
+
"t = " + @t.to_s
|
130
|
+
end
|
131
|
+
|
132
|
+
def get_x
|
133
|
+
@x.x
|
134
|
+
end
|
135
|
+
|
136
|
+
def get_y
|
137
|
+
@x.y
|
138
|
+
end
|
139
|
+
|
140
|
+
def get_t
|
141
|
+
@t
|
142
|
+
end
|
143
|
+
|
144
|
+
# total energy
|
145
|
+
def energy
|
146
|
+
@v.abs2 * 0.5 - @g/@x.abs
|
147
|
+
end
|
148
|
+
|
149
|
+
# angular momentum
|
150
|
+
def ang_mom
|
151
|
+
@x.x * @v.y - @x.y * @v.x
|
152
|
+
end
|
153
|
+
|
154
|
+
# Runge-Lenz vector
|
155
|
+
def lenz
|
156
|
+
@x * @v.abs2 - @x * @v.spr(@x) - @x.uv * @g
|
157
|
+
end
|
158
|
+
|
159
|
+
end # class Kep2D
|
160
|
+
|
161
|
+
end # AppMath
|
162
|
+
|
data/lib/linalg.rb
ADDED
@@ -0,0 +1,1309 @@
|
|
1
|
+
=begin rdoc
|
2
|
+
ruby
|
3
|
+
|
4
|
+
Ulrich Mutze, www.urichmutze.de
|
5
|
+
|
6
|
+
Linear algebra of vectors and matrices
|
7
|
+
|
8
|
+
Defines classes AppMath::Vec and AppMath::Mat.
|
9
|
+
|
10
|
+
Requires files appmath_basics and random.
|
11
|
+
|
12
|
+
2008-12-03
|
13
|
+
|
14
|
+
Copyright (C) 2008 Ulrich Mutze
|
15
|
+
|
16
|
+
This program is free software: you can redistribute it and/or modify
|
17
|
+
it under the terms of the GNU General Public License as published by
|
18
|
+
the Free Software Foundation, either version 3 of the License, or
|
19
|
+
(at your option) any later version.
|
20
|
+
|
21
|
+
This program is distributed in the hope that it will be useful,
|
22
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
23
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
24
|
+
GNU General Public License for more details.
|
25
|
+
|
26
|
+
You should have received a copy of the GNU General Public License
|
27
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
28
|
+
=end
|
29
|
+
|
30
|
+
require File.join(File.dirname(__FILE__), 'appmath_basics')
|
31
|
+
require File.join(File.dirname(__FILE__), 'random')
|
32
|
+
#require 'appmath_basics'
|
33
|
+
#require 'random'
|
34
|
+
|
35
|
+
module AppMath
|
36
|
+
|
37
|
+
# Vector space of arbitrary dimension.
|
38
|
+
# The intended usage is that the components
|
39
|
+
# of a vector are all either real or complex.
|
40
|
+
# Since
|
41
|
+
# x = Vec.new(anyArray); x[1] = anyObject
|
42
|
+
# works, there is no guaranty for type-uniformity
|
43
|
+
# of the components of a vector.
|
44
|
+
|
45
|
+
class Vec
|
46
|
+
include Enumerable
|
47
|
+
include Comparable
|
48
|
+
|
49
|
+
attr :x, true
|
50
|
+
|
51
|
+
# Returns the 'dimension' of the vector, i.e. the number of its
|
52
|
+
# components.
|
53
|
+
def dim; @x.size; end
|
54
|
+
|
55
|
+
# These are the 3 mehods to generate a vector via 'new'
|
56
|
+
# a = Vec.new(anArray)
|
57
|
+
# b = Vec.new(aVec)
|
58
|
+
# c = Vec.new(aPositiveInteger, aRealOrComplex)
|
59
|
+
def initialize(*arg)
|
60
|
+
case arg.size
|
61
|
+
when 1
|
62
|
+
a0 = arg[0]
|
63
|
+
if a0.is_a?(Array)
|
64
|
+
@x = Array.new(a0)
|
65
|
+
# @x = a0 # seems to work but can't be safe
|
66
|
+
elsif a0.is_a?(Vec)
|
67
|
+
@x = Array.new(a0.x)
|
68
|
+
else
|
69
|
+
fail "object can't be used to build a vector"
|
70
|
+
end
|
71
|
+
when 2
|
72
|
+
n = arg[0]
|
73
|
+
fail "first argument has to be an integer" unless n.integer?
|
74
|
+
fail "first argument must be non-negative" unless n >= 0
|
75
|
+
@x = Array.new(n,arg[1])
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns an independent copy of self.
|
80
|
+
def clone
|
81
|
+
Vec.new(self)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Test object.
|
85
|
+
#
|
86
|
+
# Returns a Vec res such that res.dim == n. Vector res depends rather
|
87
|
+
# chaotically on the integer argument i. If the last argument is
|
88
|
+
# 'false' res will have R-typed components, and C-typed components else.
|
89
|
+
def Vec.tob(n,i,complex=false)
|
90
|
+
vi = complex ? C.tob(i) : R.tob(i)
|
91
|
+
res=Vec.new(n, vi)
|
92
|
+
if complex
|
93
|
+
rg1 = Ran.new(-vi.re,vi.re)
|
94
|
+
rg2 = Ran.new(-vi.im,vi.im)
|
95
|
+
for j in 1..res.dim
|
96
|
+
res[j] = C.new(rg1.ran,rg2.ran)
|
97
|
+
end
|
98
|
+
else
|
99
|
+
rg = Ran.new(-vi,vi)
|
100
|
+
for j in 1..res.dim
|
101
|
+
res[j] = rg.ran
|
102
|
+
end
|
103
|
+
end
|
104
|
+
res
|
105
|
+
end
|
106
|
+
|
107
|
+
# Gives the pseudoinverse of the vector self.
|
108
|
+
# This means that all components get inverted except those that are
|
109
|
+
# close to zero in comparison to the component with the largest
|
110
|
+
# absolute value.
|
111
|
+
# For small components c ( |c| ~ acc * sup |self[i]| ) a continuous
|
112
|
+
# transition between the inverse and zero becomes operational.
|
113
|
+
|
114
|
+
def pseudo_inv(acc=0)
|
115
|
+
n = dim
|
116
|
+
fail "dim = 0" if n.zero?
|
117
|
+
res = clone
|
118
|
+
if acc.zero? # most common case, thus first and without ordering
|
119
|
+
# overhead
|
120
|
+
for i in 1..n
|
121
|
+
si = self[i]
|
122
|
+
res[i] = si.zero? ? si.to_0 : si.inv
|
123
|
+
end
|
124
|
+
else
|
125
|
+
arr = @x.clone
|
126
|
+
arr.each{ |v| v = v.abs }
|
127
|
+
arr.sort!
|
128
|
+
a_max = arr.last
|
129
|
+
eta = a_max * acc
|
130
|
+
eta *= 0.5
|
131
|
+
eta *= eta
|
132
|
+
for i in 1..n
|
133
|
+
si = self[i]
|
134
|
+
ni = si * si + eta
|
135
|
+
res[i] = si / ni
|
136
|
+
end
|
137
|
+
end
|
138
|
+
res
|
139
|
+
end
|
140
|
+
|
141
|
+
# Valid indexes start with 1 not with 0.
|
142
|
+
# Read access to the components also works via indexes such as
|
143
|
+
# y = x[3]
|
144
|
+
def [](i)
|
145
|
+
@x[i-1]
|
146
|
+
end
|
147
|
+
|
148
|
+
# Valid indexes start with 1 not with 0.
|
149
|
+
# Write access to the components also works via indexes such as
|
150
|
+
# x[1] = 3.14
|
151
|
+
def []=(i,a)
|
152
|
+
@x[i-1] = a
|
153
|
+
end
|
154
|
+
|
155
|
+
# Returns self + v, where v is a Vec
|
156
|
+
def +(v)
|
157
|
+
fail "object can't be added to a Vec" unless v.is_a?(Vec)
|
158
|
+
fail "dimension mismatch" unless dim == v.dim
|
159
|
+
res = clone
|
160
|
+
for i in 1..dim
|
161
|
+
res[i] += v[i]
|
162
|
+
end
|
163
|
+
res
|
164
|
+
end
|
165
|
+
|
166
|
+
# Returns self - v , where v is a Vec
|
167
|
+
def -(v)
|
168
|
+
fail "object can't be subtracted from a Vec" unless v.is_a?(Vec)
|
169
|
+
fail "dimension mismatch" unless dim == v.dim
|
170
|
+
res = clone
|
171
|
+
for i in 1..dim
|
172
|
+
res[i] -= v[i]
|
173
|
+
end
|
174
|
+
res
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns self * s, where s has the same type as the components of self.
|
178
|
+
def *(s)
|
179
|
+
res = clone
|
180
|
+
for i in 1..dim
|
181
|
+
res[i] *= s
|
182
|
+
end
|
183
|
+
res
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns -self.
|
187
|
+
def -@
|
188
|
+
res = clone
|
189
|
+
for i in 1..dim
|
190
|
+
res[i] = -res[i]
|
191
|
+
end
|
192
|
+
res
|
193
|
+
end
|
194
|
+
|
195
|
+
# Returns a string which consists of a list of the strings which
|
196
|
+
# represent the components.
|
197
|
+
def to_s
|
198
|
+
res = "\n Vec"
|
199
|
+
for i in 0...dim
|
200
|
+
res += "\n " + x[i].to_s
|
201
|
+
end
|
202
|
+
res + "\n end Vec"
|
203
|
+
end
|
204
|
+
|
205
|
+
# Prints the content of self and naming the output.
|
206
|
+
def prn(name)
|
207
|
+
for i in 1..dim
|
208
|
+
puts " #{name}[#{i}] = " + self[i].to_s
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# The order relation is here lexicographic ordering of lists.
|
213
|
+
# Needed only for book-keeping purposes.
|
214
|
+
# Defines the functionality of self as a Comparable.
|
215
|
+
def <=> (v)
|
216
|
+
d1 = dim; d2 = v.dim
|
217
|
+
if d1 < d2
|
218
|
+
return -1
|
219
|
+
elsif d1 > d2
|
220
|
+
return 1
|
221
|
+
else
|
222
|
+
for i in 0...d1
|
223
|
+
ci = x[i] <=> v.x[i]
|
224
|
+
return ci unless ci == 0
|
225
|
+
end
|
226
|
+
end
|
227
|
+
return 0
|
228
|
+
end
|
229
|
+
|
230
|
+
# Defines the functionality of self as an Enumerable.
|
231
|
+
def each
|
232
|
+
@x.each{ |c| yield c}
|
233
|
+
end
|
234
|
+
|
235
|
+
# Returns the scalar product (self|v). The complex
|
236
|
+
# conjugation (which acts trivially on R) affects here the
|
237
|
+
# first factor. This is the convention preferred in physics.
|
238
|
+
def spr(v)
|
239
|
+
fail "dimension mismatch" unless dim == v.dim
|
240
|
+
return nil if dim.zero?
|
241
|
+
s = self[1].conj * v[1]
|
242
|
+
for i in 2..dim
|
243
|
+
s += self[i].conj * v[i]
|
244
|
+
end
|
245
|
+
s
|
246
|
+
end
|
247
|
+
|
248
|
+
# Returns a 'modified scalar product' in which no
|
249
|
+
# complex conjugation is involved.
|
250
|
+
def convolution(v)
|
251
|
+
fail "dimension mismatch" unless dim == v.dim
|
252
|
+
return nil if dim.zero?
|
253
|
+
s = self[1] * v[1]
|
254
|
+
for i in 2..dim
|
255
|
+
s += self[i] * v[i]
|
256
|
+
end
|
257
|
+
s
|
258
|
+
end
|
259
|
+
|
260
|
+
# Returns the square of absolute value of self.
|
261
|
+
def abs2
|
262
|
+
spr(self)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Returns the absolute value of self. This is also
|
266
|
+
# known as the L2-norm.
|
267
|
+
def abs
|
268
|
+
if complex?
|
269
|
+
abs2.re.sqrt
|
270
|
+
else
|
271
|
+
abs2.sqrt
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# Returns a unit vector which has the same direction as self,
|
276
|
+
# (or self if this is the zero-vector).
|
277
|
+
def uv
|
278
|
+
r = abs
|
279
|
+
if r.zero?
|
280
|
+
clone
|
281
|
+
else
|
282
|
+
ri = r.inv
|
283
|
+
self * ri
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
# Returns a relative distance between self and v.
|
288
|
+
def dis(v)
|
289
|
+
a = abs
|
290
|
+
b = v.abs
|
291
|
+
d = (self - v).abs
|
292
|
+
s = a + b
|
293
|
+
return R.c0 if s.zero?
|
294
|
+
d1 = d/s
|
295
|
+
Basics.inf(d,d1)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Returns 'true' if the first component is complex. Notice
|
299
|
+
# that the normal usage of Vec is to have all components
|
300
|
+
# of the same type.
|
301
|
+
def complex?
|
302
|
+
return nil if dim.zero?
|
303
|
+
@x[0].complex?
|
304
|
+
end
|
305
|
+
|
306
|
+
# Consistency test of class Vec.
|
307
|
+
def Vec.test(n0, verbose = true , complex = false)
|
308
|
+
puts "Doing Vec.test( n = #{n0}, verbose = #{verbose}, " +
|
309
|
+
"complex = #{complex}) for R.prec = #{R.prec}:"
|
310
|
+
puts "*************************************************"
|
311
|
+
|
312
|
+
t1 = Time.now
|
313
|
+
s = R.c0
|
314
|
+
puts "class of s is " + s.class.to_s
|
315
|
+
i = n0
|
316
|
+
a = Vec.tob(n0, i, complex)
|
317
|
+
i += 1
|
318
|
+
b = Vec.tob(n0, i, complex)
|
319
|
+
i += 1
|
320
|
+
c = Vec.tob(n0, i, complex)
|
321
|
+
i += 1
|
322
|
+
s1 = complex ? C.ran(i) : R.ran(i)
|
323
|
+
i += 1
|
324
|
+
s2 = complex ? C.ran(i) : R.ran(i)
|
325
|
+
|
326
|
+
r = (a + b) + c
|
327
|
+
l = a + (b + c)
|
328
|
+
ds = r.dis(l)
|
329
|
+
puts "associativity: ds = " + ds.to_s if verbose
|
330
|
+
s += ds
|
331
|
+
|
332
|
+
r = (a - b) + c
|
333
|
+
l = a - (b - c)
|
334
|
+
ds = r.dis(l)
|
335
|
+
puts "associativity 2: ds = " + ds.to_s if verbose
|
336
|
+
s += ds
|
337
|
+
|
338
|
+
r = (a + b) * s1
|
339
|
+
l = a * s1 + b * s1
|
340
|
+
ds = r.dis(l)
|
341
|
+
puts "distributivity: ds = " + ds.to_s if verbose
|
342
|
+
s += ds
|
343
|
+
|
344
|
+
r = c * (s1*s2)
|
345
|
+
l = (c * s1) * s2
|
346
|
+
ds = r.dis(l)
|
347
|
+
puts "distributivity of multiplication by scalars: ds = " + ds.to_s if verbose
|
348
|
+
s += ds
|
349
|
+
|
350
|
+
r = a
|
351
|
+
l = -(-a)
|
352
|
+
ds = r.dis(l)
|
353
|
+
puts "idempotency of unary minus: ds = " + ds.to_s if verbose
|
354
|
+
s += ds
|
355
|
+
|
356
|
+
r = (a + b).spr(c)
|
357
|
+
l = a.spr(c) + b.spr(c)
|
358
|
+
ds = r.dis(l)
|
359
|
+
puts "distributivity of spr: ds = " + ds.to_s if verbose
|
360
|
+
s += ds
|
361
|
+
|
362
|
+
t2 = Time.now
|
363
|
+
|
364
|
+
if verbose
|
365
|
+
puts
|
366
|
+
a.prn("a")
|
367
|
+
puts
|
368
|
+
b.prn("b")
|
369
|
+
puts
|
370
|
+
c.prn("c")
|
371
|
+
puts
|
372
|
+
s1.prn("s1")
|
373
|
+
puts
|
374
|
+
s2.prn("s2")
|
375
|
+
end
|
376
|
+
|
377
|
+
puts "class of s is " + s.class.to_s + " ."
|
378
|
+
puts "The error sum s is " + s.to_s + " ."
|
379
|
+
puts "It should be close to 0."
|
380
|
+
puts "Computation time was " + (t2-t1).to_s
|
381
|
+
s
|
382
|
+
end
|
383
|
+
|
384
|
+
end # Vec
|
385
|
+
|
386
|
+
# Matrix space of arbitrary dimension.
|
387
|
+
# The intended usage is that the elements
|
388
|
+
# of a matrix are all either real or complex.
|
389
|
+
# Since one is allowed to change any matrix element into
|
390
|
+
# any object there is no guaranty for type-uniformity
|
391
|
+
# of the elements of a matrix.
|
392
|
+
|
393
|
+
class Mat
|
394
|
+
include Enumerable
|
395
|
+
include Comparable
|
396
|
+
attr :x, true
|
397
|
+
|
398
|
+
# Returns the 'dimension' of the matrix, i.e. the number of its
|
399
|
+
# row-vectors. This thus is m for a 'm times n - matrix'.
|
400
|
+
def dim; @x.size; end
|
401
|
+
|
402
|
+
# Let self be a (m,n)-matrix (also called a m times n matrix)
|
403
|
+
# then dim1 == m
|
404
|
+
def dim1; @x.size; end
|
405
|
+
|
406
|
+
# Let self be a (m,n)-matrix (also called a m times n matrix)
|
407
|
+
# then dim2 == n
|
408
|
+
def dim2
|
409
|
+
return 0 if dim.zero?
|
410
|
+
self[1].dim
|
411
|
+
end
|
412
|
+
|
413
|
+
# These are the 5 mehods to generate a matrix via 'new'
|
414
|
+
# m1 = Mat.new(aMat)
|
415
|
+
# m2 = Mat.new(anArrayOfVec)
|
416
|
+
# m3 = Mat.new(aVec)
|
417
|
+
# m4 = Mat.new(aPositiveInteger, aRealOrComplex)
|
418
|
+
# m5 = Mat.new(aPositiveInteger, aPositiveInteger, aRealOrComplex)
|
419
|
+
# Here, m1 is a copy of aMat, m2 is a matrix which has as row
|
420
|
+
# vectors, the components of anArrayOfVec. If these vectors have
|
421
|
+
# not all he same dimension, failure results; m3 is a square matrix
|
422
|
+
# in which only the main diagonal may have non-zero elements,
|
423
|
+
# and in which ths diagonal is given as aVec; m4 is a square matrix
|
424
|
+
# with the dimension given by the first argument, and with all matrix
|
425
|
+
# elements equal to the second argment; m5 is a rectangular matrix
|
426
|
+
# with dim1 and dim2 given by the first and the second argument, and
|
427
|
+
# with all matrix elements equal to the third argument.
|
428
|
+
|
429
|
+
def initialize(*arg)
|
430
|
+
case arg.size
|
431
|
+
when 0
|
432
|
+
@x = Array.new
|
433
|
+
when 1 # ok if this is a Matrix or an array of Vectors
|
434
|
+
a0 = arg[0]
|
435
|
+
if a0.is_a?(Mat)
|
436
|
+
@x = Array.new(a0.x)
|
437
|
+
elsif a0.is_a?(Array)
|
438
|
+
n = a0.size
|
439
|
+
if n.zero?
|
440
|
+
@x = Array.new
|
441
|
+
else
|
442
|
+
misfit = 0
|
443
|
+
a0.each{|c|
|
444
|
+
misfit += 1 unless c.is_a?(Vec)
|
445
|
+
}
|
446
|
+
fail "input must consist of Vec-objects" unless misfit.zero?
|
447
|
+
misfit2 = 0
|
448
|
+
d2 = a0[1].dim
|
449
|
+
a0.each{|c|
|
450
|
+
misfit2 += 1 unless c.dim == d2
|
451
|
+
}
|
452
|
+
fail "input Vec-objects must agree in dimension" unless
|
453
|
+
misfit.zero?
|
454
|
+
@x = a0.clone
|
455
|
+
end
|
456
|
+
elsif a0.is_a?(Vec) # make a diagonal matrix
|
457
|
+
n = a0.dim
|
458
|
+
if n.zero?
|
459
|
+
@x = Array.new
|
460
|
+
else
|
461
|
+
c = a0[1].to_0
|
462
|
+
vc = Vec.new(n,c)
|
463
|
+
@x = Array.new(n,vc)
|
464
|
+
for i in 1..n
|
465
|
+
s!(i,i,a0[i])
|
466
|
+
end
|
467
|
+
end
|
468
|
+
else
|
469
|
+
fail "no reasonable construction available for this argument"
|
470
|
+
end
|
471
|
+
when 2 # make a square matrix, the diagonal filled with one element
|
472
|
+
# (all others zero)
|
473
|
+
n = arg[0]
|
474
|
+
a = arg[1]
|
475
|
+
zero = a.to_0
|
476
|
+
vc = Vec.new(n,zero)
|
477
|
+
@x = Array.new(n,vc)
|
478
|
+
for i in 1..n
|
479
|
+
vi = Vec.new(vc)
|
480
|
+
vi[i] = a
|
481
|
+
@x[i-1] = vi
|
482
|
+
end
|
483
|
+
when 3 # make rectangular matrix filled with one element
|
484
|
+
n1 = arg[0]
|
485
|
+
fail "first argument must be integer" unless n1.integer?
|
486
|
+
n2 = arg[1]
|
487
|
+
fail "second argument must be integer" unless n2.integer?
|
488
|
+
a = arg[2]
|
489
|
+
vc = Vec.new(n2,a)
|
490
|
+
@x = Array.new(n1,vc)
|
491
|
+
else
|
492
|
+
fail "no construction for more than 3 arguments"
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# Returns an independent copy of self.
|
497
|
+
def clone
|
498
|
+
Mat.new(self)
|
499
|
+
end
|
500
|
+
|
501
|
+
# Generates a test object, here a n times n matrix with random
|
502
|
+
# elements. This object depends rather chaotically on the
|
503
|
+
# integer parameter i.
|
504
|
+
# If the last argument is 'false' the test matrix will have R-typed
|
505
|
+
# elements, and C-typed elements else.
|
506
|
+
def Mat.tob(n,i, complex = false)
|
507
|
+
if complex
|
508
|
+
ri = C.tob(i)
|
509
|
+
zero = ri.to_0
|
510
|
+
res=Mat.new(n, n, zero)
|
511
|
+
rg1 = Ran.new(-ri.re,ri.re)
|
512
|
+
rg2 = Ran.new(-ri.im,ri.im)
|
513
|
+
for j in 1..n
|
514
|
+
for k in 1..n
|
515
|
+
yjk = C.new(rg1.ran,rg2.ran)
|
516
|
+
res.s!(j,k,yjk)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
res
|
520
|
+
else
|
521
|
+
ri = R.tob(i)
|
522
|
+
zero = ri.to_0
|
523
|
+
res=Mat.new(n, n, zero)
|
524
|
+
rg = Ran.new(-ri,ri)
|
525
|
+
for j in 1..n
|
526
|
+
for k in 1..n
|
527
|
+
yjk = rg.ran
|
528
|
+
res.s!(j,k,yjk)
|
529
|
+
end
|
530
|
+
end
|
531
|
+
res
|
532
|
+
end
|
533
|
+
end
|
534
|
+
|
535
|
+
# Singular value decomposition. Slightly modified fom Press et al.
|
536
|
+
# Only needed as a algorithmic tool. The method for the end-user
|
537
|
+
# is method pseudo_inv.
|
538
|
+
def Mat.svdcmp(a, w, v)
|
539
|
+
|
540
|
+
m = a.dim1; n = a.dim2
|
541
|
+
fail "svdcmp: bad frame of a" unless m >= n
|
542
|
+
fail "svdcmp: bad frame of w" unless n == w.dim
|
543
|
+
fail "svdcmp: bad frame of v" unless v.dim1 == n && v.dim2 == n
|
544
|
+
fail "svdcmp: dim = 0 as input" if m.zero? || n.zero?
|
545
|
+
|
546
|
+
iter_max=40
|
547
|
+
a11 = a[1][1]
|
548
|
+
zero =a11.to_0
|
549
|
+
one = a11.to_1
|
550
|
+
two = one + one
|
551
|
+
rv1 = Vec.new(n,zero)
|
552
|
+
g = zero; scale = zero; anorm = zero
|
553
|
+
for i in 1..n
|
554
|
+
l = i + 1
|
555
|
+
rv1[i] = scale * g
|
556
|
+
g = zero; s = zero; scale = zero
|
557
|
+
if i <= m
|
558
|
+
for k in i..m; scale += a[k][i].abs; end
|
559
|
+
if scale.nonzero?
|
560
|
+
for k in i..m
|
561
|
+
aki = a[k][i]
|
562
|
+
aki /= scale
|
563
|
+
a.s!(k,i,aki)
|
564
|
+
s += aki * aki
|
565
|
+
end
|
566
|
+
f = a[i][i]
|
567
|
+
g = - Basics.sign2(s.sqrt,f)
|
568
|
+
h = f * g - s
|
569
|
+
a.s!(i,i,f - g)
|
570
|
+
for j in l..n
|
571
|
+
s = zero
|
572
|
+
for k in i..m; s += a[k][i] * a[k][j]; end
|
573
|
+
f = s/h
|
574
|
+
for k in i..m
|
575
|
+
akj = a[k][j]
|
576
|
+
akj += f * a[k][i]
|
577
|
+
a.s!(k,j,akj)
|
578
|
+
end
|
579
|
+
end # for j in l..n
|
580
|
+
for k in i..m
|
581
|
+
aki = a[k][i]
|
582
|
+
aki *= scale
|
583
|
+
a.s!(k,i,aki)
|
584
|
+
end
|
585
|
+
end # scale != zero
|
586
|
+
end # i <= m
|
587
|
+
w[i] = scale * g
|
588
|
+
g = zero; s = R.c0; scale = zero
|
589
|
+
if i <= m && i != n
|
590
|
+
for k in l..n; scale += a[i][k].abs; end
|
591
|
+
if scale.nonzero?
|
592
|
+
for k in l..n
|
593
|
+
aik = a[i][k]
|
594
|
+
aik /= scale
|
595
|
+
a.s!(i,k,aik)
|
596
|
+
s += aik * aik
|
597
|
+
end
|
598
|
+
f = a[i][l]
|
599
|
+
g = - Basics.sign2(s.sqrt,f)
|
600
|
+
h = f * g - s
|
601
|
+
a.s!(i,l,f - g)
|
602
|
+
for k in l..n; rv1[k] = a[i][k]/h ; end
|
603
|
+
for j in l..m
|
604
|
+
s = zero
|
605
|
+
for k in l..n; s += a[j][k] * a[i][k]; end
|
606
|
+
for k in l..n
|
607
|
+
ajk = a[j][k]
|
608
|
+
ajk += s * rv1[k]
|
609
|
+
a.s!(j,k,ajk)
|
610
|
+
end
|
611
|
+
end # for j in l..m
|
612
|
+
for k in l..n; aik = a[i][k]; aik *= scale; a.s!(i,k,aik); end
|
613
|
+
end # if scale != zero
|
614
|
+
end # if i <= m && i != n
|
615
|
+
anorm = Basics.sup(anorm,w[i].abs + rv1[i].abs)
|
616
|
+
end # for i in 1..n
|
617
|
+
i = n
|
618
|
+
while i >= 1
|
619
|
+
if i < n
|
620
|
+
if g.nonzero?
|
621
|
+
for j in l..n; v.s!(j,i, (a[i][j]/a[i][l])/g); end
|
622
|
+
for j in l..n
|
623
|
+
s = zero
|
624
|
+
for k in l..n; s += a[i][k] * v[k][j]; end
|
625
|
+
for k in l..n
|
626
|
+
vkj =v[k][j]
|
627
|
+
vkj += s * v[k][i]
|
628
|
+
v.s!(k,j,vkj)
|
629
|
+
end
|
630
|
+
end # for j in l..n
|
631
|
+
end # if g.notzero!
|
632
|
+
for j in l..n; v.s!(i,j,zero); v.s!(j,i,zero); end
|
633
|
+
end # if i< n
|
634
|
+
v.s!(i,i,one)
|
635
|
+
g = rv1[i]
|
636
|
+
l = i
|
637
|
+
i -= 1
|
638
|
+
end # while i >= 1
|
639
|
+
|
640
|
+
i = Basics.inf(m,n)
|
641
|
+
while i >= 1
|
642
|
+
l = i + 1
|
643
|
+
g = w[i]
|
644
|
+
for j in l..n; a.s!(i,j,zero); end
|
645
|
+
if g.nonzero?
|
646
|
+
g = one/g
|
647
|
+
for j in l..n
|
648
|
+
s = zero
|
649
|
+
for k in l..m; s += a[k][i] * a[k][j]; end
|
650
|
+
f = (s/a[i][i]) * g
|
651
|
+
for k in i..m
|
652
|
+
akj = a[k][j]; akj += f * a[k][i]; a.s!(k,j,akj)
|
653
|
+
end
|
654
|
+
end # for j in l..n
|
655
|
+
for j in i..m; aji = a[j][i]; aji *= g; a.s!(j,i,aji); end
|
656
|
+
else
|
657
|
+
for j in i..m; a.s!(j,i,zero); end
|
658
|
+
end # if g.nonzero?
|
659
|
+
aii = a[i][i]; aii += one; a.s!(i,i,aii)
|
660
|
+
i -= 1
|
661
|
+
end # while i >= 1
|
662
|
+
|
663
|
+
k = n
|
664
|
+
while k >=1
|
665
|
+
for its in 1..iter_max
|
666
|
+
flag = 1
|
667
|
+
l = k
|
668
|
+
while l >= 1
|
669
|
+
nm = l - 1
|
670
|
+
if rv1[l].abs + anorm == anorm
|
671
|
+
flag = 0
|
672
|
+
break
|
673
|
+
end # if rv1[l].abs + anorm == anorm
|
674
|
+
break if w[nm].abs + anorm == anorm
|
675
|
+
l -= 1
|
676
|
+
end # while l >= 1
|
677
|
+
if flag.nonzero?
|
678
|
+
c = zero
|
679
|
+
s = one
|
680
|
+
for i in l..k
|
681
|
+
f = s * rv1[i]
|
682
|
+
rv1[i] = c * rv1[i]
|
683
|
+
break if f.abs + anorm == anorm
|
684
|
+
g = w[i]
|
685
|
+
h = f.hypot(g)
|
686
|
+
w[i] = h
|
687
|
+
h = one/h
|
688
|
+
c = g * h
|
689
|
+
s = -f * h
|
690
|
+
for j in 1..m
|
691
|
+
y = a[j][nm]; z = a[j][i];
|
692
|
+
a.s!(j,nm,y*c+z*s); a.s!(j,i,z*c-y*s)
|
693
|
+
end # for j in 1..m
|
694
|
+
end # for i in l..k
|
695
|
+
end # if flag.nonzero?
|
696
|
+
z = w[k]
|
697
|
+
if l == k
|
698
|
+
if z < zero
|
699
|
+
w[k] = -z
|
700
|
+
for j in 1..n; v.s!(j,k,-v[j][k]); end
|
701
|
+
end # if z < zero
|
702
|
+
break
|
703
|
+
end # if l == k
|
704
|
+
fail "no convergence in #{iter_max} iterations" if its == iter_max
|
705
|
+
x = w[l]; nm = k - 1; y = w[nm]; g = rv1[nm]; h = rv1[k]
|
706
|
+
f = ((y-z) * (y+z) + (g-h) * (g+h))/(h*y*two)
|
707
|
+
g = f.hypot(one)
|
708
|
+
f = ((x-z)*(x+z)+h*((y/(f+Basics.sign2(g,f)))-h))/x;
|
709
|
+
c = one; s = one
|
710
|
+
for j in l..nm
|
711
|
+
i = j + 1; g = rv1[i]; y =w[i]; h = s * g; g = c * g
|
712
|
+
z = f.hypot(h)
|
713
|
+
rv1[j] = z
|
714
|
+
c = f/z
|
715
|
+
s = h/z
|
716
|
+
f = x*c+g*s; g = g*c-x*s; h=y*s; y *= c;
|
717
|
+
for jj in 1..n
|
718
|
+
x=v[jj][j]; z=v[jj][i];
|
719
|
+
v.s!(jj,j,x*c+z*s); v.s!(jj,i,z*c-x*s)
|
720
|
+
end # for jj in 1..n
|
721
|
+
z = f.hypot(h)
|
722
|
+
w[j] = z
|
723
|
+
if z.nonzero?
|
724
|
+
z = one/z; c = f * z; s = h * z
|
725
|
+
end # if z.nonzero?
|
726
|
+
f=c*g+s*y; x=c*y-s*g;
|
727
|
+
for jj in 1..m
|
728
|
+
y=a[jj][j]; z=a[jj][i]
|
729
|
+
a.s!(jj,j,y*c+z*s); a.s!(jj,i,z*c-y*s)
|
730
|
+
end # for jj in 1..m
|
731
|
+
end # for j in l..nm
|
732
|
+
rv1[l] = zero
|
733
|
+
rv1[k] = f
|
734
|
+
w[k] = x
|
735
|
+
end # for its in 1..iter_max
|
736
|
+
k -= 1
|
737
|
+
end # while k >=1
|
738
|
+
end
|
739
|
+
|
740
|
+
# Reading row vectors. Valid indexes are 1,...,dim1.
|
741
|
+
def [](i)
|
742
|
+
@x[i-1]
|
743
|
+
end
|
744
|
+
|
745
|
+
# Setting row vectors. Valid indexes are 1,...,dim1.
|
746
|
+
# Notice that setting matrix elements via [][]= is not permanent.
|
747
|
+
# For setting matrix elements the method s! is provided.
|
748
|
+
def []=(i,a)
|
749
|
+
@x[i-1] = a
|
750
|
+
end
|
751
|
+
|
752
|
+
# The 's' stands for 'set (value)' and the '!' this is a method by which
|
753
|
+
# self changes (non-constant or mutating method).
|
754
|
+
|
755
|
+
def s!(i,j,a)
|
756
|
+
si = Vec.new(self[i]) # don't change the row-vector self[i]
|
757
|
+
# itself. Such changes are subject to subtle side effects.
|
758
|
+
# Worcing on a copy is safe.
|
759
|
+
si[j] = a # changing si here is normal syntax
|
760
|
+
self[i] = si # this is OK, of course
|
761
|
+
# self[i] = Vec.new(si) would work too, but would cause more work
|
762
|
+
end
|
763
|
+
|
764
|
+
# Returns a string which consists of a list of the strings which
|
765
|
+
# represent the row-vectors.
|
766
|
+
def to_s
|
767
|
+
res = "\n Mat"
|
768
|
+
for i in 0...dim
|
769
|
+
res += "\n " + x[i].to_s
|
770
|
+
end
|
771
|
+
res + "\n end Mat"
|
772
|
+
end
|
773
|
+
|
774
|
+
# Prints the content of self and naming the output.
|
775
|
+
def prn(name)
|
776
|
+
for i in 1..dim1
|
777
|
+
for j in 1..dim2
|
778
|
+
puts " #{name}[#{i}][#{j}] = " + self[i][j].to_s
|
779
|
+
end
|
780
|
+
end
|
781
|
+
end
|
782
|
+
|
783
|
+
# Unary minus operator. Returns - self.
|
784
|
+
def -@
|
785
|
+
res = clone
|
786
|
+
for i in 1..dim
|
787
|
+
res[i] = -res[i]
|
788
|
+
end
|
789
|
+
res
|
790
|
+
end
|
791
|
+
|
792
|
+
# Returns self + v, where v is a Mat
|
793
|
+
def +(v)
|
794
|
+
fail "Object can't be added to a Mat." unless v.is_a?(Mat)
|
795
|
+
fail "Dimension mismatch." unless dim == v.dim
|
796
|
+
res = clone
|
797
|
+
for i in 1..dim
|
798
|
+
res[i] += v[i]
|
799
|
+
end
|
800
|
+
res
|
801
|
+
end
|
802
|
+
|
803
|
+
# Returns self - v , where v is a Mat
|
804
|
+
def -(v)
|
805
|
+
fail "Object can't be subtracted from a Mat." unless v.is_a?(Mat)
|
806
|
+
fail "Dimension mismatch." unless dim == v.dim
|
807
|
+
res = clone
|
808
|
+
for i in 1..dim
|
809
|
+
res[i] -= v[i]
|
810
|
+
end
|
811
|
+
res
|
812
|
+
end
|
813
|
+
|
814
|
+
# Returns the transposed of self.
|
815
|
+
def trp # implementation without s!
|
816
|
+
d1 = dim1
|
817
|
+
d2 = dim2
|
818
|
+
fail "dim1 == 0" if d1.zero?
|
819
|
+
fail "dim2 == 0" if d2.zero?
|
820
|
+
zero = @x[0][0].to_0
|
821
|
+
# self has d1 row-vectors of length d2.
|
822
|
+
# The result of transposing has d2 row-vectors of length d1.
|
823
|
+
v = Array.new(d2)
|
824
|
+
for j in 0...d2
|
825
|
+
vj = Vec.new(d1,zero)
|
826
|
+
for i in 0...d1
|
827
|
+
vj.x[i] = @x[i].x[j]
|
828
|
+
end
|
829
|
+
v[j] = vj
|
830
|
+
end
|
831
|
+
Mat.new(v)
|
832
|
+
end
|
833
|
+
|
834
|
+
# Returns the Hermitian conjugate of self.
|
835
|
+
def conj
|
836
|
+
d1 = dim1
|
837
|
+
d2 = dim2
|
838
|
+
fail "dim1 == 0" if d1.zero?
|
839
|
+
fail "dim2 == 0" if d2.zero?
|
840
|
+
zero = @x[0].x[0].to_0
|
841
|
+
res = Mat.new(d2,d1,zero)
|
842
|
+
for i in 0...d1
|
843
|
+
for j in 0...d2
|
844
|
+
sij = @x[i].x[j]
|
845
|
+
res.s!(j+1,i+1,sij.conj)
|
846
|
+
end
|
847
|
+
end
|
848
|
+
res
|
849
|
+
end
|
850
|
+
|
851
|
+
|
852
|
+
# 'to zero' Returns a matrix with he same dimensions
|
853
|
+
# as self, but with all matrix elements set to zero.
|
854
|
+
|
855
|
+
def to_0
|
856
|
+
d1 = dim1
|
857
|
+
d2 = dim2
|
858
|
+
fail "dim1 == 0" if d1.zero?
|
859
|
+
fail "dim2 == 0" if d2.zero?
|
860
|
+
zero = @x[0].x[0].to_0
|
861
|
+
Mat.new(d2,d1,zero)
|
862
|
+
end
|
863
|
+
|
864
|
+
# 'to one'. Returns a matrix with he same dimensions
|
865
|
+
# as self, but with all matrix elements set to 1.
|
866
|
+
def to_1
|
867
|
+
d1 = dim1
|
868
|
+
d2 = dim2
|
869
|
+
fail "dim1 == 0" if d1.zero?
|
870
|
+
fail "dim2 == 0" if d2.zero?
|
871
|
+
fail "dim1 != dim2" unless d1 == d2
|
872
|
+
unit = @x[0].x[0].to_1
|
873
|
+
diag = Vec.new(d1,unit)
|
874
|
+
Mat.new(diag)
|
875
|
+
end
|
876
|
+
|
877
|
+
=begin
|
878
|
+
# Multiplication of a Mat with either a Mat, Vec, or Numeric
|
879
|
+
def *(v)
|
880
|
+
d1 = dim1
|
881
|
+
fail "dim1 == 0" if d1.zero?
|
882
|
+
d2 = dim2
|
883
|
+
fail "dim2 == 0" if d2.zero?
|
884
|
+
zero=@x[0].x[0].to_0
|
885
|
+
if v.is_a?(Mat)
|
886
|
+
d3 = v.dim1
|
887
|
+
fail "dimenson mismatch" unless d3 == d2
|
888
|
+
d4 = v.dim2
|
889
|
+
fail "dim4 == 0" if d4.zero?
|
890
|
+
res = Mat.new(d1,d4,zero)
|
891
|
+
for i in 0...d1
|
892
|
+
ip = i + 1
|
893
|
+
for j in 0...d4
|
894
|
+
vij = zero
|
895
|
+
jp = j + 1
|
896
|
+
for k in 0...d2
|
897
|
+
vij += @x[i].x[k] * v.x[k].x[j]
|
898
|
+
end
|
899
|
+
res.s!(ip, jp, vij)
|
900
|
+
end
|
901
|
+
end
|
902
|
+
elsif v.is_a?(Vec)
|
903
|
+
d3 = v.dim
|
904
|
+
fail "dimenson mismatch" unless d3 == d2
|
905
|
+
res = Vec.new(d1,zero)
|
906
|
+
for i in 0...d1
|
907
|
+
vi = zero
|
908
|
+
for j in 0...d2
|
909
|
+
vi += @x[i].x[j] * v.x[j]
|
910
|
+
end
|
911
|
+
res.x[i] = vi
|
912
|
+
end
|
913
|
+
elsif v.is_a?(Numeric) # multiplication with scalar
|
914
|
+
res = clone
|
915
|
+
for i in 1..d1
|
916
|
+
res[i] *= v
|
917
|
+
end
|
918
|
+
else
|
919
|
+
fail "can't multiply with this object"
|
920
|
+
end
|
921
|
+
res
|
922
|
+
end
|
923
|
+
=end
|
924
|
+
|
925
|
+
# Multiplication of a Mat with either a Mat, Vec, or Numeric
|
926
|
+
def *(v)
|
927
|
+
d1 = dim1
|
928
|
+
fail "dim1 == 0" if d1.zero?
|
929
|
+
d2 = dim2
|
930
|
+
fail "dim2 == 0" if d2.zero?
|
931
|
+
zero=@x[0].x[0].to_0
|
932
|
+
if v.is_a?(Mat)
|
933
|
+
d3 = v.dim1
|
934
|
+
fail "dimenson mismatch" unless d3 == d2
|
935
|
+
d4 = v.dim2
|
936
|
+
fail "dim4 == 0" if d4.zero?
|
937
|
+
# we better produce the d1 row-vectors in turn
|
938
|
+
a = Array.new(d1)
|
939
|
+
for i in 0...d1
|
940
|
+
vi = Vec.new(d4,zero)
|
941
|
+
for j in 0...d4
|
942
|
+
vij = zero
|
943
|
+
for k in 0...d2
|
944
|
+
vij += @x[i].x[k] * v.x[k].x[j]
|
945
|
+
end
|
946
|
+
vi.x[j] = vij
|
947
|
+
end
|
948
|
+
a[i] = vi
|
949
|
+
end
|
950
|
+
res = Mat.new(a)
|
951
|
+
elsif v.is_a?(Vec)
|
952
|
+
d3 = v.dim
|
953
|
+
fail "dimenson mismatch" unless d3 == d2
|
954
|
+
res = Vec.new(d1,zero)
|
955
|
+
for i in 0...d1
|
956
|
+
vi = zero
|
957
|
+
for j in 0...d2
|
958
|
+
vi += @x[i].x[j] * v.x[j]
|
959
|
+
end
|
960
|
+
res.x[i] = vi
|
961
|
+
end
|
962
|
+
elsif v.is_a?(Numeric) # multiplication with scalar
|
963
|
+
res = clone
|
964
|
+
for i in 1..d1
|
965
|
+
res[i] *= v
|
966
|
+
end
|
967
|
+
else
|
968
|
+
fail "can't multiply with this object"
|
969
|
+
end
|
970
|
+
res
|
971
|
+
end
|
972
|
+
|
973
|
+
# The order relation is here lexicographic ordering of lists.
|
974
|
+
# Needed only for book-keeping purposes.
|
975
|
+
def <=> (v)
|
976
|
+
d1 = dim; d2 = v.dim
|
977
|
+
if d1 < d2
|
978
|
+
return -1
|
979
|
+
elsif d1 > d2
|
980
|
+
return 1
|
981
|
+
else
|
982
|
+
for i in 1..d1
|
983
|
+
ci = self[i] <=> v[i]
|
984
|
+
return ci unless ci == 0
|
985
|
+
end
|
986
|
+
end
|
987
|
+
return 0
|
988
|
+
end
|
989
|
+
|
990
|
+
def each
|
991
|
+
@x.each{ |c| yield c}
|
992
|
+
end
|
993
|
+
|
994
|
+
# Scalar product of matrices.
|
995
|
+
def spr(v)
|
996
|
+
fail "dimension mismatch" unless dim == v.dim
|
997
|
+
return nil if dim.zero?
|
998
|
+
s = self[1].spr(v[1])
|
999
|
+
for i in 2..dim
|
1000
|
+
s += self[i].spr(v[i])
|
1001
|
+
end
|
1002
|
+
s
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
# Square of absolute value.
|
1006
|
+
def abs2
|
1007
|
+
spr(self)
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
# Absolute value, always real
|
1011
|
+
def abs
|
1012
|
+
if complex?
|
1013
|
+
abs2.re.sqrt
|
1014
|
+
else
|
1015
|
+
abs2.sqrt
|
1016
|
+
end
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
# Relative distance between matrices
|
1020
|
+
def dis(v)
|
1021
|
+
a = abs
|
1022
|
+
b = v.abs
|
1023
|
+
d = (self - v).abs
|
1024
|
+
s = a + b
|
1025
|
+
return R.c0 if s.zero?
|
1026
|
+
d1 = d/s
|
1027
|
+
d < d1 ? d : d1
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
def square?
|
1031
|
+
dim1 == dim2
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def complex?
|
1035
|
+
return nil if dim.zero?
|
1036
|
+
@x[0].complex?
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
# Returns the pseudo-inverse (als known as Penrose inverse) of self.
|
1040
|
+
# If the argument acc is not zero, the discontinous treatment of singular
|
1041
|
+
# values near zero is replaced by a continuous one.
|
1042
|
+
# Notice that the pseudo inverse always exists, and that the pseudo-
|
1043
|
+
# inverse of a (m,n)-matrix is a (n,m)-matrix.
|
1044
|
+
# If the argument acc is not zero, the pseudo-inverse is the first
|
1045
|
+
# component of a 4-array res, which also contains the the
|
1046
|
+
# intermediary quantities a, w, v resulting from the call
|
1047
|
+
# Mat.svdcmp(a,w,v). a == res[1], w == res[2], v == res[3].
|
1048
|
+
# Especially the list w of the original singular values is thus made
|
1049
|
+
# accessible, so that one can judge whether their processing controlled
|
1050
|
+
# by the parameter acc was reasonable.
|
1051
|
+
# The pseudo_inverse is the most useful and stable mehod to solve
|
1052
|
+
# linear equations: Let a be a (m,n)-matrix, and b a m-vector.
|
1053
|
+
# The equation
|
1054
|
+
# a * x = b (i)
|
1055
|
+
# determines a n-vector x as
|
1056
|
+
# x = a.pseudo_inv * b
|
1057
|
+
# which is a solution of (i) if there is one. If there are many solutions
|
1058
|
+
# it is the one of minimum absolute value, and if there is no solution
|
1059
|
+
# it comes closest to be a solution: It minimizes the defect
|
1060
|
+
# (a * x - b).abs
|
1061
|
+
# Simply great! No need for LU-decompositions any more.
|
1062
|
+
|
1063
|
+
def pseudo_inv(acc = 0)
|
1064
|
+
if complex?
|
1065
|
+
fail "Pseudo inverse not yet impemented for complex matrices"
|
1066
|
+
end
|
1067
|
+
m = dim1
|
1068
|
+
fail "dim1 == 0" if m.zero?
|
1069
|
+
n=dim2
|
1070
|
+
fail "dim2 == 0" if n.zero?
|
1071
|
+
rightframe = m >= n
|
1072
|
+
a = clone
|
1073
|
+
a = a.trp if rightframe == false
|
1074
|
+
m = a.dim1; n = a.dim2
|
1075
|
+
zero = a[1][1].to_0
|
1076
|
+
v = Mat.new(n,n,zero)
|
1077
|
+
w = Vec.new(n,zero)
|
1078
|
+
vr = Mat.new(n,m,zero) # sic
|
1079
|
+
Mat.svdcmp(a,w,v)
|
1080
|
+
wi = w.pseudo_inv(acc) # w does not come out orderd
|
1081
|
+
for i in 1..n
|
1082
|
+
for j in 1..m
|
1083
|
+
sum = zero
|
1084
|
+
for k in 1..n
|
1085
|
+
sum += v[i][k] * wi[k] * a[j][k]
|
1086
|
+
end
|
1087
|
+
vr.s!(i,j,sum)
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
vr = vr.trp if rightframe == false
|
1091
|
+
if acc.zero?
|
1092
|
+
vr
|
1093
|
+
else
|
1094
|
+
[vr,a,w,v]
|
1095
|
+
end
|
1096
|
+
end
|
1097
|
+
|
1098
|
+
# In most cases ('up to a subst of measure zero') the pseudo-inverse
|
1099
|
+
# is also the inverse.
|
1100
|
+
def inv
|
1101
|
+
pseudo_inv
|
1102
|
+
end
|
1103
|
+
|
1104
|
+
# Consistency test of class Mat.
|
1105
|
+
def Mat.test(n0, verbose = true, complex = false )
|
1106
|
+
puts "Doing Mat.test( n = #{n0}, verbose = #{verbose}," +
|
1107
|
+
" complex = #{complex} ) for R.prec = #{R.prec}:"
|
1108
|
+
puts "******************************************************"
|
1109
|
+
t1 = Time.now
|
1110
|
+
s = R.c0
|
1111
|
+
puts "class of s is " + s.class.to_s
|
1112
|
+
i = n0 + 137
|
1113
|
+
a = Mat.tob(n0, i, complex)
|
1114
|
+
i += 1
|
1115
|
+
b = Mat.tob(n0, i, complex)
|
1116
|
+
i += 1
|
1117
|
+
c = Mat.tob(n0, i, complex)
|
1118
|
+
x = Vec.tob(n0, i, complex)
|
1119
|
+
i += 1
|
1120
|
+
y = Vec.tob(n0, i, complex)
|
1121
|
+
i += 1
|
1122
|
+
s1 = complex ? C.ran(i) : R.ran(i)
|
1123
|
+
i += 1
|
1124
|
+
s2 = complex ? C.ran(i) : R.ran(i)
|
1125
|
+
|
1126
|
+
unit = a.to_1
|
1127
|
+
|
1128
|
+
a0 = a.clone
|
1129
|
+
b0 = b.clone
|
1130
|
+
c0 = c.clone
|
1131
|
+
|
1132
|
+
abc0 = a0.abs + b0.abs + c0.abs
|
1133
|
+
|
1134
|
+
ac = a.clone
|
1135
|
+
a = b
|
1136
|
+
r = a
|
1137
|
+
l = b
|
1138
|
+
ds = r.dis(l)
|
1139
|
+
puts "assignment of variables: ds = " + ds.to_s if verbose
|
1140
|
+
s += ds
|
1141
|
+
|
1142
|
+
a = ac
|
1143
|
+
r = a
|
1144
|
+
l = ac
|
1145
|
+
ds = r.dis(l)
|
1146
|
+
puts "assignment of variables 2: ds = " + ds.to_s if verbose
|
1147
|
+
s += ds
|
1148
|
+
|
1149
|
+
r = (a + b) + c
|
1150
|
+
l = a + (b + c)
|
1151
|
+
ds = r.dis(l)
|
1152
|
+
puts "associativity of +: ds = " + ds.to_s if verbose
|
1153
|
+
s += ds
|
1154
|
+
|
1155
|
+
r = (a - b) + c
|
1156
|
+
l = a - (b - c)
|
1157
|
+
ds = r.dis(l)
|
1158
|
+
puts "associativity of -: ds = " + ds.to_s if verbose
|
1159
|
+
s += ds
|
1160
|
+
|
1161
|
+
r = (a * b) * c
|
1162
|
+
l = a * (b * c)
|
1163
|
+
ds = r.dis(l)
|
1164
|
+
puts "associativity of *: ds = " + ds.to_s if verbose
|
1165
|
+
s += ds
|
1166
|
+
|
1167
|
+
r = (a + b) * s1
|
1168
|
+
l = a * s1 + b * s1
|
1169
|
+
ds = r.dis(l)
|
1170
|
+
puts "distributivity of multiplication by scalars: ds = " +
|
1171
|
+
ds.to_s if verbose
|
1172
|
+
s += ds
|
1173
|
+
|
1174
|
+
r = c * (s1*s2)
|
1175
|
+
l = (c * s1) * s2
|
1176
|
+
ds = r.dis(l)
|
1177
|
+
puts "distributivity of multiplication by scalars: ds = " +
|
1178
|
+
ds.to_s if verbose
|
1179
|
+
s += ds
|
1180
|
+
|
1181
|
+
r = a
|
1182
|
+
l = -(-a)
|
1183
|
+
ds = r.dis(l)
|
1184
|
+
puts "idempotency of unary minus: ds = " + ds.to_s if verbose
|
1185
|
+
s += ds
|
1186
|
+
|
1187
|
+
r = (a + b).spr(c)
|
1188
|
+
l = a.spr(c) + b.spr(c)
|
1189
|
+
ds = r.dis(l)
|
1190
|
+
puts "distributivity of spr: ds = " + ds.to_s if verbose
|
1191
|
+
s += ds
|
1192
|
+
|
1193
|
+
r = (a + b) * c
|
1194
|
+
l = a * c + b * c
|
1195
|
+
ds = r.dis(l)
|
1196
|
+
puts "distributivity of matrix multiplication: ds = " +
|
1197
|
+
ds.to_s if verbose
|
1198
|
+
s += ds
|
1199
|
+
|
1200
|
+
r = (a * b) * x
|
1201
|
+
l = a * (b * x)
|
1202
|
+
ds = r.dis(l)
|
1203
|
+
puts "action on vectors 1: ds = " + ds.to_s if verbose
|
1204
|
+
s += ds
|
1205
|
+
|
1206
|
+
r = (a + b) * x
|
1207
|
+
l = a * x + b * x
|
1208
|
+
ds = r.dis(l)
|
1209
|
+
puts "action on vectors 2: ds = " + ds.to_s if verbose
|
1210
|
+
s += ds
|
1211
|
+
|
1212
|
+
r = b * (x + y)
|
1213
|
+
l = b * x + b * y
|
1214
|
+
ds = r.dis(l)
|
1215
|
+
puts "action on vectors 3: ds = " + ds.to_s if verbose
|
1216
|
+
s += ds
|
1217
|
+
|
1218
|
+
r = c * (x * s1)
|
1219
|
+
l = (c * s1) * x
|
1220
|
+
ds = r.dis(l)
|
1221
|
+
puts "action on vectors 4: ds = " + ds.to_s if verbose
|
1222
|
+
s += ds
|
1223
|
+
|
1224
|
+
if complex == false
|
1225
|
+
r = unit
|
1226
|
+
l = a * a.pseudo_inv
|
1227
|
+
ds = r.dis(l)
|
1228
|
+
puts "pseudo inverse is right inverse: ds = " + ds.to_s if verbose
|
1229
|
+
s += ds
|
1230
|
+
|
1231
|
+
r = unit
|
1232
|
+
l = a.pseudo_inv * a
|
1233
|
+
ds = r.dis(l)
|
1234
|
+
puts "pseudo inverse is left inverse: ds = " + ds.to_s if verbose
|
1235
|
+
s += ds
|
1236
|
+
else
|
1237
|
+
puts "test of pseudo inverse left out, since not implemented for complex"
|
1238
|
+
end
|
1239
|
+
|
1240
|
+
aMem = a.clone
|
1241
|
+
bMem = b.clone
|
1242
|
+
cMem = c.clone
|
1243
|
+
|
1244
|
+
# Testing the access functions, under harsh conditions with
|
1245
|
+
# inserting double transposition
|
1246
|
+
|
1247
|
+
for i in 1..a.dim1
|
1248
|
+
for j in 1..a.dim2
|
1249
|
+
b.s!(i,j,a[i][j]) # copies a to b
|
1250
|
+
end
|
1251
|
+
end
|
1252
|
+
|
1253
|
+
l = a
|
1254
|
+
r = a.trp.trp
|
1255
|
+
ds = r.dis(l)
|
1256
|
+
puts "test of double transposition: ds = " + ds.to_s if verbose
|
1257
|
+
s += ds
|
1258
|
+
|
1259
|
+
for i in 1..b.dim1
|
1260
|
+
for j in 1..b.dim2
|
1261
|
+
c.s!(i,j,b[i][j]) # copies b to c
|
1262
|
+
end
|
1263
|
+
end
|
1264
|
+
# Finally c should have the content of a
|
1265
|
+
|
1266
|
+
r = a
|
1267
|
+
l = c
|
1268
|
+
ds = r.dis(l)
|
1269
|
+
puts "test of access functions: ds = " + ds.to_s if verbose
|
1270
|
+
s += ds
|
1271
|
+
|
1272
|
+
a = aMem; b = bMem; c = cMem
|
1273
|
+
|
1274
|
+
abc = a.abs + b.abs + c.abs
|
1275
|
+
|
1276
|
+
l = abc
|
1277
|
+
r = abc0
|
1278
|
+
ds = r.dis(l)
|
1279
|
+
puts "heavy test of assignment: ds = " + ds.to_s if verbose
|
1280
|
+
|
1281
|
+
t2 = Time.now
|
1282
|
+
|
1283
|
+
if verbose
|
1284
|
+
puts
|
1285
|
+
a.prn("a")
|
1286
|
+
puts
|
1287
|
+
b.prn("b")
|
1288
|
+
puts
|
1289
|
+
c.prn("c")
|
1290
|
+
puts
|
1291
|
+
s1.prn("s1")
|
1292
|
+
puts
|
1293
|
+
s2.prn("s2")
|
1294
|
+
puts
|
1295
|
+
x.prn("x")
|
1296
|
+
puts
|
1297
|
+
y.prn("y")
|
1298
|
+
end
|
1299
|
+
|
1300
|
+
puts "class of s is " + s.class.to_s + " ."
|
1301
|
+
puts "The error sum s is " + s.to_s + " ."
|
1302
|
+
puts "It should be close to 0."
|
1303
|
+
puts "Computation time was " + (t2-t1).to_s
|
1304
|
+
s
|
1305
|
+
end
|
1306
|
+
|
1307
|
+
end # Mat
|
1308
|
+
|
1309
|
+
end # module AppMath
|