├── Imports.scala ├── LinearAlgebraReview ├── 1-MatricesAndVectors.scala ├── 2-AdditionAndScalarMultiplication.scala ├── 3-MatrixVectorMultiplication.scala ├── 4-MatrixMatrixMultiplication.scala ├── 5-MatrixMultiplicationProperties.scala └── 6-InverseAndTranspose.scala ├── README.markdown └── build.sbt /Imports.scala: -------------------------------------------------------------------------------- 1 | import scalala.scalar._ 2 | import scalala.tensor.:: 3 | import scalala.tensor.mutable._ 4 | import scalala.tensor.dense._ 5 | import scalala.tensor.sparse._ 6 | import scalala.library.Library._ 7 | import scalala.library.LinearAlgebra._ 8 | import scalala.library.Statistics._ 9 | import scalala.library.Plotting._ 10 | import scalala.operators.Implicits._ 11 | -------------------------------------------------------------------------------- /LinearAlgebraReview/1-MatricesAndVectors.scala: -------------------------------------------------------------------------------- 1 | :load Imports.scala 2 | 3 | val A = Matrix((1402, 191), (1371, 821), (949, 1437), (147, 1448)) 4 | // A: scalala.tensor.dense.DenseMatrix[Int] = 5 | // 1402 191 6 | // 1371 821 7 | // 949 1437 8 | // 147 1448 9 | 10 | val B = Matrix((1, 2, 3), (4, 5, 6)) 11 | // B: scalala.tensor.dense.DenseMatrix[Int] = 12 | // 1 2 3 13 | // 4 5 6 14 | 15 | A.domain 16 | // res2: scalala.tensor.domain.TableDomain = TableDomain(4,2) 17 | 18 | B.domain 19 | // res3: scalala.tensor.domain.TableDomain = TableDomain(2,3) 20 | 21 | val A = Matrix((1402, 191), (1371, 821), (949, 1437), (147, 1448)) 22 | // A: scalala.tensor.dense.DenseMatrix[Int] = 23 | // 1402 191 24 | // 1371 821 25 | // 949 1437 26 | // 147 1448 27 | 28 | def Aij(i: Int, j: Int) = A(i, j) 29 | // Aij: (i: Int, j: Int)Int 30 | 31 | A(1, 1) 32 | // res4: Int = 821 33 | 34 | // OOPS! Scalala is 0-indexed, not 1-indexed 35 | 36 | A(0, 0) 37 | // res5: Int = 1402 38 | 39 | A(0, 1) 40 | // res6: Int = 191 41 | 42 | A(2, 1) 43 | // res7: Int = 1437 44 | 45 | A(3, 0) 46 | // res8: Int = 147 47 | 48 | A(3, 2) 49 | // scalala.tensor.domain.DomainException: Index (3,2) out of range. Size is 4x2 50 | // at scalala.tensor.MatrixLike$class.checkKey(Matrix.scala:52) 51 | // at scalala.tensor.dense.DenseMatrix.checkKey(DenseMatrix.scala:45) 52 | // at scalala.tensor.dense.DenseMatrix.index(DenseMatrix.scala:68) 53 | // at scalala.tensor.dense.DenseMatrix.apply(DenseMatrix.scala:82) 54 | // at .(:37) 55 | 56 | val y = Vector(460, 232, 315, 178) 57 | // y: scalala.tensor.dense.DenseVectorCol[Int] = 58 | // 460 59 | // 232 60 | // 315 61 | // 178 62 | 63 | y.domain 64 | // res10: scalala.tensor.domain.IndexDomain = IndexDomain(4) 65 | 66 | def yi(i: Int) = y(i) 67 | // yi: (i: Int)Int 68 | 69 | y(0) 70 | // res11: Int = 460 71 | 72 | y(1) 73 | // res12: Int = 232 74 | 75 | y(2) 76 | // res13: Int = 315 77 | -------------------------------------------------------------------------------- /LinearAlgebraReview/2-AdditionAndScalarMultiplication.scala: -------------------------------------------------------------------------------- 1 | :load Imports.scala 2 | 3 | Matrix((1, 0), (2, 5), (3, 1)) + Matrix((4, 0.5), (2, 5), (0, 1)) 4 | // :36: error: V is not a scalar value 5 | // DenseMatrix((1, 0), (2, 5), (3, 1)) + DenseMatrix((4, 0.5), (2, 5), (0, 1)) 6 | // ^ 7 | 8 | // OOPS! This is a cryptic error, but it means we're trying to mix Ints and Doubles 9 | // in the same matrix. If we force all our Ints to be doubles, then we're fine. 10 | 11 | Matrix((1, 0), (2, 5), (3, 1)) + Matrix((4.0, 0.5), (2.0, 5.0), (0.0, 1.0)) 12 | // res1: scalala.tensor.dense.DenseMatrix[Double] = 13 | // 5.00000 0.500000 14 | // 4.00000 10.0000 15 | // 3.00000 2.00000 16 | 17 | Matrix((1, 0), (2, 5), (3, 1)) + Matrix((4.0, 0.5), (2.0, 5.0)) 18 | // scalala.tensor.domain.DomainException: Incompatible domain: TableDomain(2,2) for TableDomain(3,2) 19 | // at scalala.tensor.DomainFunction$class.checkDomain(DomainFunction.scala:54) 20 | // at scalala.tensor.dense.DenseMatrix.checkDomain(DenseMatrix.scala:45) 21 | // at scalala.tensor.Tensor$$anon$15.joinEitherNonZero(Tensor.scala:612) 22 | // at scalala.tensor.Tensor$$anon$4.apply(Tensor.scala:688) 23 | // at scalala.operators.NumericOps$class.$colon$plus(Ops.scala:39) 24 | // at scalala.tensor.dense.DenseMatrix.$colon$plus(DenseMatrix.scala:45) 25 | // at scalala.operators.NumericOps$class.$plus(Ops.scala:121) 26 | // at scalala.tensor.dense.DenseMatrix.$plus(DenseMatrix.scala:45) 27 | // at .(:36) 28 | 29 | 3 * Matrix((1, 0), (2, 5), (3, 1)) 30 | // :36: error: overloaded method value * with alternatives: 31 | // (x: Double)Double 32 | // (x: Float)Float 33 | // (x: Long)Long 34 | // (x: Int)Int 35 | // (x: Char)Int 36 | // (x: Short)Int 37 | // (x: Byte)Int 38 | // cannot be applied to (scalala.tensor.dense.DenseMatrix[Int]) 39 | // 3 * DenseMatrix((1, 0), (2, 5), (3, 1)) 40 | // ^ 41 | 42 | // OOPS! Ints don't know how to multiply themselves against matrices. 43 | // We can use the :* operator, or we can flip the order of the operands. 44 | 45 | val A = 3 :* Matrix((1, 0), (2, 5), (3, 1)) 46 | // A: scalala.tensor.dense.DenseMatrix[Int] = 47 | // 3 0 48 | // 6 15 49 | // 9 3 50 | 51 | val A = Matrix((1, 0), (2, 5), (3, 1)) * 3 52 | // A: scalala.tensor.dense.DenseMatrix[Int] = 53 | // 3 0 54 | // 6 15 55 | // 9 3 56 | 57 | A.domain 58 | // res5: scalala.tensor.domain.TableDomain = TableDomain(3,2) 59 | 60 | Matrix((4, 0), (6, 3)) / 4 61 | // res6: scalala.tensor.dense.DenseMatrix[Int] = 62 | // 1 0 63 | // 1 0 64 | 65 | // OOPS! We have to be careful with Java's integer division. If we force 66 | // 4 to be a double we get the expected result. 67 | 68 | Matrix((4, 0), (6, 3)) / 4.0 69 | // res7: scalala.tensor.dense.DenseMatrix[Double] = 70 | // 1.00000 0.00000 71 | // 1.50000 0.750000 72 | 73 | val x = Vector(1, 4, 2) * 3 + Vector(0, 0, 5) - Vector(3, 0, 2) / 3.0 74 | // x: scalala.tensor.dense.DenseVectorCol[Double] = 75 | // 2.00000 76 | // 12.0000 77 | // 10.3333 78 | 79 | x.domain 80 | // res9: scalala.tensor.domain.IndexDomain = IndexDomain(3) 81 | -------------------------------------------------------------------------------- /LinearAlgebraReview/3-MatrixVectorMultiplication.scala: -------------------------------------------------------------------------------- 1 | :load Imports.scala 2 | 3 | val A = Matrix((1, 3), (4, 0), (2, 1)) * Vector(1, 5) 4 | // A: scalala.tensor.dense.DenseVectorCol[Int] = 5 | // 16 6 | // 4 7 | // 7 8 | 9 | A.domain 10 | // res1: scalala.tensor.domain.IndexDomain = IndexDomain(3) 11 | 12 | val A = Matrix((1, 2, 1, 5), (0, 3, 0, 4), (-1, -2, 0, 0)) 13 | // A: scalala.tensor.dense.DenseMatrix[Int] = 14 | // 1 2 1 5 15 | // 0 3 0 4 16 | // -1 -2 0 0 17 | 18 | val x = Vector(1, 3, 2, 1) 19 | // x: scalala.tensor.dense.DenseVectorCol[Int] = 20 | // 1 21 | // 3 22 | // 2 23 | // 1 24 | 25 | A.domain 26 | // res2: scalala.tensor.domain.TableDomain = TableDomain(3,4) 27 | 28 | x.domain 29 | // res3: scalala.tensor.domain.IndexDomain = IndexDomain(4) 30 | 31 | val y = A * x 32 | // y: scalala.tensor.dense.DenseVectorCol[Int] = 33 | // 14 34 | // 13 35 | // -7 36 | 37 | y.domain 38 | // res4: scalala.tensor.domain.IndexDomain = IndexDomain(3) 39 | 40 | def h(x: Int) = -40 + 0.25*x 41 | // h: (x: Int)Double 42 | 43 | val data = Matrix((1, 2104), (1, 1416), (1, 1534), (1, 852)) 44 | // data: scalala.tensor.dense.DenseMatrix[Int] = 45 | // 1 2104 46 | // 1 1416 47 | // 1 1534 48 | // 1 852 49 | 50 | val parameters = Vector(-40, 0.25) 51 | // parameters: scalala.tensor.dense.DenseVectorCol[Double] = 52 | // -40.0000 53 | // 0.250000 54 | 55 | val prediction = data * parameters 56 | // prediction: scalala.tensor.dense.DenseVectorCol[Double] = 57 | // 486.000 58 | // 314.000 59 | // 343.500 60 | // 173.000 61 | -------------------------------------------------------------------------------- /LinearAlgebraReview/4-MatrixMatrixMultiplication.scala: -------------------------------------------------------------------------------- 1 | :load Imports.scala 2 | 3 | val A = Matrix((1, 3, 2), (4, 0, 1)) * Matrix((1, 3), (0, 1), (5, 2)) 4 | // A: scalala.tensor.dense.DenseMatrix[Int] = 5 | // 11 10 6 | // 9 14 7 | 8 | A.domain 9 | // res1: scalala.tensor.domain.TableDomain = TableDomain(2,2) 10 | 11 | Matrix((1, 3), (2, 5)) * Matrix((0, 1), (3, 2)) 12 | // res2: scalala.tensor.dense.DenseMatrix[Int] = 13 | // 9 7 14 | // 15 12 15 | 16 | val data = Matrix((1, 2104), (1, 1416), (1, 1534), (1, 852)) 17 | // data: scalala.tensor.dense.DenseMatrix[Int] = 18 | // 1 2104 19 | // 1 1416 20 | // 1 1534 21 | // 1 852 22 | 23 | val hypotheses = Matrix((-40.0, 200.0, -150.0), (0.25, 0.1, 0.4)) 24 | // hypotheses: scalala.tensor.dense.DenseMatrix[Double] = 25 | // -40.0000 200.000 -150.000 26 | // 0.250000 0.100000 0.400000 27 | 28 | data * hypotheses 29 | // res3: scalala.tensor.dense.DenseMatrix[Double] = 30 | // 486.000 410.400 691.600 31 | // 314.000 341.600 416.400 32 | // 343.500 353.400 463.600 33 | // 173.000 285.200 190.800 34 | -------------------------------------------------------------------------------- /LinearAlgebraReview/5-MatrixMultiplicationProperties.scala: -------------------------------------------------------------------------------- 1 | :load Imports.scala 2 | 3 | 3 * 5 == 5 * 3 4 | // res0: Boolean = true 5 | 6 | val m1 = Matrix((1, 1), (0, 0)) 7 | // m1: scalala.tensor.dense.DenseMatrix[Int] = 8 | // 1 1 9 | // 0 0 10 | 11 | val m2 = Matrix((0, 0), (2, 0)) 12 | // m2: scalala.tensor.dense.DenseMatrix[Int] = 13 | // 0 0 14 | // 2 0 15 | 16 | m1 * m2 17 | // res1: scalala.tensor.dense.DenseMatrix[Int] = 18 | // 2 0 19 | // 0 0 20 | 21 | m2 * m1 22 | // res2: scalala.tensor.dense.DenseMatrix[Int] = 23 | // 0 0 24 | // 2 2 25 | 26 | m1 * m2 == m2 * m1 27 | // res3: Boolean = false 28 | 29 | (3 * 5) * 2 == 3 * (5 * 2) 30 | // res4: Boolean = true 31 | 32 | 1 * 8 == 8 * 1 33 | // res5: Boolean = true 34 | 35 | 8 * 1 == 8 36 | // res6: Boolean = true 37 | 38 | Matrix.eye[Int](2, 2) 39 | // res7: scalala.tensor.dense.DenseMatrix[Int] = 40 | // 1 0 41 | // 0 1 42 | 43 | Matrix.eye[Int](3, 3) 44 | // res8: scalala.tensor.dense.DenseMatrix[Int] = 45 | // 1 0 0 46 | // 0 1 0 47 | // 0 0 1 48 | 49 | Matrix.eye[Int](4, 4) 50 | // res9: scalala.tensor.dense.DenseMatrix[Int] = 51 | // 1 0 0 0 52 | // 0 1 0 0 53 | // 0 0 1 0 54 | // 0 0 0 1 55 | 56 | val I = Matrix.eye[Int](2, 2) 57 | // I: scalala.tensor.dense.DenseMatrix[Int] = 58 | // 1 0 59 | // 0 1 60 | 61 | m1 * I == I * m1 62 | // res10: Boolean = true 63 | 64 | I * m1 == m1 65 | // res11: Boolean = true 66 | -------------------------------------------------------------------------------- /LinearAlgebraReview/6-InverseAndTranspose.scala: -------------------------------------------------------------------------------- 1 | :load Imports.scala 2 | 3 | 3 * math.pow(3, -1) == 1.0 4 | // res0: Boolean = true 5 | 6 | 12 * math.pow(12, -1) == 1.0 7 | // res1: Boolean = true 8 | 9 | math.pow(0, -1) 10 | // res2: Double = Infinity 11 | 12 | val A = Matrix((3, 4), (2, 16)) 13 | // A: scalala.tensor.dense.DenseMatrix[Int] = 14 | // 3 4 15 | // 2 16 16 | 17 | val A_inv = Matrix((0.4, -0.1), (-0.05, 0.075)) 18 | // A_inv: scalala.tensor.dense.DenseMatrix[Double] = 19 | // 0.400000 -0.100000 20 | // -0.0500000 0.0750000 21 | 22 | A * A_inv 23 | // res3: scalala.tensor.dense.DenseMatrix[Double] = 24 | // 1.00000 -5.55112e-17 25 | // 0.00000 1.00000 26 | 27 | A_inv * A 28 | // res4: scalala.tensor.dense.DenseMatrix[Double] = 29 | // 1.00000 0.00000 30 | // -2.77556e-17 1.00000 31 | 32 | inv(A) 33 | // res5: scalala.tensor.dense.DenseMatrix[Double] = 34 | // 0.400000 -0.100000 35 | // -0.0500000 0.0750000 36 | 37 | val inverseOfA = inv(A) 38 | // inverseOfA: scalala.tensor.dense.DenseMatrix[Double] = 39 | // 0.400000 -0.100000 40 | // -0.0500000 0.0750000 41 | 42 | A * inverseOfA 43 | // res6: scalala.tensor.dense.DenseMatrix[Double] = 44 | // 1.00000 0.00000 45 | // 0.00000 1.00000 46 | 47 | inverseOfA * A 48 | // res7: scalala.tensor.dense.DenseMatrix[Double] = 49 | // 1.00000 0.00000 50 | // 0.00000 1.00000 51 | 52 | val A = Matrix((1, 2, 0), (3, 5, 9)) 53 | // A: scalala.tensor.dense.DenseMatrix[Int] = 54 | // 1 2 0 55 | // 3 5 9 56 | 57 | val A_t = A.t 58 | // A_t: scalala.tensor.mutable.Matrix[Int] = 59 | // 1 3 60 | // 2 5 61 | // 0 9 62 | 63 | A.domain 64 | // res8: scalala.tensor.domain.TableDomain = TableDomain(2,3) 65 | 66 | A.t.domain 67 | // res9: scalala.tensor.domain.TableDomain = TableDomain(3,2) 68 | 69 | A.t(0, 1) == A(1, 0) 70 | // res10: Boolean = true 71 | 72 | A.t(2, 1) == A(1, 2) 73 | // res11: Boolean = true 74 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | ml-class 2 | ======== 3 | 4 | [Stanford's Machine Learning class][0] is open to enrollment from the public. 5 | 6 | I'll be using [Scalala][1], a linear algebra library for Scala, to work through the exercises and assignments for this class. I'll make as much work as possible available here, within the bounds of the Honor Code. 7 | 8 | Why Scalala? 9 | ------------ 10 | 11 | Scalala is a Matlab-like (and Octave-like) library for linear algebra and plotting in Scala. The similarities to Matlab/Octave will make it easy to port the course content from Octave to Scalala. The upside is access to the full power of a general-purpose programming language (Scala), a rock-solid and production-ready runtime environment (the JVM), and high-performance numerical libraries (MTJ and BLAS). While these advantages may not be significant for the course itself, they would certainly come in handy for real-world applications of the machine learning concepts learned in the class. 12 | 13 | Prerequisites 14 | ------------- 15 | 16 | The only prerequisite is installing sbt 0.11 on your machine. If you do not already have sbt 0.11 installed, [follow these instructions][2] to install it. sbt will take care of downloading Scala, Scalala, and whatever other libraries are necessary to use this code. 17 | 18 | Usage 19 | ----- 20 | 21 | Once sbt is installed, use `sbt console` to get access to a Scala console. Once in the Scala console, use `:load Imports.scala` to import the Scalala libraries. You now have a fully featured Scalala console with which to work. 22 | 23 | The files in this project are presented as console sessions. You can load them into the console if you wish (e.g., `:load LinearAlgebraReview/1-MatricesAndVectors.scala`), but it's probably more useful to read through the transcript line-by-line as you're watching the corresponding lecture video. I'd encourage you to type in each line into your own console session as you watch. 24 | 25 | Gotchas 26 | ------- 27 | 28 | ### 1-indexed vs 0-indexed 29 | 30 | The lectures, Matlab, and Octave use 1-based indexes for vectors and matrices, whereas Scalala uses 0-based indexes. This will probably be a frequent source of mistakes when porting course materials to Scalala. 31 | 32 | ### Int vs Double 33 | 34 | Java (and thus Scala) has different primitive types for Ints vs Doubles. Scalala usually does the Right Thing when it comes to Ints vs Doubles, but there's a few corner cases to be aware of: 35 | 36 | #### constructing matrices 37 | 38 | Mixing Ints and Doubles in a Matrix constructor (e.g., `Matrix((1, 1.0))`) will result in a cryptic message: `error: V is not a scalar value`. The fix is to use Doubles everywhere (e.g., `Matrix((1.0, 1.0))`). 39 | 40 | #### integer division 41 | 42 | Additions, multiplications, etc between Ints and Doubles will be upcast appropriately. However, division of Ints by Ints will NOT be upcast, and will instead result in integer division, which is probably not what you want. 43 | 44 | ### Vector/Matrix operations on Scalars 45 | 46 | Trying to use Vector/Matrix operations on scalars (e.g., `scalar * vector` or `scalar * matrix`) can fail because scalars are defined in Scala and the operators in the standard library can clash with those defined by Scalala. There are two work-arounds: use the colon-prefixed operator (e.g., `scalar :* matrix`) or flip the order of the operands (e.g., `matrix * scalar`). 47 | 48 | ### Scalala Vector vs Scala Vector 49 | 50 | If you're familiar with Scala, you'll know there's a collection type called scala.collection.immutable.Vector. This is not related at all to Scalala's scala.tensor.mutable.Vector type. In this project, we'll use "Vector" to mean the latter type unless otherwise noted. 51 | 52 | [0]: http://www.ml-class.org/ 53 | [1]: https://github.com/scalala/Scalala 54 | [2]: https://github.com/harrah/xsbt/wiki/Setup 55 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | scalaVersion := "2.9.1" 2 | 3 | resolvers ++= Seq( 4 | "ondex" at "http://ondex.rothamsted.bbsrc.ac.uk/nexus/content/groups/public", 5 | "ScalaNLP Maven2" at "http://repo.scalanlp.org/repo") 6 | 7 | libraryDependencies ++= Seq( 8 | "org.scalala" %% "scalala" % "1.0.0.RC2-SNAPSHOT") 9 | --------------------------------------------------------------------------------