All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ml.combust.mleap.tensor.Tensor.scala Maven / Gradle / Ivy

The newest version!
package ml.combust.mleap.tensor

import java.util
import java.util.Comparator

import scala.language.implicitConversions
import scala.reflect.{ClassTag, classTag}

/**
  * Created by hollinwilkins on 1/12/17.
  */
object Tensor {
  val BooleanClass: Class[Boolean] = classOf[Boolean]
  val ByteClass: Class[Byte] = classOf[Byte]
  val ShortClass: Class[Short] = classOf[Short]
  val IntClass: Class[Int] = classOf[Int]
  val LongClass: Class[Long] = classOf[Long]
  val FloatClass: Class[Float] = classOf[Float]
  val DoubleClass: Class[Double] = classOf[Double]
  val StringClass: Class[String] = classOf[String]
  val ByteStringClass: Class[ByteString] = classOf[ByteString]

  def create[T: ClassTag](values: Array[T],
                          dimensions: Seq[Int],
                          indices: Option[Seq[Seq[Int]]] = None): Tensor[T] = {
    indices match {
      case Some(is) => {
        if (!isKnownDimensions(dimensions))
          throw new IllegalArgumentException("dimensions must be known for SparseTensor")
        SparseTensor(is, values, dimensions)
      }
      case None => {
        DenseTensor(values, normalizeDimensions(values.length, dimensions))
      }
    }
  }

  def denseVector[T: ClassTag](values: Array[T]): DenseTensor[T] = DenseTensor(values, Seq(values.length))
  def scalar[T: ClassTag](value: T): DenseTensor[T] = DenseTensor(Array(value), Seq())
  def denseIndex(indices: Seq[Int], dimensions: Seq[Int]): Int = {
    var n = indices.last
    var r = dimensions
    for(i <- 0 until (indices.length - 1)) {
      r = r.tail
      n += indices(i) * r.product
    }
    n
  }
  def isKnownDimensions(dimensions: Seq[Int]): Boolean =  dimensions.count(dim => dim == -1) == 0

  def normalizeDimensions(size: Int, dimensions: Seq[Int]) : Seq[Int] = {
    val numOfUnknownDimensions = dimensions.count(dim => dim == -1)
    if (numOfUnknownDimensions > 1)
      throw new  IllegalArgumentException("dimensions contains more then one `-1`")

    val normalizedDimensions = if (numOfUnknownDimensions == 1) {
      val concreteDimension = size / dimensions.filter(dim => dim != -1 ).product
      dimensions.map(dim =>  if(dim == -1) concreteDimension else dim)
    } else {
      dimensions
    }
    if(normalizedDimensions.product != size)
      throw  new IllegalArgumentException("size of dimensions must equals size of values")

    normalizedDimensions
  }
}

sealed trait Tensor[T] {
  val dimensions: Seq[Int]
  implicit val base: ClassTag[T]

  def isDense: Boolean = false
  def isSparse: Boolean = false

  def toDense: DenseTensor[T]
  def toArray: Array[T]

  def size: Int = dimensions.product
  def rawSize: Int = rawValues.length
  def rawValues: Array[T]
  def rawValuesIterator: Iterator[T]
  def mapValues[T2: ClassTag](f: (T) => T2): Tensor[T2]

  def apply(indices: Int *): T = get(indices: _*).get
  def get(indices: Int *): Option[T]

  override def equals(obj: Any): Boolean = {
    obj match {
      case obj: Tensor[T] =>
        base == obj.base &&
          dimensions == obj.dimensions &&
          rawValues.sameElements[T](obj.rawValues)
      case _ => false
    }
  }
}

case class DenseTensor[T](values: Array[T],
                          override val dimensions: Seq[Int])
                         (implicit override val base: ClassTag[T]) extends Tensor[T] {
  override def isDense: Boolean = true
  override def toDense: DenseTensor[T] = this
  override def toArray: Array[T] = values

  override def rawValues: Array[T] = values
  override def rawValuesIterator: Iterator[T] = values.iterator
  override def mapValues[T2: ClassTag](f: (T) => T2): Tensor[T2] = {
    DenseTensor(values.map(f), dimensions)(classTag[T2])
  }

  override def get(indices: Int *): Option[T] = {
    val n = Tensor.denseIndex(indices, dimensions)
    if (values.length > n) {
      Some(values(n))
    } else { None }
  }

  override def equals(obj: Any): Boolean = obj match {
    case obj: DenseTensor[T] => super.equals(obj)
    case _ => false
  }
}

case class SparseTensor[T](indices: Seq[Seq[Int]],
                           values: Array[T],
                           override val dimensions: Seq[Int])
                          (implicit override val base: ClassTag[T]) extends Tensor[T] {
  override def isSparse: Boolean = true

  override def toDense: DenseTensor[T] = {
    DenseTensor(toArray, dimensions)
  }
  override def toArray: Array[T] = {
    val array = new Array[T](dimensions.product)
    var i = 0
    indices.foreach {
      index =>
        array(Tensor.denseIndex(index, dimensions)) = values(i)
        i += 1
    }
    array
  }

  override def rawValues: Array[T] = values
  override def rawValuesIterator: Iterator[T] = values.iterator
  override def mapValues[T2: ClassTag](f: (T) => T2): Tensor[T2] = {
    SparseTensor(indices, values.map(f), dimensions)(classTag[T2])
  }

  override def get(is: Int *): Option[T] = {
    val index = util.Arrays.binarySearch(indices.toArray, is, new Comparator[Seq[Int]] {
      override def compare(o1: Seq[Int], o2: Seq[Int]): Int = {
        for((v1, v2) <- o1.zip(o2)) {
          val c = v1.compareTo(v2)
          if(c != 0) { return c }
        }

        0
      }
    })

    if(index >= 0) {
      Some(values(index))
    } else { None }
  }

  override def equals(obj: Any): Boolean = obj match {
    case obj: SparseTensor[T] =>
        indices == obj.indices && super.equals(obj)
    case _ => false
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy