본문 바로가기

학교 공부/프로그래밍원리

HW3 - (3) Matrix 구현하기

http://jaynewho.com/post/3

trait NDArrayOps[+T <: NDArray] {
  /**
   * @param shape shape of the result array
   * @param values values to fill
   * @return new array
   */
  def apply(shape: Seq[Int], values: Seq[Float]): T

  /**
   * Return new array with the given shape.
   * If the number of value in `values` does not match to the given `shape`,
   * raise `NNException`
   *
   * @param shape  shape of the result array
   * @param value values to fill
   * @return new array
   */
  def fill(shape: Seq[Int], value: Float): T

  /**
   * Stack the arrays with a one dimension lower along the first axis.
   * If there is an array in the values with a different shape,
   * then raise `NNException`.
   *
   * @param values sequence of the same-shaped (N-1)-dim arrays.
   * @return new array
   */
  def stack(values: Seq[NDArray]): T
}

 

trait NDArrayOps[+T <: NDArray] { }

가 정의됐는데, 저기에서 +T가 의미는 +T가 NDArray의 subtype임을 의미한다.

T가 아닌 굳이 +T로 적은 이유가 궁금하다. 나중에 찾아봐야징

 

trait NDArrayOps[+T <: NDArray] {
  def apply(shape: Seq[Int], values: Seq[Float]): T
  def fill(shape: Seq[Int], value: Float): T
  def stack(values: Seq[NDArray]): T
}

 

일단 NDArrayOps 는 메서드가 상대적으로 적게 정의됐다

apply

fill

stack 이 있는데 각각의 기능을 알아보자.

 

 

1.   def apply(shape: Seq[Int], values: Seq[Float]): T

shape param에 주어진 Seq[Int]에 따라서 새로운 array를 리턴한다. 약간 NDArray를 생성하는 생성자 느낌
   * Return new array with the given shape.
   * If the number of value in `values` does not match to the given `shape`, raise `NNException`

   * @param shape shape of the result array
   * @param values values to fill
   * @return new array
   */

 

2.   def fill(shape: Seq[Int], value: Float): T

  /**
   * Return new array with the given shape.
   * If the number of value in `values` does not match to the given `shape`,raise `NNException`
   *
   * @param shape  shape of the result array
   * @param value values to fill
   * @return new array
   */
...? 아까 apply랑 다른 점이 뭐임

 

 

 

3. def stack(values: Seq[NDArray]): T


   * Stack the arrays with a one dimension lower along the first axis.
   * If there is an array in the values with a different shape, then raise `NNException`.
   *
   * @param values sequence of the same-shaped (N-1)-dim arrays.
   * @return new array
   */

 

 

뭔소린지 하나도 모르겠음ㅋㅋㅋㅋㅋㅋ

일단 Matrix로 넘어가자

object Vector extends NDArrayOps[Vector] {
  def apply(values: Seq[Float]): Vector = new Vector(values.toArray)

  def apply(shape: Seq[Int], values: Seq[Float]): Vector = {
    if (shape.length != 1) {
      throw new NNException("the length of the given shape is not 1.")
    } else if (shape.head != values.length) {
      throw new NNException("the given shape does not equal to the number of values.")
    } else {
      new Vector(values.toArray)
    }
  }

  def fill(shape: Seq[Int], value: Float): Vector = {
    if (shape.length != 1) {
      throw new NNException("the length of the given shape is not 1.")
    } else {
      new Vector(Array.fill(shape.head)(value))
    }
  }

  def stack(values: Seq[NDArray]): Vector =
    throw new NNException("There is not an implementation of 0-dim array.")
}

 

빈칸 채운 버전 :

object Matrix extends NDArrayOps[Matrix] {
  def apply(values: Seq[Vector]): Matrix = new Matrix(values.toArray)

  def apply(shape: Seq[Int], values: Seq[Float]): Matrix = {
    if (shape.length != 2) {
      throw new NNException("the length of the given shape is not 2.")
    } else if ((shape(0)*shape(1)) != values.length) {
      throw new NNException("the given shape does not equal to the number of values.")
    } else {
      val vec:Vector = new Vector(values.toArray)
      vec.reshape(shape:_*)
    }
  }

  def fill(shape: Seq[Int], value: Float): Matrix = {
    if (shape.length != 2) {
      throw new NNException("the length of the given shape is not 2.")
    } else {
      val vec:Vector = new Vector(Array.fill(shape(1))(value))
      new Matrix(Array.fill(shape.head)(vec))
    }
  }

  def stack(values: Seq[NDArray]): Matrix = {
    val stand:Int = values(0).getShape.head
  	def check(idx:Int=0, res:Boolean=false):Boolean={
    	if(idx==values.length) res
        else{
    	if((values(idx).ndim != 1) || (values(idx).getShape.head != stand) true
        else check(idx+1, res)
        }
    }
    if (check()) throw new NNException("not every value has same shape")
    // TODO: check the every value in the values is Vector and has same shape.
	else new Matrix(values.asInstanceOf[Seq[Vector]].toArray)
  }
}

type mismatch가 떴다...... 하

 

Vector의 reshape을 정의할 때 사용했던 코드를 가지고 왔다.

// Vector(values:Array[Float])
def reshape(shape: Int*): NDArray = {
  def func(x: Array[Float], shape: Int*): NDArray = {
    def sep(i: Int, n: Int, res: Array[Float] = Array()): Array[Float] = {
      if (n == 0) res
      else sep(i + 1, n - 1, res ++ Array(x(i)))
    }
    val res: Array[Vector] = Array()
      for (x <- 1 to shape(0)) {
        val a = new Vector(sep((x - 1) * shape(1), shape(1)))
        res ++ Array(a)
      }
      //res 는 Array(Vector)
      new Matrix(res)
    }
  }
  func(this.values,shape:_*)
}

  def apply(shape: Seq[Int], values: Seq[Float]): Matrix = {
    if (shape.length != 2) {
      throw new NNException("the length of the given shape is not 2.")
    } else if ((shape(0) * shape(1)) != values.length) {
      throw new NNException("the given shape does not equal to the number of values.")
    } else {
      val vec: Vector = new Vector(values.toArray)
      vec.reshape(shape: _*)
    }
  }
  def apply(shape: Seq[Int], values: Seq[Float]): Matrix = {
    def sep(i: Int, n: Int = shape(1), res: Array[Float] = Array()): Array[Float] = {
      if (n == 0) res
      else sep(i + 1, n - 1, res ++ Array(values(i)))
    }

    def fin (idx: Int = 0, result: Array[Vector]= Array()): Array[Vector] = {
      if (idx == shape(0)) result
      else {
        val x: Array[Float] = sep(idx * shape(1))
        val y: Vector = new Vector(x)
        fin(idx + 1, result ++ Array(y))
      }
    }

    if (shape.length != 2) {
      throw new NNException("the length of the given shape is not 2.")
    } else if ((shape(0) * shape(1)) != values.length) {
      throw new NNException("the given shape does not equal to the number of values.")
    } else new Matrix(fin ())
  }

최종 apply !!! error는 안뜸.

 

 

 

 

 

 

 

2차원 배열에 대해서 메서드를 정의하면 된다. 

final class Matrix(val values: Array[Vector]) extends NDArray {
  val ndim: Int = 2
  val getShape: Array[Int] = Array(values.length, values(0).getShape(0))
  def getArr(i: Int): Vector = {
    if(i>=values.length) NNException("OutOfRange") //should raise an `NNException`
    else values(i)
  }
  def get(i: Int): Float = {
    if(values.getShape.length==1) values(i)
    else NNException("Not a Vector") //raise NNException
  }

  def reshape(shape: Int*): NDArray = ???

  def reduceLeft[T](f: (Float, Float) => Float): T = ???

  def unaryOp(f: Float => Float): NDArray = ???

  def binOp(f: (Float, Float) => Float, that: NDArray): NDArray = ???

  def equals(that: NDArray): Boolean = ???
}

 

아 또 reshape 에서 고비를 만났다........

일단 Vector에서 썼던 코드를 참고해보자

매트릭스의 2차원 배열은 모두 일차원으로 바꿔서

매트릭스의 value === Array(Vector)

그 Vector의 value === Array[Float]

메트릭스의 value의 value들을 하나의 Array[Float]으로 만든다음 Vector의 reshape를 사용하면 되지 않을까??

 

 

val res:Array[Vector] = this.values

val len = res.length (행의 개수)

val arr:Array[Float] = Array()

 

for (x <- 0 until len)

   arr + res(x).values

 

def toRow(idx:Int, arr:Array[Float] = Array())={

    if(idx==len) arr

   else {

   toRow(idx+1, arr ++ Array(res(idx).values)

   }

}

 

 

 

def reshape(shape: Int*): NDArray = {
  val res: Array[Vector] = this.values
  val len = res.length
  def func(x: Array[Float], shape: Int*): NDArray = {
    def sep(i: Int, n: Int, res: Array[Float] = Array()): Array[Float] = {
      if (n == 0) res
      else sep(i + 1, n - 1, res ++ Array(x(i)))}
    if (shape.length == 1) new Vector(x)
    else if (shape.length == 2) {
      val res: Array[Vector] = Array()
      for (x <- 1 to shape(0)) {
        val a = new Vector(sep((x - 1) * shape(1), shape(1)))
        res ++ Array(a)
      }
      //res 는 Array(Vector)
      new Matrix(res)
    }
    else {
      //3차원 이상은 수정하기
      new Vector(x)}}
  
  val length:Int = this.values.length
  def toRow(idx: Int, arr: Array[Float] = Array()):Array[Float] = {
    if (idx == length) arr
    else {
      val res:Array[Vector] = this.values
      val vect:Vector = res(idx)
      val a:Array[Float]= Array(vect.values)
      toRow(idx + 1,arr++a)
    }
  }
  func(toRow(0), shape:_*)
}

나중에 차차 검토해보는걸로 하고 ...

 

 

 

reduceLeft로 가보자

/**
 * Apply an element-wise binary operator to all elements along the first axis.
 * Note that f can be non-associative.
 *
 * e.g) [[2, 3, 5], [3, 1, 0]].reduceLeft(_ + _) = [5, 4, 5]
 * [[1, 2, 3]].reduceLeft(_ + _) = [1, 2, 3]
 *
 * @param f binary operator
 * @tparam T type of the inner element: NDArray (if this is Matrix or StackedArray) or Float (if this is Vector)
 * @return see Seq.reduceLeft
 */
def reduceLeft[T](f: (Float, Float) => Float): T = ???

Matrix의 shape이 (n,m)

빈 Array()에 m번 추가함

n은 인덱스느낌이고, shape(0)이랑 비교해서 stop하면 된다.

 

 

 * e.g) [[2, 3, 5], [3, 1, 0]].reduceLeft(_ + _) = [5, 4, 5]

 

def reduceLeft[T](f: (Float, Float) => Float): T = ???

type parameter에 대한 이해가 더 필요할 것 같다 .. 

 

val shape = this.getShape //Array[Int] = Array(2,3)

def calculate(idx:Int, arr:Array[T] = Array() ):Array[T] ={

if(idx  == shape(0)) arr

else {

val new_arr:Array[T] = arr ++ getArr(idx)

calculate(idx+1, 

}

 

}

 

 

 

 

 

 

 

 

 

 

 

 

'학교 공부 > 프로그래밍원리' 카테고리의 다른 글

HW4-(1)  (0) 2022.12.02
HW3 -(4) StackedArray 구현하기  (0) 2022.11.27
HW 3- (2) reshape 구현하기  (0) 2022.11.26
HW3 - (1)  (0) 2022.11.25
1122 Stacking with Type Classes  (0) 2022.11.22