Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Upcoming SlideShare
What to Upload to SlideShare
What to Upload to SlideShare
Loading in …3
×
1 of 113

Building Machine Learning Algorithms on Apache Spark: Scaling Out and Up with William Benton

1

Share

Download to read offline

There are lots of reasons why you might want to implement your own machine learning algorithms on Spark: you might want to experiment with a new idea, try and reproduce results from a recent research paper, or simply to use an existing technique that isn’t implemented in MLlib.

In this talk, we’ll walk through the process of developing a new machine learning algorithm for Spark. We’ll start with the basics, by considering how we’d design a scale-out parallel implementation of our unsupervised learning technique. The bulk of the talk will focus on the details you need to know to turn an algorithm design into an efficient parallel implementation on Spark.

We’ll start by reviewing a simple RDD-based implementation, show some improvements, point out some pitfalls to avoid, and iteratively extend our implementation to support contemporary Spark features like ML Pipelines and structured query processing. We’ll conclude by briefly examining some useful techniques to complement scale-out performance by scaling our code up, taking advantage of specialized hardware to accelerate single-worker performance.

You’ll leave this talk with everything you need to build a new machine learning technique that runs on Spark.

Building Machine Learning Algorithms on Apache Spark: Scaling Out and Up with William Benton

  1. 1. Building parallel machine learning algorithms: scaling out and up William Benton
 willb@redhat.com • @willb
  2. 2. Motivation
  3. 3. Motivation
  4. 4. Motivation
  5. 5. Motivation
  6. 6. Forecast Introducing our case study: self-organizing maps Parallel implementations for partitioned collections (in particular, RDDs) Beyond the RDD: data frames and ML pipelines Practical considerations and key takeaways
  7. 7. Introducing our case study
  8. 8. Training self-organizing maps
  9. 9. Training self-organizing maps
  10. 10. Training self-organizing maps
  11. 11. Training self-organizing maps
  12. 12. Training self-organizing maps while t < maxupdates: random.shuffle(examples) for ex in examples: t = t + 1 if t == maxupdates: break bestMatch = closest(somt, ex) for (unit, wt) in neighborhood(bestMatch, sigma(t)): somt+1[unit] = somt[unit] + (ex - somt[unit]) * alpha(t) * wt
  13. 13. Training self-organizing maps while t < maxupdates: random.shuffle(examples) for ex in examples: t = t + 1 if t == maxupdates: break bestMatch = closest(somt, ex) for (unit, wt) in neighborhood(bestMatch, sigma(t)): somt+1[unit] = somt[unit] + (ex - somt[unit]) * alpha(t) * wt process the training set in random order
  14. 14. Training self-organizing maps while t < maxupdates: random.shuffle(examples) for ex in examples: t = t + 1 if t == maxupdates: break bestMatch = closest(somt, ex) for (unit, wt) in neighborhood(bestMatch, sigma(t)): somt+1[unit] = somt[unit] + (ex - somt[unit]) * alpha(t) * wt process the training set in random order the neighborhood size controls how much of the map around the BMU is affected
  15. 15. Training self-organizing maps while t < maxupdates: random.shuffle(examples) for ex in examples: t = t + 1 if t == maxupdates: break bestMatch = closest(somt, ex) for (unit, wt) in neighborhood(bestMatch, sigma(t)): somt+1[unit] = somt[unit] + (ex - somt[unit]) * alpha(t) * wt process the training set in random order the neighborhood size controls how much of the map around the BMU is affected the learning rate controls how much closer to the example each unit gets
  16. 16. Parallel implementations for partitioned collections
  17. 17. Historical aside: Amdahl’s Law 1 1 - p lim So =sp —> ∞
  18. 18. What forces serial execution?
  19. 19. What forces serial execution?
  20. 20. What forces serial execution? state[t+1] = combine(state[t], x)
  21. 21. What forces serial execution? state[t+1] = combine(state[t], x)
  22. 22. What forces serial execution? f1: (T, T) => T f2: (T, U) => T
  23. 23. What forces serial execution? f1: (T, T) => T f2: (T, U) => T
  24. 24. What forces serial execution? f1: (T, T) => T f2: (T, U) => T
  25. 25. How can we fix these? a ⊕ b = b ⊕ a (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)
  26. 26. How can we fix these? a ⊕ b = b ⊕ a (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)
  27. 27. How can we fix these? a ⊕ b = b ⊕ a (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)
  28. 28. How can we fix these? a ⊕ b = b ⊕ a (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)
  29. 29. How can we fix these? L-BGFSSGD a ⊕ b = b ⊕ a (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c)
  30. 30. How can we fix these? SGD L-BGFS a ⊕ b = b ⊕ a (a ⊕ b) ⊕ c = a ⊕ (b ⊕ c) There will be examples of each of these approaches for many problems in the literature and in open-source code!
  31. 31. Implementing atop RDDs We’ll start with a batch implementation of our technique: for t in (1 to iterations): state = newState() for ex in examples: bestMatch = closest(somt-1, ex) hood = neighborhood(bestMatch, sigma(t)) state.matches += ex * hood state.hoods += hood somt = newSOM(state.matches / state.hoods)
  32. 32. Implementing atop RDDs for t in (1 to iterations): state = newState() for ex in examples: bestMatch = closest(somt-1, ex) hood = neighborhood(bestMatch, sigma(t)) state.matches += ex * hood state.hoods += hood somt = newSOM(state.matches / state.hoods) Each batch produces a model that can be averaged with other models
  33. 33. Implementing atop RDDs Each batch produces a model that can be averaged with other models partition for t in (1 to iterations): state = newState() for ex in examples: bestMatch = closest(somt-1, ex) hood = neighborhood(bestMatch, sigma(t)) state.matches += ex * hood state.hoods += hood somt = newSOM(state.matches / state.hoods)
  34. 34. Implementing atop RDDs This won’t always work! for t in (1 to iterations): state = newState() for ex in examples: bestMatch = closest(somt-1, ex) hood = neighborhood(bestMatch, sigma(t)) state.matches += ex * hood state.hoods += hood somt = newSOM(state.matches / state.hoods)
  35. 35. An implementation template var nextModel = initialModel for (int i = 0; i < iterations; i++) { val current = sc.broadcast(nextModel) val newState = examples.aggregate(ModelState.empty()) { { case (state: ModelState, example: Example) => state.update(current.value.lookup(example, i), example) } { case (s1: ModelState, s2: ModelState) => s1.combine(s2) } } nextModel = modelFromState(newState) current.unpersist } var nextModel = initialModel for (int i = 0; i < iterations; i++) { val current = sc.broadcast(nextModel) val newState = examples.aggregate(ModelState.empty()) { { case (state: ModelState, example: Example) => state.update(current.value.lookup(example, i), example) } { case (s1: ModelState, s2: ModelState) => s1.combine(s2) } } nextModel = modelFromState(newState) current.unpersist }
  36. 36. An implementation template “fold”: update the state for this partition with a single new example var nextModel = initialModel for (int i = 0; i < iterations; i++) { val current = sc.broadcast(nextModel) val newState = examples.aggregate(ModelState.empty()) { { case (state: ModelState, example: Example) => state.update(current.value.lookup(example, i), example) } { case (s1: ModelState, s2: ModelState) => s1.combine(s2) } } nextModel = modelFromState(newState) current.unpersist } var nextModel = initialModel for (int i = 0; i < iterations; i++) { val current = sc.broadcast(nextModel) val newState = examples.aggregate(ModelState.empty()) { { case (state: ModelState, example: Example) => state.update(current.value.lookup(example, i), example) } { case (s1: ModelState, s2: ModelState) => s1.combine(s2) } } nextModel = modelFromState(newState) current.unpersist }
  37. 37. An implementation template “reduce”: combine the states from two partitions var nextModel = initialModel for (int i = 0; i < iterations; i++) { val current = sc.broadcast(nextModel) val newState = examples.aggregate(ModelState.empty()) { { case (state: ModelState, example: Example) => state.update(current.value.lookup(example, i), example) } { case (s1: ModelState, s2: ModelState) => s1.combine(s2) } } nextModel = modelFromState(newState) current.unpersist } var nextModel = initialModel for (int i = 0; i < iterations; i++) { val current = sc.broadcast(nextModel) val newState = examples.aggregate(ModelState.empty()) { { case (state: ModelState, example: Example) => state.update(current.value.lookup(example, i), example) } { case (s1: ModelState, s2: ModelState) => s1.combine(s2) } } nextModel = modelFromState(newState) current.unpersist }
  38. 38. An implementation template var nextModel = initialModel for (int i = 0; i < iterations; i++) { val current = sc.broadcast(nextModel) val newState = examples.aggregate(ModelState.empty()) { { case (state: ModelState, example: Example) => state.update(current.value.lookup(example, i), example) } { case (s1: ModelState, s2: ModelState) => s1.combine(s2) } } nextModel = modelFromState(newState) current.unpersist } var nextModel = initialModel for (int i = 0; i < iterations; i++) { val current = sc.broadcast(nextModel) val newState = examples.aggregate(ModelState.empty()) { { case (state: ModelState, example: Example) => state.update(current.value.lookup(example, i), example) } { case (s1: ModelState, s2: ModelState) => s1.combine(s2) } } nextModel = modelFromState(newState) current.unpersist } remove the stale broadcasted model broadcast the current working model for this iteration
  39. 39. workersdriver (aggregate) Implementing on RDDs ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕
  40. 40. workersdriver (aggregate) Implementing on RDDs ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕
  41. 41. workersdriver (aggregate) Implementing on RDDs
  42. 42. workersdriver (treeAggregate) Implementing on RDDs ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕
  43. 43. workersdriver (treeAggregate) Implementing on RDDs ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕
  44. 44. workersdriver (treeAggregate) Implementing on RDDs ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕
  45. 45. workersdriver (treeAggregate) Implementing on RDDs ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ driver (treeAggregate) ⊕ ⊕ ⊕ ⊕
  46. 46. workersdriver (treeAggregate) Implementing on RDDs ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕ ⊕
  47. 47. workersdriver (treeAggregate) Implementing on RDDs ⊕ ⊕
  48. 48. driver (treeAggregate) workers Implementing on RDDs ⊕ ⊕
  49. 49. driver (treeAggregate) workers Implementing on RDDs ⊕ ⊕⊕
  50. 50. Beyond the RDD: Data frames and ML Pipelines
  51. 51. RDDs: some good parts val rdd: RDD[String] = /* ... */ rdd.map(_ * 3.0).collect() val df: DataFrame = /* data frame with one String-valued column */ df.select($"_1" * 3.0).show()
  52. 52. RDDs: some good parts val rdd: RDD[String] = /* ... */ rdd.map(_ * 3.0).collect() val df: DataFrame = /* data frame with one String-valued column */ df.select($"_1" * 3.0).show() doesn’t compile
  53. 53. RDDs: some good parts val rdd: RDD[String] = /* ... */ rdd.map(_ * 3.0).collect() val df: DataFrame = /* data frame with one String-valued column */ df.select($"_1" * 3.0).show() doesn’t compile
  54. 54. RDDs: some good parts val rdd: RDD[String] = /* ... */ rdd.map(_ * 3.0).collect() val df: DataFrame = /* data frame with one String-valued column */ df.select($"_1" * 3.0).show() doesn’t compile crashes at runtime
  55. 55. RDDs: some good parts rdd.map { vec => (vec, model.value.closestWithSimilarity(vec)) } val predict = udf ((vec: SV) => model.value.closestWithSimilarity(vec)) df.withColumn($"predictions", predict($"features"))
  56. 56. RDDs: some good parts rdd.map { vec => (vec, model.value.closestWithSimilarity(vec)) } val predict = udf ((vec: SV) => model.value.closestWithSimilarity(vec)) df.withColumn($"predictions", predict($"features"))
  57. 57. RDDs versus query planning val numbers1 = sc.parallelize(1 to 100000000) val numbers2 = sc.parallelize(1 to 1000000000) numbers1.cartesian(numbers2) .map((x, y) => (x, y, expensive(x, y))) .filter((x, y, _) => isPrime(x), isPrime(y))
  58. 58. RDDs versus query planning val numbers1 = sc.parallelize(1 to 100000000) val numbers2 = sc.parallelize(1 to 1000000000) numbers1.filter(isPrime(_)) .cartesian(numbers2.filter(isPrime(_))) .map((x, y) => (x, y, expensive(x, y)))
  59. 59. RDDs and the Java heap val mat = Array(Array(1.0, 2.0), Array(3.0, 4.0))
  60. 60. RDDs and the Java heap val mat = Array(Array(1.0, 2.0), Array(3.0, 4.0)) class pointer flags size locks element pointer element pointer class pointer flags size locks 1.0 class pointer flags size locks 3.0 4.0 2.0
  61. 61. RDDs and the Java heap val mat = Array(Array(1.0, 2.0), Array(3.0, 4.0)) class pointer flags size locks element pointer element pointer class pointer flags size locks 1.0 class pointer flags size locks 3.0 4.0 2.0 32 bytes of data…
  62. 62. RDDs and the Java heap val mat = Array(Array(1.0, 2.0), Array(3.0, 4.0)) class pointer flags size locks element pointer element pointer class pointer flags size locks 1.0 class pointer flags size locks 3.0 4.0 2.0 …and 64 bytes of overhead! 32 bytes of data…
  63. 63. ML pipelines: a quick example from pyspark.ml.clustering import KMeans K, SEED = 100, 0xdea110c8 randomDF = make_random_df() kmeans = KMeans().setK(K).setSeed(SEED).setFeaturesCol("features") model = kmeans.fit(randomDF) withPredictions = model.transform(randomDF).select("x", "y", "prediction")
  64. 64. Working with ML pipelines estimator.fit(df)
  65. 65. Working with ML pipelines estimator.fit(df) model.transform(df)
  66. 66. Working with ML pipelines model.transform(df)
  67. 67. Working with ML pipelines model.transform(df)
  68. 68. Working with ML pipelines estimator.fit(df) model.transform(df)
  69. 69. Working with ML pipelines estimator.fit(df) model.transform(df) inputCol epochs seed outputCol
  70. 70. private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ... Defining parameters private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ...
  71. 71. private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ... Defining parameters private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ...
  72. 72. Defining parameters private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ... private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ...
  73. 73. Defining parameters private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ... private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ...
  74. 74. Defining parameters private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ... private[som] trait SOMParams extends Params with DefaultParamsWritable { final val x: IntParam = new IntParam(this, "x", "width of self-organizing map (>= 1)", ParamValidators.gtEq(1)) final def getX: Int = $(x) final def setX(value: Int): this.type = set(x, value) // ...
  75. 75. Don’t repeat yourself /** * Common params for KMeans and KMeansModel */ private[clustering] trait KMeansParams extends Params with HasMaxIter with HasFeaturesCol with HasSeed with HasPredictionCol with HasTol { /* ... */ }
  76. 76. Estimators and transformers estimator.fit(df)
  77. 77. Estimators and transformers estimator.fit(df)
  78. 78. Estimators and transformers estimator.fit(df) model.transform(df)
  79. 79. Estimators and transformers estimator.fit(df) model.transform(df)
  80. 80. Estimators and transformers estimator.fit(df) model.transform(df)
  81. 81. Validate and transform at once def transformSchema(schema: StructType): StructType = { // check that the input columns exist... // ...and are the proper type // ...and that the output columns don’t exist // ...and then make a new schema }
  82. 82. Validate and transform at once def transformSchema(schema: StructType): StructType = { // check that the input columns exist… require(schema.fieldNames.contains($(featuresCol))) // ...and are the proper type // ...and that the output columns don’t exist // ...and then make a new schema }
  83. 83. Validate and transform at once def transformSchema(schema: StructType): StructType = { // check that the input columns exist... // ...and are the proper type schema($(featuresCol)) match { case sf: StructField => require(sf.dataType.equals(VectorType)) } // ...and that the output columns don’t exist // ...and then make a new schema }
  84. 84. Validate and transform at once def transformSchema(schema: StructType): StructType = { // check that the input columns exist… // ...and are the proper type // ...and that the output columns don’t exist require(!schema.fieldNames.contains($(predictionCol))) require(!schema.fieldNames.contains($(similarityCol))) // ...and then make a new schema }
  85. 85. Validate and transform at once def transformSchema(schema: StructType): StructType = { // check that the input columns exist… // ...and are the proper type // ...and that the output columns don’t exist // ...and then make a new schema schema.add($(predictionCol), "int") .add($(similarityCol), "double") }
  86. 86. Training on data frames def fit(examples: DataFrame) = { import examples.sparkSession.implicits._ import org.apache.spark.ml.linalg.{Vector=>SV} val dfexamples = examples.select($(exampleCol)).rdd.map { case Row(sv: SV) => sv } /* construct a model object with the result of training */ new SOMModel(train(dfexamples, $(x), $(y))) }
  87. 87. Practical considerations
  88. 88. Improve serial execution times 2 6 6 4 0 0 1 0 1 0 0 0 1 1 0 0 3 7 7 5 • 2 4 0.1 0.7 0.2 3 5 <latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit>
  89. 89. Improve serial execution times 2 6 6 4 0 0 1 0 1 0 0 0 1 1 0 0 3 7 7 5 • 2 4 0.1 0.7 0.2 3 5 <latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit>
  90. 90. Improve serial execution times 2 6 6 4 0 0 1 0 1 0 0 0 1 1 0 0 3 7 7 5 • 2 4 0.1 0.7 0.2 3 5 <latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit><latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit>
  91. 91. <latexit sha1_base64="F0lpnVum/z3V85cOiWhHJHNgpmw=">AAACFHicbVDLSsNAFJ34rPUVdelmsAiCUBIRdFl047KCfUATymQ6aYdOkunMjVDSfoQbf8WNC0XcunDn3zhtI2jrgbkczrmXO/cEUnANjvNlLS2vrK6tFzaKm1vbO7v23n5dJ6mirEYTkahmQDQTPGY14CBYUypGokCwRtC/nviNe6Y0T+I7GErmR6Qb85BTAkZq26deqAjNJPaCVAgGeDDORiMssSk/kqEDU8Ztu+SUnSnwInFzUkI5qm370+skNI1YDFQQrVuuI8HPiAJOBRsXvVQzSWifdFnL0JhETPvZ9KgxPjZKB4eJMi8GPFV/T2Qk0noYBaYzItDT895E/M9rpRBe+hmPZQosprNFYSowJHiSEO5wxSiIoSGEKm7+immPmJTA5Fg0IbjzJy+S+lnZdcru7XmpcpXHUUCH6AidIBddoAq6QVVUQxQ9oCf0gl6tR+vZerPeZ61LVj5zgP7A+vgG1reeqw==</latexit> ||q||<latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit>
  92. 92. <latexit sha1_base64="F0lpnVum/z3V85cOiWhHJHNgpmw=">AAACFHicbVDLSsNAFJ34rPUVdelmsAiCUBIRdFl047KCfUATymQ6aYdOkunMjVDSfoQbf8WNC0XcunDn3zhtI2jrgbkczrmXO/cEUnANjvNlLS2vrK6tFzaKm1vbO7v23n5dJ6mirEYTkahmQDQTPGY14CBYUypGokCwRtC/nviNe6Y0T+I7GErmR6Qb85BTAkZq26deqAjNJPaCVAgGeDDORiMssSk/kqEDU8Ztu+SUnSnwInFzUkI5qm370+skNI1YDFQQrVuuI8HPiAJOBRsXvVQzSWifdFnL0JhETPvZ9KgxPjZKB4eJMi8GPFV/T2Qk0noYBaYzItDT895E/M9rpRBe+hmPZQosprNFYSowJHiSEO5wxSiIoSGEKm7+immPmJTA5Fg0IbjzJy+S+lnZdcru7XmpcpXHUUCH6AidIBddoAq6QVVUQxQ9oCf0gl6tR+vZerPeZ61LVj5zgP7A+vgG1reeqw==</latexit> ||q||<latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit>
  93. 93. ||q||<latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit><latexit sha1_base64="Fotu1JHT5+QrvqFi2jYz5La5cyI=">AAAB7nicbVBNS8NAEJ3Ur1q/qh69LBbBU0lE0GPRi8cK9gPaUDbbTbt0s4m7E6Gk/RFePCji1d/jzX/jts1BWx8MPN6bYWZekEhh0HW/ncLa+sbmVnG7tLO7t39QPjxqmjjVjDdYLGPdDqjhUijeQIGStxPNaRRI3gpGtzO/9cS1EbF6wHHC/YgOlAgFo2il1mRCHslk0itX3Ko7B1klXk4qkKPeK391+zFLI66QSWpMx3MT9DOqUTDJp6VuanhC2YgOeMdSRSNu/Gx+7pScWaVPwljbUkjm6u+JjEbGjKPAdkYUh2bZm4n/eZ0Uw2s/EypJkSu2WBSmkmBMZr+TvtCcoRxbQpkW9lbChlRThjahkg3BW355lTQvqp5b9e4vK7WbPI4inMApnIMHV1CDO6hDAxiM4Ble4c1JnBfn3flYtBacfOYY/sD5/AESAY9h</latexit>
  94. 94. <latexit sha1_base64="sgjZxc6SmNq0zTGtiQFaoRPCk2s=">AAACQHicbVC7SgNBFJ2NrxhfUUubwaBYhd0YH2XQxjKCeUB2CbOzN8mQ2dllZlYMSz7Nxk+ws7axUMTWyskDNIkHDhzOPZe5c/yYM6Vt+8XKLC2vrK5l13Mbm1vbO/ndvbqKEkmhRiMeyaZPFHAmoKaZ5tCMJZDQ59Dw+9ejeeMepGKRuNODGLyQdAXrMEq0sdr5hutDl4nUD4mW7GGIHXyMS4an2AUR/Pqun3AOGs/nyyZ7Zng+m2/nC3bRHgMvCmcqCmiKajv/7AYRTUIQmnKiVMuxY+2lRGpGOQxzbqIgJrRPutAyUpAQlJeOCxjiI+MEuBNJQ6Hx2P27kZJQqUHom6S5r6fmZyPzv1kr0Z1LL2UiTjQIOnmok3CsIzxqEwdMAtV8YAShkplbMe0RSag2nedMCc78lxdFvVR07KJzWy5UrqZ1ZNEBOkQnyEEXqIJuUBXVEEWP6BW9ow/ryXqzPq2vSTRjTXf20Qys7x/ZKq4m</latexit>
  95. 95. ⇥ 0 0 1 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 0 1 0 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 0 0 1 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 1 0 0 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ <latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit>
  96. 96. ⇥ 0 0 1 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 0 1 0 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 0 0 1 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 1 0 0 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ <latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit> <latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit>
  97. 97. ⇥ 0 0 1 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 0 1 0 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 0 0 1 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ ⇥ 1 0 0 ⇤ • ⇥ 0.1 0.7 0.2 ⇤ <latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit><latexit sha1_base64="zslMj5nDZfArLAY/7Q+RF8nfHL4=">AAADanictVJNS8NAEN0mftT61VZQxMtiVTyFpAj1WPTisYJVoSlls53Wxc0m7G7EEgr+Rm/+Ai/+CDdpQG096sAujzfzeDPDBDFnSrvuW8myl5ZXVstrlfWNza3taq1+q6JEUujSiEfyPiAKOBPQ1UxzuI8lkDDgcBc8Xmb5uyeQikXiRk9i6IdkLNiIUaINNaiVXvwAxkykQUi0ZM9T7OKT/HnYBzH84v0g4Rw0Xqh3vEzhtPK/Oa/yK78Z5JL/NPjXCbzC4E8mGFQbruPmgReBV4AGKqIzqL76w4gmIQhNOVGq57mx7qdEakY5TCt+oiAm9JGMoWegICGofpqfyhQfG2aIR5E0T2ics98VKQmVmoSBqTT9Paj5XEb+luslenTeT5mIEw2CzoxGCcc6wtnd4SGTQDWfGECoZKZXTB+IJFSb66yYJXjzIy+C26bjmV1enzXaF8U6yugAHaJT5KEWaqMr1EFdREvv1qa1a+1ZH3bd3rcPZqVWqdDsoB9hH30CrJcKDw==</latexit> <latexit sha1_base64="yGPDKZdOXUPJZlyEHQdtfy2cnBM=">AAACcHicbVHLSgMxFM2Mr1pf1W4EFaNFERdlpgh1WXTjUsE+oFNKJr1tQzOZIcmIZeja/3PnR7jxC0yng9rWCyHnnnPvTXLiR5wp7Tgflr2yura+kdvMb23v7O4V9g8aKowlhToNeShbPlHAmYC6ZppDK5JAAp9D0x/dT/XmC0jFQvGsxxF0AjIQrM8o0YbqFt48HwZMJH5AtGSvE+zgy3S52PMyMCWy5Edxs8QoIHq/7Z4fcw4aL40tzwaWqzif7pX5xm6h5JSdNPAycDNQQlk8dgvvXi+kcQBCU06UartOpDsJkZpRDpO8FyuICB2RAbQNFCQA1UlSwyb4wjA93A+lWULjlP3bkZBAqXHgm0pzv6Fa1Kbkf1o71v3bTsJEFGsQdHZQP+ZYh3jqPu4xCVTzsQGESmbuiumQSEK1+aO8McFdfPIyaFTKrrHz6aZUu8vsyKEjdI6ukIuqqIYe0COqI4o+raJ1bJ1YX/ahfWqfzUptK+spormwr78BvNCySg==</latexit> libraryDependencies += "org.scalanlp" %% "breeze-natives" % "0.13.1"
  98. 98. val vec = Array[Double](/* ... */)
  99. 99. val vec = Array[Double](/* ... */)
  100. 100. def dot[S](a: Array[S], b: Array[S]) (implicit num: Numeric[S]): S = { import num._ (0 until a.length).foldLeft(num.zero)({ (acc, i) => acc + a(i) * b(i) }) } val vec = Array[Double](/* ... */)
  101. 101. def dot[S](a: Array[S], b: Array[S]) (implicit num: Numeric[S]): S = { import num._ (0 until a.length).foldLeft(num.zero)({ (acc, i) => acc + a(i) * b(i) }) } val vec = Array[Double](/* ... */) dot(Array(0.1d, 0.2d, 0.3d), Array(1.0d, 0.0d, 0.0d)) dot(Array(0.1f, 0.2f, 0.3f), Array(1.0f, 0.0f, 0.0f))
  102. 102. val vec = Array[Double](/* ... */) vdppd vdppd 0.1d 0.2d 1.0d 0.0d 0.3d (unused) 0.0d (unused) def dot[S](a: Array[S], b: Array[S]) (implicit num: Numeric[S]): S = { import num._ (0 until a.length).foldLeft(num.zero)({ (acc, i) => acc + a(i) * b(i) }) }
  103. 103. val vec = Array[Double](/* ... */) vdppd vdppd 0.1d 0.2d 1.0d 0.0d 0.3d (unused) 0.0d (unused) vdpps 0.3f unused0.2f0.1f 0.0f unused0.0f1.0f def dot[S](a: Array[S], b: Array[S]) (implicit num: Numeric[S]): S = { import num._ (0 until a.length).foldLeft(num.zero)({ (acc, i) => acc + a(i) * b(i) }) }
  104. 104. KEY TAKEAWAYS
  105. 105. THANKS!willb@redhat.com • @willb https://chapeau.freevariable.com https://radanalytics.io also: “Spark for Library Developers” Room 2014 at 5:40 PM today

×