From 0f4739138cb6f86db6cb2fa5269b703361705819 Mon Sep 17 00:00:00 2001
From: Shardul Chiplunkar <shardul.chiplunkar@epfl.ch>
Date: Tue, 30 May 2023 18:32:50 +0200
Subject: [PATCH] Update convolution solution (oops mainly for completeness
 now)

---
 .../scala/midterm23/convolution.worksheet.sc  | 59 ++++++++++++++++---
 1 file changed, 52 insertions(+), 7 deletions(-)

diff --git a/src/main/scala/midterm23/convolution.worksheet.sc b/src/main/scala/midterm23/convolution.worksheet.sc
index 1f76d77..a2457d4 100644
--- a/src/main/scala/midterm23/convolution.worksheet.sc
+++ b/src/main/scala/midterm23/convolution.worksheet.sc
@@ -16,13 +16,30 @@ def sequentialConvolve(
   from: Int,
   until: Int
   ): Unit = {
+    // Approach A, as described in the clarification announcement for this
+    // exercise, where we treat `from` and `until` as indices on `output`
+    // instead of `input` as in the given code.
     var iOutput = from
     while iOutput < until do
       var iKernel = math.max(0, iOutput - input.length + 1)
       while iKernel < kernel.length && iKernel <= iOutput do
+        // `output` is only ever written to between the indices `from` and
+        // `until`, the range of `iOutput`. The indices for `input` and
+        // `kernel` are computed accordingly.
         output(iOutput) += input(iOutput - iKernel) * kernel(iKernel)
         iKernel += 1
       iOutput += 1
+
+    // ALTERNATE SOLUTION: Approach B, as described in the clarification
+    // announcement for this exercise, which is unchanged from the given
+    // code, i.e. we treat `from` and `until` as indices on `input`.
+    var iInput = from
+    while iInput < until do
+      var iKernel = 0
+      while iKernel < kernel.length do
+        output(iInput + iKernel) += input(iInput) * kernel(iKernel)
+        iKernel += 1
+      iInput += 1
   }
 
 def parallelConvolve(
@@ -32,7 +49,12 @@ def parallelConvolve(
     from: Int,
     until: Int,
     threshold: Int
-): Unit = 
+): Unit =
+  // Approach A, as described in the clarification announcement for this
+  // exercise, where we treat `from` and `until` as indices on `output`
+  // instead of `input` as in the given code. This does not require us to
+  // change anything in this function.  Only receives full credit if used
+  // together with Approach A for `sequentialConvolve`.
   if (until - from) <= threshold then
     sequentialConvolve(input, kernel, output, from, until)
   else
@@ -42,6 +64,31 @@ def parallelConvolve(
       parallelConvolve(input, kernel, output, mid, until, threshold)
     )
 
+  // ALTERNATE SOLUTION: Approach B, as described in the clarification
+  // announcement for this exercise, where we treat `from` and `until` as
+  // indices on `input` as in the given code. This requires up to leave a
+  // gap in the parallel calls to be filled in sequentially as described
+  // below. Only receives full credit if used together with Approach B for
+  // `sequentialConvolve`.
+  if (until - from) <= threshold then
+    sequentialConvolve(input, kernel, output, from, until)
+  else
+    val mid = from + (until - from) / 2
+    val gap = numContributeTo(input, kernel, output, mid)
+    // Leave a gap large enough that accesses to `output` from the first
+    // parallel call will not overlap with accesses from the second. This
+    // gap is of size equal to the number of elements of `input` that will
+    // contribute to the computation of the result at the lowest index of
+    // `output` in the second parallel call, namely, at index `mid`.
+    // However, we are careful not to access indices lower than `from`.
+    val gapBeforeMid = Math.max(from, mid - gap + 1)
+    parallel(
+      parallelConvolve(input, kernel, output, from, gapBeforeMid, threshold),
+      parallelConvolve(input, kernel, output, mid, until, threshold)
+    )
+    // Fill in the gap sequentially.
+    sequentialConvolve(input, kernel, output, gapBeforeMid, mid)
+
 object Original_WithOutputRace:
 
   def sequentialConvolve(
@@ -50,15 +97,13 @@ object Original_WithOutputRace:
       output: Array[Int],
       from: Int,
       until: Int): Unit =
-    var iOutput = from
-    while iOutput < until do
+    var iInput = from
+    while iInput < until do
       var iKernel = 0
       while iKernel < kernel.length do
-        val iInput = iOutput - iKernel
-        if 0 <= iInput && iInput < input.length then
-          output(iOutput) += input(iInput) * kernel(iKernel)
+        output(iInput + iKernel) += input(iInput) * kernel(iKernel)
         iKernel += 1
-      iOutput += 1
+      iInput += 1
 
   def parallelConvolve(
       input: Array[Int],
-- 
GitLab