diff --git a/core/src/main/scala/dimwit/tensor/Tensor.scala b/core/src/main/scala/dimwit/tensor/Tensor.scala index 027dba5..5aa4b60 100644 --- a/core/src/main/scala/dimwit/tensor/Tensor.scala +++ b/core/src/main/scala/dimwit/tensor/Tensor.scala @@ -330,9 +330,15 @@ object Tensor2: def apply[L1: Label, L2: Label](axisExtent1: AxisExtent[L1], axisExtent2: AxisExtent[L2]): Tensor.DefaultsFactory[Tuple2[L1, L2]] = Tensor.DefaultsFactory(Shape(axisExtent1, axisExtent2)) def apply[L1: Label, L2: Label, V](axisExtent1: AxisExtent[L1], axisExtent2: AxisExtent[L2], vtype: VType[V]): Tensor.TypedFactory[Tuple2[L1, L2], V] = Tensor.TypedFactory(Shape(axisExtent1, axisExtent2), vtype) - private def eyeImpl[L: Label, V](dim: AxisExtent[L], vtype: VType[V]): Tensor2[L, Prime[L], V] = Tensor(Jax.jnp.eye(dim.size, dtype = vtype.dtype.jaxType)) - def eye[L: Label](dim: AxisExtent[L]): Tensor2[L, Prime[L], Float32] = eyeImpl(dim, VType[Float32]) - def eye[L: Label, V](dim: AxisExtent[L], vtype: VType[V]): Tensor2[L, Prime[L], V] = eyeImpl(dim, vtype) + private def eyeImpl[T <: Tuple: Labels, V](n: Int, m: Int, vtype: VType[V] = VType[Float32]): Tensor[T, V] = Tensor(Jax.jnp.eye(n, m, dtype = vtype.dtype.jaxType)) + + def eye[L: Label](dim: AxisExtent[L]): Tensor2[L, Prime[L], Float32] = eye(dim, Axis[Prime[L]] -> dim.size) + def eye[L: Label, V](dim: AxisExtent[L], vtype: VType[V]): Tensor2[L, Prime[L], V] = eye(dim, Axis[Prime[L]] -> dim.size, vtype) + def eye[L1: Label, L2: Label](n: AxisExtent[L1], m: AxisExtent[L2]): Tensor2[L1, L2, Float32] = eyeImpl[(L1, L2), Float32](n.size, m.size) + def eye[L1: Label, L2: Label, V](n: AxisExtent[L1], m: AxisExtent[L2], vtype: VType[V]): Tensor2[L1, L2, V] = eyeImpl[(L1, L2), V](n.size, m.size, vtype) + def eye[L1: Label, L2: Label](shape: Shape2[L1, L2]): Tensor2[L1, L2, Float32] = eye(shape.extent(Axis[L1]), shape.extent(Axis[L2])) + def eye[L1: Label, L2: Label, V](shape: Shape2[L1, L2], vtype: VType[V]): Tensor2[L1, L2, V] = eye(shape.extent(Axis[L1]), shape.extent(Axis[L2]), vtype) + def diag[L: Label, V](diag: Tensor1[L, V]): Tensor2[L, Prime[L], V] = Tensor(Jax.jnp.diag(diag.jaxValue)) /** Companion object for Tensors of rank 3. diff --git a/core/src/test/scala/dimwit/tensor/TensorCreationSuite.scala b/core/src/test/scala/dimwit/tensor/TensorCreationSuite.scala index b2023e8..9dccf79 100644 --- a/core/src/test/scala/dimwit/tensor/TensorCreationSuite.scala +++ b/core/src/test/scala/dimwit/tensor/TensorCreationSuite.scala @@ -92,3 +92,45 @@ class TensorCreationSuite extends DimwitTest: idx(Axis[A]).toFloat } result shouldEqual Tensor1(Axis[A]).fromArray(Array(0.0f, 1.0f, 2.0f, 3.0f)) + + describe("eye"): + + it("square: from a single extent"): + val result = Tensor2.eye(Axis[A] -> 3) + result.shape shouldEqual Shape2(Axis[A] -> 3, Axis[Prime[A]] -> 3) + result shouldEqual Tensor2(Axis[A], Axis[B]).fromArray( + Array(Array(1.0f, 0.0f, 0.0f), Array(0.0f, 1.0f, 0.0f), Array(0.0f, 0.0f, 1.0f)) + ) + + it("square: from two extents and from a shape"): + val expected = Tensor2(Axis[A], Axis[B]).fromArray( + Array(Array(1.0f, 0.0f), Array(0.0f, 1.0f)) + ) + Tensor2.eye(Axis[A] -> 2, Axis[B] -> 2) shouldEqual expected + Tensor2.eye(Shape2(Axis[A] -> 2, Axis[B] -> 2)) shouldEqual expected + + it("wide: more columns than rows, zero padded"): + val expected = Tensor2(Axis[A], Axis[B]).fromArray( + Array(Array(1.0f, 0.0f, 0.0f), Array(0.0f, 1.0f, 0.0f)) + ) + val fromExtents = Tensor2.eye(Axis[A] -> 2, Axis[B] -> 3) + fromExtents.shape shouldEqual Shape2(Axis[A] -> 2, Axis[B] -> 3) + fromExtents shouldEqual expected + Tensor2.eye(Shape2(Axis[A] -> 2, Axis[B] -> 3)) shouldEqual expected + + it("tall: more rows than columns, truncating"): + val expected = Tensor2(Axis[A], Axis[B]).fromArray( + Array(Array(1.0f, 0.0f), Array(0.0f, 1.0f), Array(0.0f, 0.0f)) + ) + val fromExtents = Tensor2.eye(Axis[A] -> 3, Axis[B] -> 2) + fromExtents.shape shouldEqual Shape2(Axis[A] -> 3, Axis[B] -> 2) + fromExtents shouldEqual expected + Tensor2.eye(Shape2(Axis[A] -> 3, Axis[B] -> 2)) shouldEqual expected + + it("defaults to Float32 and accepts an explicit vtype in all variants"): + Tensor2.eye(Axis[A] -> 2).dtype shouldBe DType.Float32 + Tensor2.eye(Axis[A] -> 2, Axis[B] -> 3).dtype shouldBe DType.Float32 + Tensor2.eye(Shape2(Axis[A] -> 2, Axis[B] -> 3)).dtype shouldBe DType.Float32 + Tensor2.eye(Axis[A] -> 2, VType[Int32]).dtype shouldBe DType.Int32 + Tensor2.eye(Axis[A] -> 2, Axis[B] -> 3, VType[Int32]).dtype shouldBe DType.Int32 + Tensor2.eye(Shape2(Axis[A] -> 2, Axis[B] -> 3), VType[Int32]).dtype shouldBe DType.Int32