To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.
root / src / may / vector.yeti @ 595:3b1f2aa8d36b
History | View | Annotate | Download (11.4 KB)
| 1 |
|
|---|---|
| 2 |
/** |
| 3 |
* Vectors. A May vector is a typesafe, immutable wrapper around a Java |
| 4 |
* primitive array of doubles. |
| 5 |
* |
| 6 |
* Although not as convenient and flexible as a Yeti array<number> or |
| 7 |
* list<number>, a vector can be faster and more compact when dealing |
| 8 |
* with dense data of suitable range and precision such as sampled |
| 9 |
* sequences. |
| 10 |
* |
| 11 |
* Because vectors are immutable, functions that transform them will |
| 12 |
* return the original data for identity transformations |
| 13 |
* (e.g. resizing to the vector's existing length). |
| 14 |
*/ |
| 15 |
|
| 16 |
module may.vector; |
| 17 |
|
| 18 |
import java.util: Arrays; |
| 19 |
import may.bits: VectorBits; |
| 20 |
|
| 21 |
{ floor, ceil } = load may.mathmisc;
|
| 22 |
|
| 23 |
/// Return a vector of n zeros. |
| 24 |
zeros n is number -> ~double[] = |
| 25 |
new double[n]; |
| 26 |
|
| 27 |
/// Return a vector of length n, containing all m. |
| 28 |
consts m n is number -> number -> ~double[] = |
| 29 |
(a = zeros n; |
| 30 |
Arrays#fill(a, m); |
| 31 |
a); |
| 32 |
|
| 33 |
/// Return a vector of length n, containing all ones. |
| 34 |
ones n = consts 1.0 n; |
| 35 |
|
| 36 |
/// Return a vector of length n, containing random values. |
| 37 |
randoms n is number -> ~double[] = |
| 38 |
(map \(Math#random()) [1..n]) as ~double[]; |
| 39 |
|
| 40 |
/// Return a vector of the values in the given list. |
| 41 |
fromList l is list?<number> -> ~double[] = |
| 42 |
l as ~double[]; |
| 43 |
|
| 44 |
/// Return the given vector as a list. |
| 45 |
list' a is ~double[] -> list<number> = |
| 46 |
list a; |
| 47 |
|
| 48 |
/// Return the given vector as a Yeti array. |
| 49 |
array' a is ~double[] -> array<number> = |
| 50 |
array a; |
| 51 |
|
| 52 |
/// Return the length of the given vector. |
| 53 |
length' v is ~double[] -> number = |
| 54 |
length v; |
| 55 |
|
| 56 |
/// Return true if the given vector is empty (has length 0). |
| 57 |
empty?' = |
| 58 |
empty? . list'; |
| 59 |
|
| 60 |
/// Return element n in the given vector v. (The function name and |
| 61 |
/// argument order are chosen for symmetry with the similar standard |
| 62 |
/// library array function.) |
| 63 |
at' v n is ~double[] -> number -> number = |
| 64 |
v[n]; |
| 65 |
|
| 66 |
/// Return the given vector as a Java primitive float array. |
| 67 |
floats a is ~double[] -> ~float[] = |
| 68 |
(len = length' a; |
| 69 |
f = new float[len]; |
| 70 |
for [0..len-1] do i: |
| 71 |
f[i] := a[i]; |
| 72 |
done; |
| 73 |
f); |
| 74 |
|
| 75 |
/// Return a vector of the values in the given Java primitive float array. |
| 76 |
fromFloats ff is ~float[] -> ~double[] = |
| 77 |
(len = length (list ff); |
| 78 |
a = new double[len]; |
| 79 |
for [0..len-1] do i: |
| 80 |
a[i] := ff[i]; |
| 81 |
done; |
| 82 |
a); |
| 83 |
|
| 84 |
/// Return true if the given vectors are equal, using the standard == |
| 85 |
/// comparator on their elements. |
| 86 |
equal v1 v2 is ~double[] -> ~double[] -> boolean = |
| 87 |
list v1 == list v2; |
| 88 |
|
| 89 |
/// Return true if the given vectors are equal, when applying the |
| 90 |
/// given numerical comparator to each element. |
| 91 |
equalUnder comparator v1 v2 = |
| 92 |
length' v1 == length' v2 and |
| 93 |
all id (map2 comparator (list v1) (list v2)); |
| 94 |
|
| 95 |
/// Return another copy of the given vector. |
| 96 |
copyOf v is ~double[] -> ~double[] = |
| 97 |
Arrays#copyOf(v, length v); |
| 98 |
|
| 99 |
/// Return the given vector as a primitive array. Modifying the |
| 100 |
/// array contents will not affect the original vector. |
| 101 |
primitive = copyOf; |
| 102 |
|
| 103 |
/// Return a new vector containing a subset of the elements of the |
| 104 |
/// given vector, from index start (inclusive) to index finish |
| 105 |
/// (exclusive). (The function name and argument order are chosen for |
| 106 |
/// symmetry with the standard library slice and strSlice functions.) |
| 107 |
slice v start finish is ~double[] -> number -> number -> ~double[] = |
| 108 |
(len = length v; |
| 109 |
if start == 0 and finish == len then v |
| 110 |
elif start < 0 then slice v 0 finish |
| 111 |
elif start > len then slice v len finish |
| 112 |
elif finish < start then slice v start start |
| 113 |
elif finish > len then slice v start len |
| 114 |
else |
| 115 |
Arrays#copyOfRange(v, start, finish); |
| 116 |
fi); |
| 117 |
|
| 118 |
/// Return a vector of length n, containing the contents of the given |
| 119 |
/// vector v. If v is longer than n, the contents will be truncated; |
| 120 |
/// if shorter, they will be padded with zeros. |
| 121 |
resizedTo n v is number -> ~double[] -> ~double[] = |
| 122 |
if n == length v then v; |
| 123 |
else Arrays#copyOf(v, n); |
| 124 |
fi; |
| 125 |
|
| 126 |
/// Return a vector that is the reverse of the given vector. Name |
| 127 |
/// chosen (in preference to passive "reversed") for symmetry with the |
| 128 |
/// standard library list reverse function. |
| 129 |
reverse v is ~double[] -> ~double[] = |
| 130 |
(len = length v; |
| 131 |
a = new double[len]; |
| 132 |
for [0..len-1] do i: |
| 133 |
a[len-i-1] := v[i]; |
| 134 |
done; |
| 135 |
a); |
| 136 |
|
| 137 |
/// Return a single new vector that contains the contents of all the |
| 138 |
/// given vectors, in order. (Unlike the standard module list concat |
| 139 |
/// function, this one cannot be lazy.) |
| 140 |
concat' vv is list?<~double[]> -> ~double[] = |
| 141 |
case vv of |
| 142 |
[v]: v; |
| 143 |
v0::rest: |
| 144 |
(len = sum (map length' vv); |
| 145 |
vout = Arrays#copyOf(v0 is ~double[], len); |
| 146 |
var base = length' v0; |
| 147 |
for rest do v: |
| 148 |
vlen = length' v; |
| 149 |
System#arraycopy(v, 0, vout, base, vlen); |
| 150 |
base := base + vlen; |
| 151 |
done; |
| 152 |
vout); |
| 153 |
_: zeros 0; |
| 154 |
esac; |
| 155 |
|
| 156 |
/// Return a single new vector that contains the contents of the given |
| 157 |
/// vector, repeated n times. The vector will therefore have length n |
| 158 |
/// times the length of v. |
| 159 |
repeated v n is ~double[] -> number -> ~double[] = |
| 160 |
concat' (map \(v) [1..n]); |
| 161 |
|
| 162 |
sum' v is ~double[] -> number = |
| 163 |
VectorBits#sum(v); |
| 164 |
|
| 165 |
max' v is ~double[] -> number = |
| 166 |
(var mx = 0; |
| 167 |
for [0..length v - 1] do i: |
| 168 |
if i == 0 or v[i] > mx then |
| 169 |
mx := v[i]; |
| 170 |
fi |
| 171 |
done; |
| 172 |
mx); |
| 173 |
|
| 174 |
maxindex v is ~double[] -> number = |
| 175 |
(var mx = 0; |
| 176 |
var mi = -1; |
| 177 |
for [0..length v - 1] do i: |
| 178 |
if i == 0 or v[i] > mx then |
| 179 |
mx := v[i]; |
| 180 |
mi := i; |
| 181 |
fi |
| 182 |
done; |
| 183 |
mi); |
| 184 |
|
| 185 |
min' v is ~double[] -> number = |
| 186 |
(var mn = 0; |
| 187 |
for [0..length v - 1] do i: |
| 188 |
if i == 0 or v[i] < mn then |
| 189 |
mn := v[i]; |
| 190 |
fi |
| 191 |
done; |
| 192 |
mn); |
| 193 |
|
| 194 |
minindex v is ~double[] -> number = |
| 195 |
(var mn = 0; |
| 196 |
var mi = -1; |
| 197 |
for [0..length v - 1] do i: |
| 198 |
if i == 0 or v[i] < mn then |
| 199 |
mn := v[i]; |
| 200 |
mi := i; |
| 201 |
fi |
| 202 |
done; |
| 203 |
mi); |
| 204 |
|
| 205 |
mean v is ~double[] -> number = |
| 206 |
case length v of |
| 207 |
0: 0; |
| 208 |
len: sum' v / len |
| 209 |
esac; |
| 210 |
|
| 211 |
sorted v is ~double[] -> ~double[] = |
| 212 |
(v' = copyOf v; |
| 213 |
Arrays#sort(v'); |
| 214 |
v'); |
| 215 |
|
| 216 |
quantile q v is number -> ~double[] -> number = |
| 217 |
if empty? v then 0 |
| 218 |
else |
| 219 |
v = sorted v; |
| 220 |
dist = q * (length v - 1); |
| 221 |
low = floor dist; |
| 222 |
high = ceil dist; |
| 223 |
if low == high then v[low] |
| 224 |
else |
| 225 |
v[low] * (high - dist) + v[high] * (dist - low); |
| 226 |
fi; |
| 227 |
fi; |
| 228 |
|
| 229 |
median v is ~double[] -> number = |
| 230 |
quantile 0.5 v; |
| 231 |
|
| 232 |
listOp f vv = |
| 233 |
case vv of |
| 234 |
[v]: v; |
| 235 |
v::rest: |
| 236 |
(out = copyOf v; |
| 237 |
for rest (f out); |
| 238 |
out); |
| 239 |
_: failWith "Empty argument list"; |
| 240 |
esac; |
| 241 |
|
| 242 |
//!!! doc: throws exception if not all inputs are of the same length |
| 243 |
add vv is list?<~double[]> -> ~double[] = |
| 244 |
listOp do out v: VectorBits#addTo(out, v) done vv; |
| 245 |
|
| 246 |
//!!! doc: throws exception if not all inputs are of the same length |
| 247 |
subtract v1 v2 is ~double[] -> ~double[] -> ~double[] = |
| 248 |
listOp do out v: VectorBits#subtractFrom(out, v) done [v1, v2]; |
| 249 |
|
| 250 |
//!!! doc: throws exception if not all inputs are of the same length |
| 251 |
multiply vv is list?<~double[]> -> ~double[] = |
| 252 |
listOp do out v: VectorBits#multiplyBy(out, v) done vv; |
| 253 |
|
| 254 |
//!!! doc: throws exception if not all inputs are of the same length |
| 255 |
divide v1 v2 is ~double[] -> ~double[] -> ~double[] = |
| 256 |
listOp do out v: VectorBits#divideBy(out, v) done [v1, v2]; |
| 257 |
|
| 258 |
scaled n v is number -> ~double[] -> ~double[] = |
| 259 |
if n == 1 then v |
| 260 |
else VectorBits#scaled(v, n); |
| 261 |
fi; |
| 262 |
|
| 263 |
divideBy n v is number -> ~double[] -> ~double[] = |
| 264 |
// Not just "scaled (1/n)" -- this way we get exact rationals. In fact |
| 265 |
// the unit test for this function will fail if we use scaled (1/n) |
| 266 |
if n == 1 then v |
| 267 |
else fromList (map (/ n) (list v)); |
| 268 |
fi; |
| 269 |
|
| 270 |
sqr v = |
| 271 |
(out = copyOf v; |
| 272 |
VectorBits#multiplyBy(out, v); |
| 273 |
out); |
| 274 |
|
| 275 |
rms = |
| 276 |
sqrt . mean . sqr; |
| 277 |
|
| 278 |
abs' v is ~double[] -> ~double[] = |
| 279 |
fromList (map abs (list v)); |
| 280 |
|
| 281 |
negative v is ~double[] -> ~double[] = |
| 282 |
fromList (map (0-) (list v)); |
| 283 |
|
| 284 |
sqrt' v is ~double[] -> ~double[] = |
| 285 |
fromList (map sqrt (list v)); |
| 286 |
|
| 287 |
unityNormalised v is ~double[] -> ~double[] = |
| 288 |
(m = max' (abs' v); |
| 289 |
if m != 0 then |
| 290 |
divideBy m v; |
| 291 |
else |
| 292 |
v; |
| 293 |
fi); |
| 294 |
|
| 295 |
euclideanDistance v1 v2 is ~double[] -> ~double[] -> number = |
| 296 |
VectorBits#euclideanDistance(v1, v2); |
| 297 |
|
| 298 |
zipped vv is list?<~double[]> -> ~double[] = |
| 299 |
case vv of |
| 300 |
[v]: v; |
| 301 |
first::rest: |
| 302 |
(len = length' first; |
| 303 |
if len != min' (fromList (map length' vv)) then |
| 304 |
failWith "Vectors must all be of the same length"; |
| 305 |
fi; |
| 306 |
fromList |
| 307 |
(concat |
| 308 |
(map do i: |
| 309 |
map do v: |
| 310 |
at' v i |
| 311 |
done vv |
| 312 |
done [0..len-1]))); |
| 313 |
_: zeros 0; |
| 314 |
esac; |
| 315 |
|
| 316 |
unzipped n v is number -> ~double[] -> array<~double[]> = |
| 317 |
if n == 1 then array [v] |
| 318 |
else |
| 319 |
len = length' v; |
| 320 |
vv = array (map \(new double[ceil(len / n)]) [1..n]); |
| 321 |
for [0..len-1] do x: |
| 322 |
vv[x % n][int (x / n)] := at' v x; |
| 323 |
done; |
| 324 |
array vv; |
| 325 |
fi; |
| 326 |
|
| 327 |
typedef opaque vector_t = ~double[]; |
| 328 |
|
| 329 |
{
|
| 330 |
zeros, |
| 331 |
consts, |
| 332 |
ones, |
| 333 |
randoms, |
| 334 |
vector v = v, |
| 335 |
primitive, |
| 336 |
floats, |
| 337 |
fromFloats, |
| 338 |
fromList, |
| 339 |
list = list', |
| 340 |
array = array', |
| 341 |
length = length', |
| 342 |
empty? = empty?', |
| 343 |
at = at', |
| 344 |
equal, |
| 345 |
equalUnder, |
| 346 |
slice, |
| 347 |
resizedTo, |
| 348 |
reverse, |
| 349 |
sorted, |
| 350 |
repeated, |
| 351 |
concat = concat', |
| 352 |
sum = sum', |
| 353 |
mean, |
| 354 |
median, |
| 355 |
quantile, |
| 356 |
add, |
| 357 |
subtract, |
| 358 |
multiply, |
| 359 |
divide, |
| 360 |
scaled, |
| 361 |
divideBy, |
| 362 |
abs = abs', |
| 363 |
negative, |
| 364 |
sqr, |
| 365 |
sqrt = sqrt', |
| 366 |
rms, |
| 367 |
max = max', |
| 368 |
min = min', |
| 369 |
maxindex, |
| 370 |
minindex, |
| 371 |
unityNormalised, |
| 372 |
euclideanDistance, |
| 373 |
zipped, |
| 374 |
unzipped, |
| 375 |
} as {
|
| 376 |
zeros is number -> vector_t, |
| 377 |
consts is number -> number -> vector_t, |
| 378 |
ones is number -> vector_t, |
| 379 |
randoms is number -> vector_t, |
| 380 |
vector is ~double[] -> vector_t, |
| 381 |
primitive is vector_t -> ~double[], |
| 382 |
floats is vector_t -> ~float[], |
| 383 |
fromFloats is ~float[] -> vector_t, |
| 384 |
fromList is list?<number> -> vector_t, |
| 385 |
list is vector_t -> list<number>, |
| 386 |
array is vector_t -> array<number>, |
| 387 |
length is vector_t -> number, |
| 388 |
empty? is vector_t -> boolean, |
| 389 |
at is vector_t -> number -> number, |
| 390 |
equal is vector_t -> vector_t -> boolean, |
| 391 |
equalUnder is (number -> number -> boolean) -> vector_t -> vector_t -> boolean, |
| 392 |
slice is vector_t -> number -> number -> vector_t, |
| 393 |
resizedTo is number -> vector_t -> vector_t, |
| 394 |
reverse is vector_t -> vector_t, |
| 395 |
sorted is vector_t -> vector_t, |
| 396 |
repeated is vector_t -> number -> vector_t, |
| 397 |
concat is list?<vector_t> -> vector_t, |
| 398 |
sum is vector_t -> number, |
| 399 |
mean is vector_t -> number, |
| 400 |
median is vector_t -> number, |
| 401 |
quantile is number -> vector_t -> number, |
| 402 |
add is list?<vector_t> -> vector_t, |
| 403 |
subtract is vector_t -> vector_t -> vector_t, |
| 404 |
multiply is list?<vector_t> -> vector_t, |
| 405 |
divide is vector_t -> vector_t -> vector_t, |
| 406 |
scaled is number -> vector_t -> vector_t, |
| 407 |
divideBy is number -> vector_t -> vector_t, |
| 408 |
abs is vector_t -> vector_t, |
| 409 |
negative is vector_t -> vector_t, |
| 410 |
sqr is vector_t -> vector_t, |
| 411 |
sqrt is vector_t -> vector_t, |
| 412 |
rms is vector_t -> number, |
| 413 |
max is vector_t -> number, |
| 414 |
min is vector_t -> number, |
| 415 |
maxindex is vector_t -> number, |
| 416 |
minindex is vector_t -> number, |
| 417 |
unityNormalised is vector_t -> vector_t, |
| 418 |
euclideanDistance is vector_t -> vector_t -> number, |
| 419 |
zipped is list?<vector_t> -> vector_t, |
| 420 |
unzipped is number -> vector_t -> array<vector_t>, |
| 421 |
} |
| 422 |
|