Repository: mahout Updated Branches: refs/heads/master 7d71ad00a -> ff51ab448
MAHOUT-1813: Functional 'apply' DSL for distributed and in-memory matrices closes apache/mahout#194 Project: http://git-wip-us.apache.org/repos/asf/mahout/repo Commit: http://git-wip-us.apache.org/repos/asf/mahout/commit/ff51ab44 Tree: http://git-wip-us.apache.org/repos/asf/mahout/tree/ff51ab44 Diff: http://git-wip-us.apache.org/repos/asf/mahout/diff/ff51ab44 Branch: refs/heads/master Commit: ff51ab448b07c5b218d1a08ed5c1194ad5b24179 Parents: 7d71ad0 Author: Andrew Palumbo <[email protected]> Authored: Tue Mar 15 18:19:49 2016 -0400 Committer: Andrew Palumbo <[email protected]> Committed: Tue Mar 15 18:19:49 2016 -0400 ---------------------------------------------------------------------- .../org/apache/mahout/math/drm/DrmLikeOps.scala | 23 ++++++- .../mahout/math/scalabindings/MatrixOps.scala | 63 ++++++++++++++++++-- .../mahout/math/scalabindings/package.scala | 6 ++ .../mahout/math/drm/RLikeDrmOpsSuiteBase.scala | 14 +++++ .../math/scalabindings/MatrixOpsSuite.scala | 12 ++++ 5 files changed, 112 insertions(+), 6 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/mahout/blob/ff51ab44/math-scala/src/main/scala/org/apache/mahout/math/drm/DrmLikeOps.scala ---------------------------------------------------------------------- diff --git a/math-scala/src/main/scala/org/apache/mahout/math/drm/DrmLikeOps.scala b/math-scala/src/main/scala/org/apache/mahout/math/drm/DrmLikeOps.scala index e2c6e17..43b4f56 100644 --- a/math-scala/src/main/scala/org/apache/mahout/math/drm/DrmLikeOps.scala +++ b/math-scala/src/main/scala/org/apache/mahout/math/drm/DrmLikeOps.scala @@ -19,7 +19,7 @@ package org.apache.mahout.math.drm import scala.reflect.ClassTag import org.apache.mahout.math.scalabindings._ -import org.apache.mahout.math.drm.logical.{OpPar, OpMapBlock, OpRowRange} +import org.apache.mahout.math.drm.logical.{OpAewUnaryFunc, OpPar, OpMapBlock, OpRowRange} /** Common Drm ops */ class DrmLikeOps[K](protected[drm] val drm: DrmLike[K]) { @@ -116,4 +116,25 @@ class DrmLikeOps[K](protected[drm] val drm: DrmLike[K]) { } else rowSrc } + + /** + * Apply a function element-wise. + * + * @param f element-wise function + * @param evalZeros Do we have to process zero elements? true, false, auto: if auto, we will test + * the supplied function for `f(0) != 0`, and depending on the result, will + * decide if we want evaluation for zero elements. WARNING: the AUTO setting + * may not always work correctly for functions that are meant to run in a specific + * backend context, or non-deterministic functions, such as {-1,0,1} random + * generators. + * @return new DRM with the element-wise function applied. + */ + def apply(f: Double â Double, evalZeros: AutoBooleanEnum.T = AutoBooleanEnum.AUTO) = { + val ezeros = evalZeros match { + case AutoBooleanEnum.TRUE â true + case AutoBooleanEnum.FALSE â false + case AutoBooleanEnum.AUTO â f(0) != 0 + } + new OpAewUnaryFunc[K](drm, f, ezeros) + } } http://git-wip-us.apache.org/repos/asf/mahout/blob/ff51ab44/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/MatrixOps.scala ---------------------------------------------------------------------- diff --git a/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/MatrixOps.scala b/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/MatrixOps.scala index cb92e1d..f3be285 100644 --- a/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/MatrixOps.scala +++ b/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/MatrixOps.scala @@ -128,10 +128,60 @@ class MatrixOps(val m: Matrix) { } /** - * Warning: This provides read-only view only. + * Apply a function element-wise without side-effects to the argument (creates a new matrix). + * + * @param f element-wise function "value" â "new value" + * @param evalZeros Do we have to process zero elements? true, false, auto: if auto, we will test + * the supplied function for `f(0) != 0`, and depending on the result, will + * decide if we want evaluation for zero elements. WARNING: the AUTO setting + * may not always work correctly for functions that are meant to run in a specific + * backend context, or non-deterministic functions, such as {-1,0,1} random + * generators. + * @return new DRM with the element-wise function applied. + */ + def apply(f: Double â Double, evalZeros: AutoBooleanEnum.T): Matrix = { + val ezeros = evalZeros match { + case AutoBooleanEnum.TRUE â true + case AutoBooleanEnum.FALSE â false + case AutoBooleanEnum.AUTO â f(0) != 0 + } + if (ezeros) m.cloned := f else m.cloned ::= f + } + + /** + * Apply a function element-wise without side-effects to the argument (creates a new matrix). + * + * @param f element-wise function (row, column, value) â "new value" + * @param evalZeros Do we have to process zero elements? true, false, auto: if auto, we will test + * the supplied function for `f(0) != 0`, and depending on the result, will + * decide if we want evaluation for zero elements. WARNING: the AUTO setting + * may not always work correctly for functions that are meant to run in a specific + * backend context, or non-deterministic functions, such as {-1,0,1} random + * generators. + * @return new DRM with the element-wise function applied. + */ + def apply(f: (Int, Int, Double) â Double, evalZeros: AutoBooleanEnum.T): Matrix = { + val ezeros = evalZeros match { + case AutoBooleanEnum.TRUE â true + case AutoBooleanEnum.FALSE â false + case AutoBooleanEnum.AUTO â f(0,0,0) != 0 + } + if (ezeros) m.cloned := f else m.cloned ::= f + } + + /** A version of function apply with default AUTO treatment of `evalZeros`. */ + def apply(f: Double â Double): Matrix = apply(f, AutoBooleanEnum.AUTO) + + /** A version of function apply with default AUTO treatment of `evalZeros`. */ + def apply(f: (Int, Int, Double) â Double): Matrix = apply(f, AutoBooleanEnum.AUTO) + + + /** + * Warning: This provides read-only view only. * In most cases that's what one wants. To get a copy, * use <code>m.t cloned</code> - * @return transposed view + * + * @return transposed view */ def t = Matrices.transposedView(m) @@ -143,7 +193,8 @@ class MatrixOps(val m: Matrix) { /** * Assigning from a row-wise collection of vectors - * @param that - + * + * @param that - */ def :=(that: TraversableOnce[Vector]) = { var row = 0 @@ -212,7 +263,8 @@ class MatrixOps(val m: Matrix) { * Ideally, we would probably want to override equals(). But that is not * possible without modifying AbstractMatrix implementation in Mahout * which would require discussion at Mahout team. - * @param that + * + * @param that * @return */ def equiv(that: Matrix) = @@ -233,7 +285,8 @@ class MatrixOps(val m: Matrix) { /** * test if rank == min(nrow,ncol). - * @return + * + * @return */ def isFullRank: Boolean = new QRDecomposition(if (nrow < ncol) m t else m cloned).hasFullRank http://git-wip-us.apache.org/repos/asf/mahout/blob/ff51ab44/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/package.scala ---------------------------------------------------------------------- diff --git a/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/package.scala b/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/package.scala index 426996b..6a3aa06 100644 --- a/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/package.scala +++ b/math-scala/src/main/scala/org/apache/mahout/math/scalabindings/package.scala @@ -29,6 +29,12 @@ package object scalabindings { // Reserved "ALL" range final val `::`: Range = null + // Some enums + object AutoBooleanEnum extends Enumeration { + type T = Value + val TRUE, FALSE, AUTO = Value + } + implicit def seq2Vector(s: TraversableOnce[AnyVal]) = new DenseVector(s.map(_.asInstanceOf[Number].doubleValue()).toArray) http://git-wip-us.apache.org/repos/asf/mahout/blob/ff51ab44/math-scala/src/test/scala/org/apache/mahout/math/drm/RLikeDrmOpsSuiteBase.scala ---------------------------------------------------------------------- diff --git a/math-scala/src/test/scala/org/apache/mahout/math/drm/RLikeDrmOpsSuiteBase.scala b/math-scala/src/test/scala/org/apache/mahout/math/drm/RLikeDrmOpsSuiteBase.scala index b46ee30..685c540 100644 --- a/math-scala/src/test/scala/org/apache/mahout/math/drm/RLikeDrmOpsSuiteBase.scala +++ b/math-scala/src/test/scala/org/apache/mahout/math/drm/RLikeDrmOpsSuiteBase.scala @@ -634,5 +634,19 @@ trait RLikeDrmOpsSuiteBase extends DistributedMahoutSuite with Matchers { } + test("functional apply()") { + val mxA = sparse ( + (1 -> 3) :: (7 -> 7) :: Nil, + (4 -> 5) :: (5 -> 8) :: Nil + ) + + val mxAControl = mxA cloned + val drmA = drmParallelize(mxA) + + (drmA(x => x + 1).collect - (mxAControl + 1)).norm should be < 1e-7 + (drmA(x => x * 2).collect - (2 * mxAControl)).norm should be < 1e-7 + + } + } http://git-wip-us.apache.org/repos/asf/mahout/blob/ff51ab44/math-scala/src/test/scala/org/apache/mahout/math/scalabindings/MatrixOpsSuite.scala ---------------------------------------------------------------------- diff --git a/math-scala/src/test/scala/org/apache/mahout/math/scalabindings/MatrixOpsSuite.scala b/math-scala/src/test/scala/org/apache/mahout/math/scalabindings/MatrixOpsSuite.scala index 5c8a310..1296d9e 100644 --- a/math-scala/src/test/scala/org/apache/mahout/math/scalabindings/MatrixOpsSuite.scala +++ b/math-scala/src/test/scala/org/apache/mahout/math/scalabindings/MatrixOpsSuite.scala @@ -131,6 +131,18 @@ class MatrixOpsSuite extends FunSuite with MahoutSuite { g.sum shouldBe 3 } + test("functional apply()") { + val mxA = sparse ( + (1 -> 3) :: (7 -> 7) :: Nil, + (4 -> 5) :: (5 -> 8) :: Nil + ) + val mxAControl = mxA cloned + + (mxA(x â x + 1) - (mxAControl + 1)).norm should be < 1e-7 + (mxA(x â x * 2) - (2 * mxAControl)).norm should be < 1e-7 + + } + test("sparse") { val a = sparse((1, 3) :: Nil,
