# HG changeset patch # User Chris Cannam # Date 1396710144 -3600 # Node ID ab85dfc45e3f9d39e5a237d2e702fc2d637fc96f # Parent eeebedb84ca7e82243a64c43ebb24a2db52dec39# Parent 6a5c20ceb94921503bb271d3b58d292cffd5037e Merge diff -r eeebedb84ca7 -r ab85dfc45e3f .hgsubstate --- a/.hgsubstate Mon Mar 03 17:03:32 2014 +0000 +++ b/.hgsubstate Sat Apr 05 16:02:24 2014 +0100 @@ -1,1 +1,1 @@ -0a4af51413b7ddd1cc4c058fb732556b849cac21 ext +ea762d69743ec57dd9f10dbfc3ff87ffacf64565 ext diff -r eeebedb84ca7 -r ab85dfc45e3f bin/may --- a/bin/may Mon Mar 03 17:03:32 2014 +0000 +++ b/bin/may Sat Apr 05 16:02:24 2014 +0100 @@ -30,7 +30,7 @@ YETI_LIBDIR=$YLDIR/../other/yeti fi -CLASSPATH=$YLDIR/may.jar:$JARDIR/ayr.jar:$YETI_LIBDIR/yeti.jar:$YETI_LIBDIR/yeti-lib.jar:$JARDIR/jline-2.11-SNAPSHOT.jar:$JARDIR/jvamp.jar:$JARDIR/yertle.jar:$JARDIR/jtransforms-2.4.jar:$JARDIR/org.jzy3d-0.9.jar:$JARDIR/jogl-all.jar:$JARDIR/gluegen.jar:$JARDIR/gluegen-rt.jar:$JARDIR/opencsv-2.1.jar:$JARDIR/org.convexhull.jar:$CLASSPATH +CLASSPATH=$YLDIR/may.jar:$JARDIR/ayr.jar:$YETI_LIBDIR/yeti.jar:$YETI_LIBDIR/yeti-lib.jar:$JARDIR/jline-2.11-SNAPSHOT.jar:$JARDIR/jvamp.jar:$JARDIR/yertle.jar:$JARDIR/jtransforms-2.4.jar:$JARDIR/jzy3d-swt-0.9.1.jar:$JARDIR/jzy3d-api-0.9.1.jar:$JARDIR/jogl-all.jar:$JARDIR/gluegen.jar:$JARDIR/gluegen-rt.jar:$JARDIR/opencsv-2.1.jar:$JARDIR/org.convexhull.jar:$CLASSPATH YETI_MODULE_SOURCE_PATH=${YETI_LIBDIR}/modules \ LD_LIBRARY_PATH=$SODIR:$LD_LIBRARY_PATH \ diff -r eeebedb84ca7 -r ab85dfc45e3f bin/may.bat --- a/bin/may.bat Mon Mar 03 17:03:32 2014 +0000 +++ b/bin/may.bat Sat Apr 05 16:02:24 2014 +0100 @@ -1,3 +1,3 @@ @echo off set MAY_INIT_MODULES=may.vector;may.matrix;may.complex;may.plot;may.vamp -java -classpath "may.jar;ext\jar\ayr.jar;..\yeti\yeti.jar;ext\jar\jline-2.11-SNAPSHOT.jar;ext\jar\jvamp.jar;ext\jar\yertle.jar;ext\jar\jtransforms-2.4.jar;ext\jar\org.jzy3d-0.9.jar;ext\jar\jogl-all.jar;ext\jar\gluegen.jar;ext\jar\opencsv-2.1.jar;ext\jar\org.convexhull.jar" -Djava.library.path=ext\native\win64 com.particularprograms.ayr -init "%MAY_INIT_MODULES%" +java -classpath "may.jar;ext\jar\ayr.jar;..\yeti\yeti.jar;ext\jar\jline-2.11-SNAPSHOT.jar;ext\jar\jvamp.jar;ext\jar\yertle.jar;ext\jar\jtransforms-2.4.jar;ext\jar\jzy3d-swt-0.9.1.jar;ext\jar\jzy3d-api-0.9.1.jar;ext\jar\jogl-all.jar;ext\jar\gluegen.jar;ext\jar\opencsv-2.1.jar;ext\jar\org.convexhull.jar" -Djava.library.path=ext\native\win64 com.particularprograms.ayr -init "%MAY_INIT_MODULES%" diff -r eeebedb84ca7 -r ab85dfc45e3f bin/yc --- a/bin/yc Mon Mar 03 17:03:32 2014 +0000 +++ b/bin/yc Sat Apr 05 16:02:24 2014 +0100 @@ -34,7 +34,7 @@ shift fi -CLASSPATH=${YLJAR_WITH_COLON}$YETI_LIBDIR/yeti.jar:$YETI_LIBDIR/yeti-lib.jar:$JARDIR/jvamp.jar:$JARDIR/yertle.jar:$JARDIR/jtransforms-2.4.jar:$JARDIR/org.jzy3d-0.9.jar:$JARDIR/jogl-all.jar:$JARDIR/gluegen.jar:$JARDIR/gluegen-rt.jar:$JARDIR/misc/opencsv-2.1.jar:$JARDIR/misc/org.convexhull.jar:$JARDIR/misc/swt.jar:$CLASSPATH +CLASSPATH=${YLJAR_WITH_COLON}$YETI_LIBDIR/yeti.jar:$YETI_LIBDIR/yeti-lib.jar:$JARDIR/jvamp.jar:$JARDIR/yertle.jar:$JARDIR/jtransforms-2.4.jar:$JARDIR/jzy3d-swt-0.9.1.jar:$JARDIR/jzy3d-api-0.9.1.jar:$JARDIR/jogl-all.jar:$JARDIR/gluegen.jar:$JARDIR/gluegen-rt.jar:$JARDIR/misc/opencsv-2.1.jar:$JARDIR/misc/org.convexhull.jar:$JARDIR/misc/swt.jar:$CLASSPATH LD_LIBRARY_PATH=$SODIR:$LD_LIBRARY_PATH \ $JAVA_HOME/bin/java $JAVA_OPTS -classpath "$CLASSPATH" yeti.lang.compiler.yeti "$@" diff -r eeebedb84ca7 -r ab85dfc45e3f bin/yc.bat --- a/bin/yc.bat Mon Mar 03 17:03:32 2014 +0000 +++ b/bin/yc.bat Sat Apr 05 16:02:24 2014 +0100 @@ -1,2 +1,2 @@ -java -classpath "may.jar;..\yeti\yeti.jar;ext\jar\jline-2.11-SNAPSHOT.jar;ext\jar\jvamp.jar;ext\jar\yertle.jar;ext\jar\jtransforms-2.4.jar;ext\jar\org.jzy3d-0.9.jar;ext\jar\jogl-all.jar;ext\jar\gluegen.jar;ext\jar\opencsv-2.1.jar;ext\jar\org.convexhull.jar" -Djava.library.path=ext\native\win64 yeti.lang.compiler.yeti "%~1" +java -classpath "may.jar;..\yeti\yeti.jar;ext\jar\jline-2.11-SNAPSHOT.jar;ext\jar\jvamp.jar;ext\jar\yertle.jar;ext\jar\jtransforms-2.4.jar;ext\jar\jzy3d-swt-0.9.1.jar;ext\jar\jzy3d-api-0.9.1.jar;ext\jar\jogl-all.jar;ext\jar\gluegen.jar;ext\jar\opencsv-2.1.jar;ext\jar\org.convexhull.jar" -Djava.library.path=ext\native\win64 yeti.lang.compiler.yeti "%~1" diff -r eeebedb84ca7 -r ab85dfc45e3f build.xml --- a/build.xml Mon Mar 03 17:03:32 2014 +0000 +++ b/build.xml Sat Apr 05 16:02:24 2014 +0100 @@ -2,7 +2,7 @@ - + diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/bits/VectorBits.java --- a/src/may/bits/VectorBits.java Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/bits/VectorBits.java Sat Apr 05 16:02:24 2014 +0100 @@ -3,6 +3,15 @@ public class VectorBits { + public static void checkLengths(double[] v1, double[] v2) { + if (v1.length != v2.length) { + throw new IllegalArgumentException + ("Found vector of length " + v2.length + + ", but all so far in this arithmetic operation have had length " + + v1.length); + } + } + public static double sum(double[] v) { double tot = 0.0; int len = v.length; @@ -12,24 +21,37 @@ return tot; } - public static double[] multiply(double[] v1, double[] v2) { - int len = v1.length; - if (v2.length < len) { - len = v2.length; + public static void multiplyBy(double[] out, double[] in) { + checkLengths(out, in); + for (int i = 0; i < in.length && i < out.length; ++i) { + out[i] *= in[i]; } - double[] out = new double[len]; - for (int i = 0; i < len; ++i) { - out[i] = v1[i] * v2[i]; + for (int i = in.length; i < out.length; ++i) { + out[i] *= 0.0; } - return out; } - public static void addTo(double[] out, double[] in, int len) { - for (int i = 0; i < len; ++i) { + public static void divideBy(double[] out, double[] in) { + checkLengths(out, in); + for (int i = 0; i < in.length && i < out.length; ++i) { + out[i] /= in[i]; + } + } + + public static void addTo(double[] out, double[] in) { + checkLengths(out, in); + for (int i = 0; i < in.length && i < out.length; ++i) { out[i] += in[i]; } } + public static void subtractFrom(double[] out, double[] in) { + checkLengths(out, in); + for (int i = 0; i < in.length && i < out.length; ++i) { + out[i] -= in[i]; + } + } + public static double[] scaled(double[] v, double factor) { int len = v.length; double[] out = new double[v.length]; diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/complex.yeti --- a/src/may/complex.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/complex.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -2,7 +2,6 @@ module may.complex; vec = load may.vector; -mm = load may.mathmisc; import java.lang: ClassCastException; @@ -31,13 +30,16 @@ fi, String format() (f v r = - //!!! not right -- want to round to 1dp of exp notation if less than 1e-4 - (n = mm.round (v * 10000); - s = "\(n / 10000)"; + (s = if v == 0.0 then "0.0" + elif abs v >= 1000.0 or abs v < 0.01 then + String#format("%.1E", [v as ~Double]) + else + String#format("%4f", [v as ~Double]) + fi; if r then - (strPad ' ' (8 - strLength s) '') ^ s + (strPad ' ' (9 - strLength s) '') ^ s else - strPad ' ' 7 "\(s)i"; + strPad ' ' 8 "\(s)i"; fi); if imag < 0 then " \(f real true)-\(f (-imag) false)" diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/feature/specdiff.yeti --- a/src/may/feature/specdiff.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/feature/specdiff.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -3,7 +3,6 @@ fr = load may.stream.framer; ch = load may.stream.channels; -cm = load may.matrix.complex; load may.feature.feature; diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/mathmisc.yeti --- a/src/may/mathmisc.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/mathmisc.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -65,6 +65,9 @@ np x 1; fi; +random () = + Math#random(); + { gcd, factorial, @@ -76,5 +79,6 @@ log2, isPowerOfTwo, nextPowerOfTwo, + random, } diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/matrix.yeti --- a/src/may/matrix.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/matrix.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -40,8 +40,11 @@ module may.matrix; +{ ceil, floor, random } = load may.mathmisc; + vec = load may.vector; -mm = load may.mathmisc; + +load yeti.experimental.json; typedef opaque matrix_t = { size is { rows is number, columns is number }, @@ -361,7 +364,7 @@ }; constMatrix n = generate do row col: n done; -randomMatrix = generate do row col: Math#random() done; +randomMatrix = generate do row col: random () done; identityMatrix = constMatrix 1; transposed m = @@ -503,16 +506,22 @@ done (keys h)); newSparseMatching m1 entries); -sum' m1 m2 = - if (size m1) != (size m2) - then failWith "Matrices are not the same size: \(size m1), \(size m2)"; - elif isSparse? m1 and isSparse? m2 then - sparseSumOrDifference (+) m1 m2; - else - add2 v1 v2 = vec.add [v1,v2]; - denseLinearOp add2 m1 m2; - fi; - +sum' mm = + case mm of + m1::m2::rest: + sum' + (if (size m1) != (size m2) + then failWith "Matrices are not the same size: \(size m1), \(size m2)"; + elif isSparse? m1 and isSparse? m2 then + sparseSumOrDifference (+) m1 m2; + else + add2 v1 v2 = vec.add [v1,v2]; + denseLinearOp add2 m1 m2; + fi :: rest); + [m1]: m1; + _: failWith "Empty argument list"; + esac; + difference m1 m2 = if (size m1) != (size m2) then failWith "Matrices are not the same size: \(size m1), \(size m2)"; @@ -652,7 +661,7 @@ for [0..size.rows - 1] do i: row = getRow i m1; for [0..size.columns - 1] do j: - data[j][i] := vec.sum (vec.multiply row (getColumn j m2)); + data[j][i] := vec.sum (vec.multiply [row, getColumn j m2]); done; done; newMatrix size (Columns (array (map vec.vector (list data))))); @@ -675,11 +684,52 @@ fi; fi; -entryWiseProduct m1 m2 = // or element-wise, or Hadamard product -//!!! todo: faster, sparse version, unit tests +entryWiseProduct mm = + case mm of + m1::m2::rest: + entryWiseProduct + (if (size m1) != (size m2) + then failWith "Matrices are not the same size: \(size m1), \(size m2)"; + else + if isSparse? m1 then + newSparse (size m1) + ((taggerForTypeOf m1) + (map do { i, j, v }: { i, j, v = v * (at' m2 i j) } done + (enumerateSparse m1))) + elif isSparse? m2 then + entryWiseProduct (m2::m1::rest) + else + if isRowMajor? m1 then + fromRows (array (map2 do v1 v2: vec.multiply [v1,v2] done + (asRows m1) (asRows m2))); + else + fromColumns (array (map2 do v1 v2: vec.multiply [v1,v2] done + (asColumns m1) (asColumns m2))); + fi + fi + fi :: rest); + [m1]: m1; + _: failWith "Empty argument list"; + esac; + +entryWiseDivide m1 m2 = if (size m1) != (size m2) then failWith "Matrices are not the same size: \(size m1), \(size m2)"; - else generate do row col: at' m1 row col * at' m2 row col done (size m1); + else + if isSparse? m1 then + newSparse (size m1) + ((taggerForTypeOf m1) + (map do { i, j, v }: { i, j, v = v / (at' m2 i j) } done + (enumerateSparse m1))) + // For m2 to be sparse makes no sense (divide by zero all over + // the shop). + else + if isRowMajor? m1 then + fromRows (array (map2 vec.divide (asRows m1) (asRows m2))); + else + fromColumns (array (map2 vec.divide (asColumns m1) (asColumns m2))); + fi + fi fi; concatAgainstGrain tagger getter counter mm = @@ -691,11 +741,10 @@ concatWithGrain tagger getter counter mm = tagger (array - (concat - (map do m: - n = counter (size m); - map do i: getter i m done [0..n-1] - done mm))); + (concatMap do m: + n = counter (size m); + map do i: getter i m done [0..n-1] + done mm)); sparseConcat direction first mm = (dimension d f = if direction == d then sum (map f mm) else f first fi; @@ -850,18 +899,71 @@ fi fi); +//!!! doc: always dense +repeatedHorizontal n m = + (if n == 1 then m + else + cols = asColumns m; + fromColumns (fold do acc _: acc ++ cols done [] [1..n]) + fi); + +//!!! doc: always dense +repeatedVertical n m = + (if n == 1 then m + else + rows = asRows m; + fromRows (fold do acc _: acc ++ rows done [] [1..n]) + fi); + +//!!! doc: always dense +tiledTo newsize m = + if newsize == size m then + m + elif (height m) == 0 or (width m) == 0 then + zeroMatrixWithTypeOf m newsize; + else + h = ceil (newsize.columns / (width m)); + v = ceil (newsize.rows / (height m)); + if isRowMajor? m then + resizedTo newsize (repeatedHorizontal h (repeatedVertical v m)) + else + resizedTo newsize (repeatedVertical v (repeatedHorizontal h m)) + fi + fi; + minValue m = if width m == 0 or height m == 0 then 0 - else + elif isSparse? m then minv ll = fold min (head ll) (tail ll); - minv (map (.v) (enumerate m)); + minnz = minv (map (.v) (enumerate m)); + if minnz > 0 and nonZeroValues m < (width m * height m) then 0 + else minnz fi; + elif isRowMajor? m then + vec.min (vec.fromList (map vec.min (asRows m))); + else + vec.min (vec.fromList (map vec.min (asColumns m))); fi; maxValue m = if width m == 0 or height m == 0 then 0 - else + elif isSparse? m then maxv ll = fold max (head ll) (tail ll); - maxv (map (.v) (enumerate m)); + maxnz = maxv (map (.v) (enumerate m)); + if maxnz < 0 and nonZeroValues m < (width m * height m) then 0 + else maxnz fi; + elif isRowMajor? m then + vec.max (vec.fromList (map vec.max (asRows m))); + else + vec.max (vec.fromList (map vec.max (asColumns m))); + fi; + +total m = + if isSparse? m then + fold (+) 0 (map (.v) (enumerateSparse m)); + elif isRowMajor? m then + fold (+) 0 (map vec.sum (asRows m)); + else + fold (+) 0 (map vec.sum (asColumns m)); fi; mapRows rf m = @@ -880,21 +982,23 @@ [ "\nColumns \(c0) to \(c1)\n", (map do row: map do v: - //!!! not right -- want to round to 1dp of exp notation if less than 1e-4 - n = mm.round (v * 100000); - strPad ' ' 10 "\(n / 100000)"; + strPad ' ' 10 + (if v == 0 then "0.0" + elif abs v >= 1000.0 or abs v < 0.01 then + String#format("%.2E", [v as ~Double]) + else + String#format("%5f", [v as ~Double]) + fi); done (vec.list row) |> strJoin ""; done (asRows (columnSlice m c0 (c1 + 1))) |> strJoin "\n") ]; - done [0..width m / chunk - 1] |> concat); + done [0..floor(width m / chunk)] |> concat); print' = println . format; eprint' = eprintln . format; -//!!! todo: look at all occurrences of matrix (and complexmatrix) -// construction and make sure we have good apis for those use cases. -// in particular, constructing from literal data (list>) -// is much too hard at the moment. +json m = + jsonList (map do r: jsonList (map jsonNum (vec.list r)) done (asRows m)); { size, @@ -922,9 +1026,9 @@ toSparse, toDense, scaled, - resizedTo, minValue, maxValue, + total, asRows, asColumns, sum = sum', @@ -936,6 +1040,11 @@ any = any', product, entryWiseProduct, + entryWiseDivide, + resizedTo, + tiledTo, + repeatedHorizontal, + repeatedVertical, concatHorizontal, concatVertical, rowSlice, @@ -953,6 +1062,7 @@ format, print = print', eprint = eprint', + json, } as { @@ -981,12 +1091,12 @@ toSparse is matrix_t -> matrix_t, toDense is matrix_t -> matrix_t, scaled is number -> matrix_t -> matrix_t, - resizedTo is { rows is number, columns is number } -> matrix_t -> matrix_t, minValue is matrix_t -> number, maxValue is matrix_t -> number, + total is matrix_t -> number, asRows is matrix_t -> list, asColumns is matrix_t -> list, - sum is matrix_t -> matrix_t -> matrix_t, + sum is list? -> matrix_t, difference is matrix_t -> matrix_t -> matrix_t, abs is matrix_t -> matrix_t, negative is matrix_t -> matrix_t, @@ -994,7 +1104,12 @@ all is (number -> boolean) -> matrix_t -> boolean, any is (number -> boolean) -> matrix_t -> boolean, product is matrix_t -> matrix_t -> matrix_t, - entryWiseProduct is matrix_t -> matrix_t -> matrix_t, + entryWiseProduct is list? -> matrix_t, + entryWiseDivide is matrix_t -> matrix_t -> matrix_t, + resizedTo is { rows is number, columns is number } -> matrix_t -> matrix_t, + tiledTo is { rows is number, columns is number } -> matrix_t -> matrix_t, + repeatedHorizontal is number -> matrix_t -> matrix_t, + repeatedVertical is number -> matrix_t -> matrix_t, concatHorizontal is list -> matrix_t, concatVertical is list -> matrix_t, rowSlice is matrix_t -> number -> number -> matrix_t, @@ -1012,5 +1127,6 @@ format is matrix_t -> string, print is matrix_t -> (), eprint is matrix_t -> (), + json is matrix_t -> json, } diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/matrix/complex.yeti --- a/src/may/matrix/complex.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/matrix/complex.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -5,6 +5,8 @@ vec = load may.vector; cpx = load may.complex; +{ floor } = load may.mathmisc; + typedef opaque complexmatrix_t = { size is { rows is number, columns is number }, real is Some mat.matrix_t | None (), @@ -204,7 +206,7 @@ case p1 of Some m1: case p2 of - Some m2: Some (mat.sum m1 m2); + Some m2: Some (mat.sum [m1, m2]); none: Some m1; esac; none: @@ -239,16 +241,22 @@ none; esac; -sum c1 c2 = - (a = c1.real; - b = c1.imaginary; - c = c2.real; - d = c2.imaginary; - { - size = c1.size, - real = addParts a c, - imaginary = addParts b d, - }); +sum cc = + case cc of + c1::c2::rest: + sum + (a = c1.real; + b = c1.imaginary; + c = c2.real; + d = c2.imaginary; + { + size = c1.size, + real = addParts a c, + imaginary = addParts b d, + } :: rest); + [c1]: c1; + _: failWith "Empty argument list"; + esac; product c1 c2 = (a = c1.real; @@ -305,7 +313,7 @@ (map do row: map cpx.format row |> strJoin ""; done (asRows (columnSlice m c0 (c1 + 1))) |> strJoin "\n") ]; - done [0..width m / chunk - 1] |> concat); + done [0..floor(width m / chunk)] |> concat); print' = println . format; eprint' = eprintln . format; @@ -374,7 +382,7 @@ toSparse is complexmatrix_t -> complexmatrix_t, toDense is complexmatrix_t -> complexmatrix_t, scaled is number -> complexmatrix_t -> complexmatrix_t, - sum is complexmatrix_t -> complexmatrix_t -> complexmatrix_t, + sum is list? -> complexmatrix_t, difference is complexmatrix_t -> complexmatrix_t -> complexmatrix_t, abs is complexmatrix_t -> mat.matrix_t, product is complexmatrix_t -> complexmatrix_t -> complexmatrix_t, diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/matrix/test/speedtest.yeti --- a/src/may/matrix/test/speedtest.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/matrix/test/speedtest.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -127,30 +127,30 @@ println ""; print "CMD + CMD... "; -reportOn (time \(mat.sum cmd cmd)); +reportOn (time \(mat.sum [cmd, cmd])); print "CMD + RMD... "; -reportOn (time \(mat.sum cmd rmd)); +reportOn (time \(mat.sum [cmd, rmd])); print "RMD + CMD... "; -reportOn (time \(mat.sum rmd cmd)); +reportOn (time \(mat.sum [rmd, cmd])); print "RMD + RMD... "; -reportOn (time \(mat.sum rmd rmd)); +reportOn (time \(mat.sum [rmd, rmd])); println ""; print "CMS + CMS... "; -reportOn (time \(mat.sum cms cms)); +reportOn (time \(mat.sum [cms, cms])); print "CMS + RMS... "; -reportOn (time \(mat.sum cms rms)); +reportOn (time \(mat.sum [cms, rms])); print "RMS + CMS... "; -reportOn (time \(mat.sum rms cms)); +reportOn (time \(mat.sum [rms, cms])); print "RMS + RMS... "; -reportOn (time \(mat.sum rms rms)); +reportOn (time \(mat.sum [rms, rms])); println ""; @@ -204,15 +204,15 @@ println ""; print "CMS + CMS... "; -reportOn (time \(mat.sum cms cms)); +reportOn (time \(mat.sum [cms, cms])); print "CMS + RMS... "; -reportOn (time \(mat.sum cms rms)); +reportOn (time \(mat.sum [cms, rms])); print "RMS + CMS... "; -reportOn (time \(mat.sum rms cms)); +reportOn (time \(mat.sum [rms, cms])); print "RMS + RMS... "; -reportOn (time \(mat.sum rms rms)); +reportOn (time \(mat.sum [rms, rms])); (); diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/matrix/test/test_matrix.yeti --- a/src/may/matrix/test/test_matrix.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/matrix/test/test_matrix.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -227,17 +227,45 @@ compareMatrices (mat.mapColumns (vec.scaled 2) m') m'' ), +"minValue-\(name)": \( + compare (mat.minValue (fromRows [[1,2],[3,4],[5,-1]])) (-1) and + compare (mat.minValue (fromRows [[1,2],[3,0],[5,-1]])) (-1) and + compare (mat.minValue (fromRows [[1,2],[3,0],[5,1]])) 0 and + compare (mat.minValue (fromRows [[],[],[]])) 0 +), + +"maxValue-\(name)": \( + compare (mat.maxValue (fromRows [[1,2],[3,4],[5,-1]])) 5 and + compare (mat.maxValue (fromRows [[1,2],[3,0],[5,-1]])) 5 and + compare (mat.maxValue (fromRows [[-1,-2],[-3,0],[-5,-1]])) 0 and + compare (mat.maxValue (fromRows [[-1,-2],[-3,-4],[-5,-1]])) (-1) and + compare (mat.maxValue (fromRows [[],[],[]])) 0 +), + +"total-\(name)": \( + compare (mat.total (fromRows [[1,2],[3,4],[5,-1]])) 14 and + compare (mat.total (fromRows [[1,2],[3,0],[5,-1]])) 10 and + compare (mat.total (fromRows [[-1,-2],[-3,0],[-5,-1]])) (-12) and + compare (mat.total (fromRows [[-1,-2],[-3,-4],[-5,-1]])) (-16) and + compare (mat.total (fromRows [[],[],[]])) 0 +), + "sum-\(name)": \( compareMatrices - (mat.sum (constMatrix 2 { rows = 3, columns = 4 }) - (constMatrix 1 { rows = 3, columns = 4 })) - (constMatrix 3 { rows = 3, columns = 4 }) + (mat.sum [constMatrix 2 { rows = 3, columns = 4 }, + constMatrix 1 { rows = 3, columns = 4 }]) + (constMatrix 3 { rows = 3, columns = 4 }) and + compareMatrices + (mat.sum [constMatrix 2 { rows = 3, columns = 4 }, + constMatrix 1 { rows = 3, columns = 4 }, + fromRows [[1,2,3,4],[5,6,7,8],[9,10,11,12]]]) + (fromRows [[4,5,6,7],[8,9,10,11],[12,13,14,15]]) ), "sumFail-\(name)": \( try - \() (mat.sum (constMatrix 2 { rows = 3, columns = 4 }) - (constMatrix 1 { rows = 3, columns = 5 })); + \() (mat.sum [constMatrix 2 { rows = 3, columns = 4 }, + constMatrix 1 { rows = 3, columns = 5 }]); false; catch FailureException e: true @@ -257,11 +285,11 @@ { i = 1, j = 0, v = 5 }, { i = 1, j = 1, v = -4 }, // NB this means [1,1] -> 0, sparse zero ]); - tot = mat.sum s t; + tot = mat.sum [s, t]; mat.isSparse? tot and - compareMatrices tot (mat.sum (mat.toDense s) t) and - compareMatrices tot (mat.sum (mat.toDense s) (mat.toDense t)) and - compareMatrices tot (mat.sum s (mat.toDense t)) and + compareMatrices tot (mat.sum [mat.toDense s, t]) and + compareMatrices tot (mat.sum [mat.toDense s, mat.toDense t]) and + compareMatrices tot (mat.sum [s, mat.toDense t]) and compareMatrices tot (mat.newSparseMatrix { rows = 2, columns = 3 } (Rows [ { i = 0, j = 0, v = 1 }, @@ -332,6 +360,32 @@ (fromColumns [[58,139],[64,154]]) ), +"entryWiseProduct-\(name)": \( + compareMatrices + (mat.entryWiseProduct + [fromRows [[1,2,3],[4,5,0]], + fromRows [[6,7,8],[0,1,2]]]) + (fromRows [[6,14,24],[0,5,0]]) and + compareMatrices + (mat.entryWiseProduct + [fromRows [[1,2,3],[4,5,0]], + fromColumns [[6,0],[7,1],[8,2]]]) + (fromRows [[6,14,24],[0,5,0]]) +), + +"entryWiseDivide-\(name)": \( + compareMatrices + (mat.entryWiseDivide + (fromRows [[1,2,3],[4,5,0]]) + (fromRows [[6,7,8],[1,2,3]])) + (fromRows [[1/6,2/7,3/8],[4,5/2,0]]) and + compareMatrices + (mat.entryWiseDivide + (fromRows [[1,2,3],[4,5,0]]) + (fromColumns [[6,1],[7,2],[8,3]])) + (fromRows [[1/6,2/7,3/8],[4,5/2,0]]); +), + "sparseProduct-\(name)": \( s = mat.newSparseMatrix { rows = 2, columns = 3 } (Columns [ { i = 0, j = 0, v = 1 }, @@ -388,6 +442,33 @@ (mat.toSparse (fromColumns [[1,4],[2,5],[3,6]]))) ), +"tiledTo-\(name)": \( + compareMatrices + (mat.tiledTo { rows = 2, columns = 2 } + (fromColumns [[1,4],[2,5],[3,6]])) + (fromColumns [[1,4],[2,5]]) and + compareMatrices + (mat.tiledTo { rows = 3, columns = 4 } + (fromColumns [[1,4],[2,5],[3,6]])) + (fromColumns [[1,4,1],[2,5,2],[3,6,3],[1,4,1]]) and + compareMatrices + (mat.tiledTo { rows = 7, columns = 2 } + (fromColumns [[1,4],[2,5],[3,6]])) + (fromColumns [[1,4,1,4,1,4,1],[2,5,2,5,2,5,2]]) and + compareMatrices + (mat.tiledTo { rows = 1, columns = 1 } + (fromColumns [[1,4],[2,5],[3,6]])) + (fromRows [[1]]) and + compareMatrices + (mat.tiledTo { rows = 2, columns = 3 } + (mat.zeroMatrix { rows = 0, columns = 0 })) + (fromRows [[0,0,0],[0,0,0]]) and + compareMatrices + (mat.tiledTo { rows = 0, columns = 0 } + (mat.zeroMatrix { rows = 4, columns = 3 })) + (fromRows []) +), + "zeroSizeMatrix-\(name)": \( compareMatrices (mat.zeroMatrix { rows = 0, columns = 0 }) @@ -419,6 +500,36 @@ [[1,4],[0,5],[3,6]]; ), +"repeated-horiz-\(name)": \( + compareMatrices + (mat.repeatedHorizontal 2 (fromColumns [[1,4],[0,5]])) + (fromColumns [[1,4],[0,5],[1,4],[0,5]]) and + compareMatrices + (mat.repeatedHorizontal 2 (fromRows [[1,0],[4,5]])) + (fromColumns [[1,4],[0,5],[1,4],[0,5]]) and + compareMatrices + (mat.repeatedHorizontal 0 (fromColumns [[1,4],[0,5]])) + (fromColumns []) and + compareMatrices + (mat.repeatedHorizontal 4 (fromColumns [[]])) + (fromColumns [[],[],[],[]]) +), + +"repeated-vert-\(name)": \( + compareMatrices + (mat.repeatedVertical 2 (fromRows [[1,4],[0,5]])) + (fromRows [[1,4],[0,5],[1,4],[0,5]]) and + compareMatrices + (mat.repeatedVertical 2 (fromColumns [[1,0],[4,5]])) + (fromRows [[1,4],[0,5],[1,4],[0,5]]) and + compareMatrices + (mat.repeatedVertical 0 (fromRows [[1,4],[0,5]])) + (fromRows []) and + compareMatrices + (mat.repeatedVertical 4 (fromRows [[]])) + (fromRows [[],[],[],[]]) +), + "concat-horiz-\(name)": \( compareMatrices (mat.concatHorizontal diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/plot.yeti --- a/src/may/plot.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/plot.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -19,6 +19,8 @@ import org.jzy3d.plot3d.primitives.axes: ContourAxeBox; import org.jzy3d.contour: DefaultContourColoringPolicy; import org.jzy3d.contour: MapperContourPictureGenerator; +import org.jzy3d.chart.factories.AWTChartComponentFactory; +import org.jzy3d.plot3d.text.drawable: DrawableTextBillboard, DrawableTextBitmap; import javax.imageio: ImageIO; @@ -69,7 +71,7 @@ //!!! doesn't work if either rows or columns is 1 xrange = new Range(0, size.columns - 1); yrange = new Range(0, size.rows - 1); - grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows); +// grid = new OrthonormalGrid(xrange, size.columns, yrange, size.rows); eprintln "Matrix size: \(size)"; // surface = Builder#buildOrthonormal(grid, mapper); //??? big? // println "Z Bounds: \(surface#getBounds()#getZmin()) -> \(surface#getBounds()#getZmax())"; @@ -128,6 +130,11 @@ */ ()); +addCaption chart colour caption is ~Chart -> ~Color -> string -> () = + (scene = chart#getScene(); + text = new DrawableTextBitmap(caption, new Coord3d(0, 0, 0), colour); + scene#add(text)); + /** * Plot a list of typed structures onto a single chart, and return the * resulting Chart object. @@ -138,11 +145,14 @@ plot structures = (quality = Quality#Nicest; quality#setAnimated(false); - chart = new Chart(quality); + chart = AWTChartComponentFactory#chart(quality, "awt"); var j = 0; for structures do s: colour = distinctColour j; case s of + Caption caption: + addCaption chart (new Color(0, 0, 0)) caption; + j := j - 1; // doesn't count as a distinct structure Grid matrix: plotMatrix chart colour matrix; Contour matrix: diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/plot/chart.yeti --- a/src/may/plot/chart.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/plot/chart.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -77,7 +77,8 @@ \() ChartLauncher#openStaticChart(chart); fi; if opts.saveTo != "" then - \() chart#screenshot(opts.saveTo); + outfile = new File(opts.saveTo); + \() chart#screenshot(outfile); fi); plotBarChart options values = diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/signal/window.yeti --- a/src/may/signal/window.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/signal/window.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -208,19 +208,19 @@ //!!! doc note: recalculates window every time! not fast for multiple blocks, consider windowedFrames then windowed windowFunc data = - vec.multiply (windowFunc (vec.length data)) data; + vec.multiply [windowFunc (vec.length data), data]; windowedRows windowFunc matrix = (w = windowFunc (mat.width matrix); mat.fromRows - (map (vec.multiply w) (mat.asRows matrix))); + (map do v: vec.multiply [v,w] done (mat.asRows matrix))); windowedFrames windowFunc frames = case frames of []: frames; _: (first = head frames; window = windowFunc (vec.length first); - map (vec.multiply window) frames); + map do v: vec.multiply [v,window] done frames); esac; { diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/convolve.yeti --- a/src/may/stream/convolve.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/convolve.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -19,6 +19,7 @@ // framesize*2, in which each frame contains all channels one // after another (not interleaved) [because we lack a complex // matrix type] + //!!! we now have a complex matrix type, revisit this (forwardTransform = fft.realForward (framesize * 2); do stream: padded = @@ -46,29 +47,36 @@ else parts.fst :: splitInto n parts.snd; fi); -fastConvolvedWith ir framesize s = +fastConvolvedWith ir framesize = // prerequisite: ir and s have same number of channels (framer = zeroPaddedFreqFrames framesize (mat.height ir); - ch = s.channels; + ch = mat.height ir; irfr = framer (syn.precalculated 1 ir); // rate arg is irrelevant here - sigfr = framer s; inverseTransform = fft.realInverse (framesize * 2); - extended = sigfr ++ - map do _: list (cplx.zeros (ch * (framesize + 1))) done - [1..length irfr-1]; - cframes = doFastConvolve irfr extended; - rframes = (mat.zeroMatrix { rows = ch, columns = framesize * 2}) :: - map do fr: - mat.fromRows - (map inverseTransform (splitInto (framesize+1) fr)) - done cframes; - fr.streamed s.sampleRate (framesize * 2) - [ Window win.boxcar, Hop framesize ] - rframes; + do s: + if ch != s.channels then + failWith "Signal stream and IR must have same number of channels (\(s.channels) != \(ch))" + fi; + sigfr = framer s; + extended = sigfr ++ + map do _: list (cplx.zeros (ch * (framesize + 1))) done + [1..length irfr-1]; + cframes = doFastConvolve irfr extended; + rframes = + (mat.toRowMajor (mat.zeroMatrix { rows = ch, columns = framesize * 2})) :: + map do fr: + mat.fromRows + (map inverseTransform (splitInto (framesize+1) fr)) + done cframes; + fr.streamed s.sampleRate (framesize * 2) + [ Window win.boxcar, Hop framesize ] + rframes; + done; ); plainConvolvedWith ir s = // prerequisite: ir and s have same number of channels + //!!! test that and throw (var history = mat.toRowMajor (mat.zeroMatrix { rows = s.channels, columns = mat.width ir - 1 }); s with @@ -127,23 +135,21 @@ fi; nextPowerOfTwo' 1 n); -//!!! todo: partial application so as to preprocess ir (in fast convolution case) -convolvedWith options ir s = //!!! cheap mono thing here +//!!! doc note: this supports partial application (it returns a function that +// expects the signal) so as to pre-process the IR +convolvedWith options ir = //!!! cheap mono thing here //!!! doc note: fast convolution output is padded to next frame boundary - (if mat.height ir != s.channels then - failWith "Signal stream and IR must have same number of channels (\(s.channels) != \(mat.height ir))" - fi; - var type = Fast (); + (var type = Fast (); var framesize = nextPowerOfTwo (mat.width ir); for options \case of - Fast s: if s then type := Fast () else type := Plain () fi; + Fast f: if f then type := Fast () else type := Plain () fi; Framesize n: framesize := nextPowerOfTwo n; esac; case type of Fast (): - fastConvolvedWith ir framesize s; + fastConvolvedWith ir framesize; Plain (): - plainConvolvedWith ir s; + plainConvolvedWith ir; esac); { diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/filter.yeti --- a/src/may/stream/filter.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/filter.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -27,7 +27,7 @@ else win.dirac filterLength; fi; fi; - filter = vec.multiply idealBandpass kw; + filter = vec.multiply [idealBandpass, kw]; filtered = convolve.convolvedWith [Framesize 1024] (mat.fromRows (map \filter [1..s.channels])) s; diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/framer.yeti --- a/src/may/stream/framer.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/framer.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -70,7 +70,8 @@ else mat.asRows (mat.concatHorizontal - [mat.zeroMatrix { rows = stream.channels, columns = hop }, + [mat.toRowMajor + (mat.zeroMatrix { rows = stream.channels, columns = hop }), stream.read (framesize - hop)]); fi; overlappingBlockList framesize hop stream framesize initialBuffer; @@ -85,7 +86,8 @@ Hop h: hop := h; Padded p: padded := p; Window w: - windower := mat.mapRows (vec.multiply (w framesize)); + (window = w framesize; + windower := mat.mapRows (do v: vec.multiply [v, window] done)); FrequencyDomain f: if f then transform := mat.mapRows (fft.realForwardMagnitude framesize); @@ -105,7 +107,8 @@ Hop h: hop := h; Padded p: padded := p; Window w: - windower := mat.mapRows (vec.multiply (w framesize)); + (window = w framesize; + windower := mat.mapRows (do v: vec.multiply [v, window] done)); FrequencyDomain f: //!!! what if both f and not-f provided in one options list? need reset if f then @@ -122,7 +125,8 @@ (var remaining = frames; var position = 0; channels = mat.height (head frames); // so we don't need to keep a head ptr - var buffered = mat.zeroMatrix { rows = channels, columns = 0 }; + var buffered = mat.toRowMajor + (mat.zeroMatrix { rows = channels, columns = 0 }); { get position () = position, get channels () = channels, @@ -159,9 +163,10 @@ first::rest: (w = mat.width pending; pre = mat.columnSlice pending 0 (w - overlap); - added = mat.sum first - (mat.resizedTo (mat.size first) - (mat.columnSlice pending (w - overlap) w)); + added = mat.sum + [first, + (mat.resizedTo (mat.size first) + (mat.columnSlice pending (w - overlap) w))]; ola rest added (pre::acc)); _: reverse (pending::acc); @@ -170,7 +175,7 @@ first::rest: mat.concatHorizontal (ola rest first []); _: - mat.zeroMatrix { rows = 0, columns = 0 }; + mat.toRowMajor (mat.zeroMatrix { rows = 0, columns = 0 }); esac); streamOverlapping rate { framesize, hop, window } frames = @@ -181,7 +186,8 @@ w = vec.scaled factor (window framesize); channels = mat.height (head frames); // so we don't need to keep a head ptr - var buffered = mat.zeroMatrix { rows = channels, columns = 0 }; + var buffered = + mat.toRowMajor (mat.zeroMatrix { rows = channels, columns = 0 }); syncd = synchronized remaining; @@ -193,7 +199,7 @@ reverse acc else this = mat.resizedTo { columns = framesize, rows = channels } - (mat.mapRows (vec.multiply w) (head remaining)); + (mat.mapRows do v: vec.multiply [v,w] done (head remaining)); remaining := tail remaining; framesFor (samples - hop) (this::acc) fi; diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/manipulate.yeti --- a/src/may/stream/manipulate.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/manipulate.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -41,7 +41,8 @@ if not s.finished? then mat.resizedTo { rows = s.channels, columns = n } (s.read n); else - mat.zeroMatrix { columns = n, rows = s.channels } + mat.toRowMajor + (mat.zeroMatrix { columns = n, rows = s.channels }); fi), }); @@ -49,7 +50,7 @@ (var prepos = 0; zeros n = mat.toRowMajor (prepos := prepos + n; - mat.zeroMatrix { rows = s.channels, columns = n }); + mat.toRowMajor (mat.zeroMatrix { rows = s.channels, columns = n })); delay = if nsamples < 0 then \0 (s.read (-nsamples)); @@ -105,7 +106,9 @@ pos := pos + zc; toAdd := toAdd - zc; mat.concatHorizontal - [r, mat.zeroMatrix { rows = s.channels, columns = zc }]; + [r, + mat.toRowMajor + (mat.zeroMatrix { rows = s.channels, columns = zc })]; fi), close = s.close }); @@ -149,7 +152,7 @@ if sz.columns == 0 then mat.zeroMatrix sz else - mat.sum (mat.resizedTo sz m1) (mat.resizedTo sz m2); + mat.sum [mat.resizedTo sz m1, mat.resizedTo sz m2]; fi); channels = head (sortBy (>) (map (.channels) streams)); { @@ -214,7 +217,8 @@ if s.available == Infinite () then s else var pos = 0; - var cache = mat.zeroMatrix { rows = s.channels, columns = 0 }; + var cache = mat.toRowMajor + (mat.zeroMatrix { rows = s.channels, columns = 0 }); chunks = array []; cachedPartsFor count = (start = pos % (mat.width cache); @@ -262,6 +266,7 @@ duplicated copies s = //!!! doc fact that original s cannot be used independently of this afterward // (so maybe name is misleading?) +//!!! also of course "duplicated" means two of them, while we could have lots array if copies < 2 then map \s [0..copies-1]; else pos = array (map \0 [0..copies-1]); @@ -376,7 +381,7 @@ read n = (m = s.read (n*frac); obtained = maths.ceil ((mat.width m) / frac); - mat.flipped + mat.toRowMajor (mat.fromColumns (map do c: mat.getColumn (c*frac) m done [0..obtained-1]))) }; diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/playback.yeti --- a/src/may/stream/playback.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/playback.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -5,6 +5,7 @@ mat = load may.matrix; af = load may.stream.audiofile; ch = load may.stream.channels; +manip = load may.stream.manipulate; import javax.sound.sampled: AudioSystem, AudioFormat, AudioFormat$Encoding, SourceDataLine; @@ -45,18 +46,26 @@ line.close()); playStream stream = - (line = open { rate = stream.sampleRate, channels = stream.channels }; - blocksize = 10240; - not stream.finished? loop - line.play [(ch.mixedAndInterleavedTo line.channels - (stream.read blocksize))]; - line.close(); - stream.close()); + case stream.available of + Infinite (): + failWith "Refusing to play infinite stream. Use playStreamFor to limit playback duration"; + _: + line = open { rate = stream.sampleRate, channels = stream.channels }; + blocksize = 10240; + not stream.finished? loop + line.play [(ch.mixedAndInterleavedTo line.channels + (stream.read blocksize))]; + line.close(); + stream.close(); + esac; + +playStreamFor seconds stream = + playStream (manip.withDuration (seconds * stream.sampleRate) stream); playFile filename = playStream (af.open filename); { - open, playMatrix, playStream, playFile, + open, playMatrix, playStream, playStreamFor, playFile, } diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/resample.yeti --- a/src/may/stream/resample.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/resample.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -25,7 +25,7 @@ length = if length > maxlength then maxlength else length fi; kw = win.kaiser (pp with { length }); sw = win.sinc (n*2) length; - vec.multiply sw kw); + vec.multiply [sw, kw]); durationAdjusterFor factor s = case s.available of @@ -297,8 +297,9 @@ pd = phaseData[phase]; for [0..s.channels-1] do ch: +// println "lengths \(vec.length (mat.getRow ch buffer)) and \(vec.length pd.filter)"; rowdata[ch][i] := vec.sum - (vec.multiply (mat.getRow ch buffer) pd.filter); + (vec.multiply [mat.getRow ch buffer, pd.filter]); done; buffer := mat.concatHorizontal @@ -335,7 +336,10 @@ fi) }); -resampledTo = resampledDirectlyTo; +resampledTo targetRate s = + if s.sampleRate == targetRate then s + else resampledDirectlyTo targetRate s; + fi; { kaiserSincFilterFor, diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/syntheticstream.yeti --- a/src/may/stream/syntheticstream.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/syntheticstream.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -26,25 +26,22 @@ close = \(), }); -sinusoid rate freq = - generated rate (sin . (* (2 * pi * freq / rate))); +sinusoid sampleRate freq = + generated sampleRate (sin . (* (2 * pi * freq / sampleRate))); -whiteNoise rate = - generated rate \((Math#random() * 2.0) - 1.0); +whiteNoise sampleRate = + generated sampleRate \((Math#random() * 2.0) - 1.0); -pulseTrain rate freq = - generated rate do n: if n % int (rate / freq) == 0 then 1 else 0 fi done; +silent sampleRate = + generated sampleRate \0; -silent rate = - generated rate \0; - -precalculatedMono rate data = +precalculatedMono sampleRate data = (n = vec.length data; var position = 0; { get position () = position, get channels () = 1, - get sampleRate () = rate, + get sampleRate () = sampleRate, get available () = Known (n - position), get finished? () = not (n > position), read count = ch.deinterleaved 1 @@ -55,14 +52,14 @@ close = \(), }); -precalculated rate data = +precalculated sampleRate data = (n = mat.width data; c = mat.height data; var position = 0; { get position () = position, get channels () = c, - get sampleRate () = rate, + get sampleRate () = sampleRate, get available () = Known (n - position), get finished? () = not (n > position), read count = @@ -73,7 +70,7 @@ close = \(), }); -precalculatedRepeated rate data = +precalculatedRepeated sampleRate data = (n = mat.width data; c = mat.height data; chunks count pos = @@ -89,7 +86,7 @@ { get position () = position, get channels () = c, - get sampleRate () = rate, + get sampleRate () = sampleRate, get available () = Infinite (), get finished? () = false, read count = @@ -99,11 +96,11 @@ close = \(), }); -empty rate channels = // degenerate stream with no data in it, occasionally useful +empty sampleRate channels = // degenerate stream with no data in it, occasionally useful { get position () = 0, get channels () = channels, - get sampleRate () = rate, + get sampleRate () = sampleRate, get available () = Known 0, get finished? () = true, read count = mat.zeroMatrix { rows = channels, columns = 0 }, @@ -117,7 +114,6 @@ precalculatedRepeated is number -> mat.matrix_t -> stream_t, sinusoid is number -> number -> stream_t, whiteNoise is number -> stream_t, - pulseTrain is number -> number -> stream_t, silent is number -> stream_t, empty is number -> number -> stream_t, } diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/test/test_framer.yeti --- a/src/may/stream/test/test_framer.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/test/test_framer.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -5,6 +5,7 @@ vec = load may.vector; mat = load may.matrix; syn = load may.stream.syntheticstream; +win = load may.signal.window; { compare, compareUsing } = load may.test; @@ -12,6 +13,30 @@ testStream n is number -> 'a = syn.precalculatedMono rate (vec.fromList [1..n]); +expectedStream framesize opts n = + (var padded = true; + var hop = framesize; + for opts \case of + Padded p: padded := p; + Hop h: hop := h; + _: (); + esac; + if padded or (hop == framesize) then + testStream n + else + // Unpadded streams can't be perfectly reconstructed from + // windowed frames: the first framesize-hop samples will + // remain windowed (and scaled by the overlap-add) + m = framesize - hop; + factor = hop / (framesize/2); + w = vec.scaled factor (win.hann framesize); + syn.precalculatedMono rate + (vec.concat [ + vec.multiply [vec.fromList [1..m], vec.slice w 0 m], + vec.fromList [m+1..n] + ]) + fi); + compareFrames frames1 frames2 = all id (map2 do f1 f2: compareUsing mat.equal f1 f2 done frames1 (map (mat.newRowVector . vec.fromList) frames2)); @@ -19,7 +44,7 @@ testFramesInvertibleWith framesize opts length expected firstChunkSize = (f = fr.frames framesize opts (testStream length); str = fr.streamed rate framesize opts f; // inversion (framed -> streamed) - ts = testStream length; // newly initialised stream to compare with + ts = expectedStream framesize opts length; (firstChunk = str.read firstChunkSize; compareUsing mat.equal diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/test/test_syntheticstream.yeti --- a/src/may/stream/test/test_syntheticstream.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/test/test_syntheticstream.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -37,18 +37,6 @@ compare str.position 6 ), -"pulseTrain": \( - // 2 pulses a second sampled 8 times a second - str = syn.pulseTrain 8 2; - compare str.position 0 and - compare str.channels 1 and - compare str.sampleRate 8 and - compare str.available (Infinite ()) and - compare str.finished? false and - compareApprox epsilon (vec.list (mat.getRow 0 (str.read 9))) [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ] and - compare str.position 9 -), - "silent": \( str = syn.silent 8; compare str.position 0 and diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/test/test_waves.yeti --- a/src/may/stream/test/test_waves.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/test/test_waves.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -5,8 +5,15 @@ mat = load may.matrix; vec = load may.vector; +plt = load may.plot; + { compare, compareUsing, compareMatrices, assert } = load may.test; +compareApprox eps = + compareUsing do v1 v2: all id (map2 do f1 f2: abs (f1 - f2) < eps done v1 v2) done; + +epsilon = 0.000001; + [ "square": \( @@ -21,6 +28,49 @@ 0,-0.942809])) ), +"impulseTrain-naive": \( + // 2 pulses a second sampled 8 times a second (naive pulse train) + str = wav.impulseTrain 8 2; + compare str.position 0 and + compare str.channels 1 and + compare str.sampleRate 8 and + compare str.available (Infinite ()) and + compare str.finished? false and + compareApprox epsilon (vec.list (mat.getRow 0 (str.read 9))) [ 1, 0, 0, 0, 1, 0, 0, 0, 1 ] and + compare str.position 9 +), + +"impulseTrain-naive-frac": \( + // 2.5 pulses a second sampled 10 times a second (still a naive + // pulse train even though the pulse rate is non-integer) + str = wav.impulseTrain 10 2.5; + compare str.position 0 and + compare str.channels 1 and + compare str.sampleRate 10 and + compare str.available (Infinite ()) and + compare str.finished? false and + compareApprox epsilon (vec.list (mat.getRow 0 (str.read 11))) [ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0 ] and + compare str.position 11 +), + +"impulseTrain-bandlimited": \( + // 3 pulses a second sampled 10 times a second + str = wav.impulseTrain 100 3; + + x = mat.getRow 0 (str.read 200); +false; //!!! todo +/* + compare str.position 0 and + compare str.channels 1 and + compare str.sampleRate 8 and + compare str.available (Infinite ()) and + compare str.finished? false and + compareApprox epsilon (vec.list (mat.getRow 0 (str.read 14))) + [ 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0 ] and + compare str.position 14 +*/ +), + "cycleLengthFor": \( compare (wav.cycleLengthFor 48000 1000) 48 and compare (wav.cycleLengthFor 44100 1000) 441 and diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/stream/waves.yeti --- a/src/may/stream/waves.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/stream/waves.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -4,60 +4,87 @@ module may.stream.waves; -{ sinusoid } = load may.stream.syntheticstream; +{ sinusoid, generated } = load may.stream.syntheticstream; { sum, scaledBy } = load may.stream.manipulate; { precalculatedRepeated } = load may.stream.syntheticstream; -{ gcd } = load may.mathmisc; +{ resampledTo } = load may.stream.resample; +{ gcd, floor, ceil } = load may.mathmisc; load may.stream.type; -cycleLengthFor rate freq = +cycleLengthFor sampleRate freq = // The number of samples we can cache and repeat from a wavetable, // in order to exactly reproduce a signal of the given frequency // at the given samplerate. - (if rate == int rate and freq == int freq and rate > 0 and freq > 0 then - rate / (gcd rate freq) + (if sampleRate == int sampleRate and + freq == int freq and + sampleRate > 0 and freq > 0 then + sampleRate / (gcd sampleRate freq) else - period = rate / freq; - m = find do i: i * period == int (i * period) done [1..rate]; + period = sampleRate / freq; + m = find do i: i * period == int (i * period) done [1..sampleRate]; if empty? m then 0 else period * (head m) fi fi); -square' rate freq = +square' sampleRate freq = sum (map do n: m = n*2 + 1; scaledBy (1/m) - (sinusoid rate (m * freq)) - done (0 :: [1 .. int (rate/4 / freq) - 1])); + (sinusoid sampleRate (m * freq)) + done (0 :: [1 .. int (sampleRate/4 / freq) - 1])); -saw' rate freq = +saw' sampleRate freq = sum (map do n: scaledBy if n % 2 == 0 then -1/n else 1/n fi - (sinusoid rate (n * freq)) - done [1 .. int (rate/2 / freq)]); + (sinusoid sampleRate (n * freq)) + done [1 .. int (sampleRate/2 / freq)]); -triangle' rate freq = +triangle' sampleRate freq = sum (map do n: m = n*2 + 1; scaledBy if n % 2 == 0 then -1/(m*m) else 1/(m*m) fi - (sinusoid rate (m * freq)) - done (0 :: [1 .. int (rate/4 / freq) - 1])); + (sinusoid sampleRate (m * freq)) + done (0 :: [1 .. int (sampleRate/4 / freq) - 1])); -cached f rate freq = - (n = cycleLengthFor rate freq; +// Bandlimited impulse train: produce naive pulse train at a frequency +// that is an integer fraction of the sample rate (so as to have exact +// sample locations), then resample so as to place the impulses at the +// requested frequency +impulseTrain sampleRate freq = + if freq > sampleRate/2 then + failWith "Can't generate impulse train with frequency > half the sample rate (\(freq) > \(sampleRate)/2)" + else + naiveTrain f = + (spacing = int (sampleRate / f); + generated sampleRate + do n: if n % spacing == 0 then 1 else 0 fi done); + ratio = sampleRate / freq; + if ratio == int ratio then + naiveTrain freq + else + integerRatio = floor ratio; + naiveFreq = sampleRate / integerRatio; + n = naiveTrain (sampleRate / integerRatio); + resampled = resampledTo (sampleRate * naiveFreq / freq) n; + resampled with { sampleRate }; + fi; + fi; + +cached f sampleRate freq = + (n = cycleLengthFor sampleRate freq; if n == 0 then - f rate freq; + f sampleRate freq; else // one could alternatively do - // repeated (precalculated rate ((f rate freq).read n)) + // repeated (precalculated sampleRate ((f sampleRate freq).read n)) // or - // repeated (withDuration n (f rate freq)) - precalculatedRepeated rate ((f rate freq).read n); + // repeated (withDuration n (f sampleRate freq)) + precalculatedRepeated sampleRate ((f sampleRate freq).read n); fi); square = cached square'; @@ -68,6 +95,7 @@ square is number -> number -> stream_t, saw is number -> number -> stream_t, triangle is number -> number -> stream_t, + impulseTrain is number -> number -> stream_t, cycleLengthFor is number -> number -> number, } diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/test.yeti --- a/src/may/test.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/test.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -44,6 +44,16 @@ false; fi); +assertException f = + try + \() (f ()); + println "** failed to catch expected exception"; + false; + catch Exception _: + goodCompares := goodCompares + 1; + true; + yrt; + time msg f = (start = System#currentTimeMillis(); result = f (); @@ -62,8 +72,12 @@ println "Test \(name) failed"; name; fi - catch FailureException e: + catch Exception e: println "Test \(name) threw exception: \(e)"; + trace = e#getStackTrace(); + maxLen = 10; + for (take maxLen trace) do e: println " at \(e)" done; + if length trace > maxLen then println " ..." fi; name; yrt; done (sort (keys testHash))); @@ -81,7 +95,7 @@ bad); { - compare, compareUsing, compareMatrices, assert, + compare, compareUsing, compareMatrices, assert, assertException, time, runTests, } diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/vector.yeti --- a/src/may/vector.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/vector.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -18,7 +18,7 @@ import java.util: Arrays; import may.bits: VectorBits; -{ ceil } = load may.mathmisc; +{ floor, ceil } = load may.mathmisc; /// Return a vector of n zeros. zeros n is number -> ~double[] = @@ -33,6 +33,10 @@ /// Return a vector of length n, containing all ones. ones n = consts 1.0 n; +/// Return a vector of length n, containing random values. +randoms n is number -> ~double[] = + (map \(Math#random()) [1..n]) as ~double[]; + /// Return a vector of the values in the given list. fromList l is list? -> ~double[] = l as ~double[]; @@ -204,24 +208,52 @@ len: sum' v / len esac; +sorted v is ~double[] -> ~double[] = + (v' = copyOf v; + Arrays#sort(v'); + v'); + +quantile q v is number -> ~double[] -> number = + if empty? v then 0 + else + v = sorted v; + dist = q * (length v - 1); + low = floor dist; + high = ceil dist; + if low == high then v[low] + else + v[low] * (high - dist) + v[high] * (dist - low); + fi; + fi; + +median v is ~double[] -> number = + quantile 0.5 v; + +listOp f vv = + case vv of + [v]: v; + v::rest: + (out = copyOf v; + for rest (f out); + out); + _: failWith "Empty argument list"; + esac; + +//!!! doc: throws exception if not all inputs are of the same length add vv is list?<~double[]> -> ~double[] = - (len = head (sort (map length' vv)); - out = new double[len]; - for vv do v: - VectorBits#addTo(out, v, len); - done; - out); + listOp do out v: VectorBits#addTo(out, v) done vv; +//!!! doc: throws exception if not all inputs are of the same length subtract v1 v2 is ~double[] -> ~double[] -> ~double[] = - (len = if length v1 < length v2 then length v1 else length v2 fi; - out = new double[len]; - for [0..len-1] do i: - out[i] := v1[i] - v2[i] - done; - out); + listOp do out v: VectorBits#subtractFrom(out, v) done [v1, v2]; -multiply b1 b2 is ~double[] -> ~double[] -> ~double[] = - VectorBits#multiply(b1, b2); +//!!! doc: throws exception if not all inputs are of the same length +multiply vv is list?<~double[]> -> ~double[] = + listOp do out v: VectorBits#multiplyBy(out, v) done vv; + +//!!! doc: throws exception if not all inputs are of the same length +divide v1 v2 is ~double[] -> ~double[] -> ~double[] = + listOp do out v: VectorBits#divideBy(out, v) done [v1, v2]; scaled n v is number -> ~double[] -> ~double[] = if n == 1 then v @@ -236,7 +268,9 @@ fi; sqr v = - multiply v v; + (out = copyOf v; + VectorBits#multiplyBy(out, v); + out); rms = sqrt . mean . sqr; @@ -293,6 +327,7 @@ zeros, consts, ones, + randoms, vector v = v, primitive, floats, @@ -308,15 +343,19 @@ slice, resizedTo, reverse, + sorted, repeated, concat = concat', sum = sum', mean, + median, + quantile, add, subtract, multiply, + divide, + scaled, divideBy, - scaled, abs = abs', negative, sqr, @@ -333,6 +372,7 @@ zeros is number -> vector_t, consts is number -> number -> vector_t, ones is number -> vector_t, + randoms is number -> vector_t, vector is ~double[] -> vector_t, primitive is vector_t -> ~double[], floats is vector_t -> ~float[], @@ -348,15 +388,19 @@ slice is vector_t -> number -> number -> vector_t, resizedTo is number -> vector_t -> vector_t, reverse is vector_t -> vector_t, + sorted is vector_t -> vector_t, repeated is vector_t -> number -> vector_t, concat is list? -> vector_t, sum is vector_t -> number, mean is vector_t -> number, + median is vector_t -> number, + quantile is number -> vector_t -> number, add is list? -> vector_t, subtract is vector_t -> vector_t -> vector_t, - multiply is vector_t -> vector_t -> vector_t, + multiply is list? -> vector_t, + divide is vector_t -> vector_t -> vector_t, + scaled is number -> vector_t -> vector_t, divideBy is number -> vector_t -> vector_t, - scaled is number -> vector_t -> vector_t, abs is vector_t -> vector_t, negative is vector_t -> vector_t, sqr is vector_t -> vector_t, diff -r eeebedb84ca7 -r ab85dfc45e3f src/may/vector/test/test_vector.yeti --- a/src/may/vector/test/test_vector.yeti Mon Mar 03 17:03:32 2014 +0000 +++ b/src/may/vector/test/test_vector.yeti Sat Apr 05 16:02:24 2014 +0100 @@ -3,7 +3,7 @@ vec = load may.vector; -{ compare } = load may.test; +{ compare, assertException } = load may.test; [ @@ -163,24 +163,65 @@ compare ((vec.mean . vec.zeros) 0) 0 and compare ((vec.mean . vec.zeros) 5) 0 and compare ((vec.mean . vec.ones) 5) 1 and - compare ((vec.mean . vec.fromList) [1,-2,3,0]) 0.5 + compare ((vec.mean . vec.fromList) [1,-2,3,0]) 0.5 and + compare ((vec.mean . vec.fromList) [1,-2,3,2]) 1 +), + +"median": \( + compare ((vec.median . vec.zeros) 0) 0 and + compare ((vec.median . vec.zeros) 5) 0 and + compare ((vec.median . vec.ones) 5) 1 and + compare ((vec.median . vec.fromList) [-2,1,2,3]) 1.5 and + compare ((vec.median . vec.fromList) [1,-2,3,2]) 1.5 and + compare ((vec.median . vec.fromList) [1,-2,3,0]) 0.5 and + compare ((vec.median . vec.fromList) [1,-2,3,0,6]) 1 +), + +"upperQuartile": \( + compare ((vec.quantile 0.75 . vec.zeros) 0) 0 and + compare ((vec.quantile 0.75 . vec.zeros) 5) 0 and + compare ((vec.quantile 0.75 . vec.ones) 5) 1 and + compare ((vec.quantile 0.75 . vec.fromList) [0,1,2]) 1.5 and + compare ((vec.quantile 0.75 . vec.fromList) [0,1,2,3]) 2.25 and + compare ((vec.quantile 0.75 . vec.fromList) [1,-2,3,2]) 2.25 +), + +"sorted": \( + compare ((vec.list . vec.sorted . vec.zeros) 0) [] and + compare ((vec.list . vec.sorted . vec.zeros) 5) [0,0,0,0,0] and + compare ((vec.list . vec.sorted . vec.ones) 5) [1,1,1,1,1] and + compare ((vec.list . vec.sorted . vec.fromList) [-2,1,2,3]) [-2,1,2,3] and + compare ((vec.list . vec.sorted . vec.fromList) [1,-2,3,2]) [-2,1,2,3] and + compare ((vec.list . vec.sorted . vec.fromList) [1,-2,-2.1,3,0.5,1]) [-2.1,-2,0.5,1,1,3] ), "add": \( - compare (vec.list (vec.add [vec.zeros 0, vec.ones 5])) [] and - compare (vec.list (vec.add [vec.consts 3 4, vec.fromList [1,2,3] ])) [4,5,6] and - compare (vec.list (vec.add [vec.consts (-3) 4, vec.fromList [1,2,3] ])) [-2,-1,0] + compare (vec.list (vec.add [vec.zeros 0, vec.ones 0])) [] and + compare (vec.list (vec.add [vec.consts 3 4, vec.fromList [1,2,3,0] ])) [4,5,6,3] and + assertException \(vec.add [vec.consts 3 3, vec.fromList [1,2,3], vec.fromList [6,7,8,9] ]) ), "subtract": \( - compare (vec.list (vec.subtract (vec.zeros 0) (vec.ones 5))) [] and - compare (vec.list (vec.subtract (vec.consts 3 4) (vec.fromList [1,2,3]))) [2,1,0] and - compare (vec.list (vec.subtract (vec.consts (-3) 4) (vec.fromList [1,2,3]))) [-4,-5,-6] + compare (vec.list (vec.subtract (vec.zeros 0) (vec.ones 0))) [] and + compare (vec.list (vec.subtract (vec.consts 3 4) (vec.fromList [1,2,3,0]))) [2,1,0,3] and + assertException \(vec.subtract (vec.consts (-3) 4) (vec.fromList [1,2,3])) ), "multiply": \( - compare (vec.list (vec.multiply (vec.zeros 0) (vec.ones 5))) [] and - compare (vec.list (vec.multiply (vec.consts (-3) 4) (vec.fromList [1,2,3]))) [-3,-6,-9] + compare (vec.list (vec.multiply [vec.zeros 0, vec.ones 0])) [] and + compare (vec.list (vec.multiply [vec.consts (-3) 4, vec.fromList [1,2,3,0]])) [-3,-6,-9,0] and + assertException \(vec.multiply [vec.consts 3 3, vec.fromList [1,2,3], vec.fromList [6,7,8,9]]) +), + +"divide": \( + compare (vec.list (vec.divide (vec.zeros 0) (vec.ones 0))) [] and + compare (vec.list (vec.divide (vec.consts (-3) 3) (vec.fromList [1,2,3]))) [-3,-(3/2),-1] and + assertException \(vec.divide (vec.consts (-3) 4) (vec.fromList [1,2,3])); +), + +"scaled": \( + compare (vec.list (vec.scaled 5 (vec.ones 0))) [] and + compare (vec.list (vec.scaled 5 (vec.fromList [1,2,-3]))) [5,10,-15] ), "divideBy": \( @@ -188,11 +229,6 @@ compare (vec.list (vec.divideBy 5 (vec.fromList [1,2,-3]))) [0.2,0.4,-0.6] ), -"scaled": \( - compare (vec.list (vec.scaled 5 (vec.ones 0))) [] and - compare (vec.list (vec.scaled 5 (vec.fromList [1,2,-3]))) [5,10,-15] -), - "abs": \( compare (vec.list (vec.abs (vec.ones 0))) [] and compare (vec.list (vec.abs (vec.fromList [1,2,-3]))) [1,2,3]