-
Notifications
You must be signed in to change notification settings - Fork 2
Add commonly-used methods on tensors #164
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
fb87b5e
92fe377
0e9e213
24b73aa
9344c83
e15a720
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -126,12 +126,19 @@ object ElementWiseOps: | |
| /** Multiplies each element of a tensor by a scalar tensor, returning a new tensor. */ | ||
| def multiplyScalar[T <: Tuple: Labels, V: IsNumber](t1: Tensor[T, V], s: Tensor0[V]): Tensor[T, V] = Tensor(Jax.jnp.multiply(t1.jaxValue, s.jaxValue)) | ||
|
|
||
| /** Computes the element-wise remainder of `t1 / t2`, matching Python's `%` operator (the result takes the sign of the divisor). */ | ||
| def mod[T <: Tuple: Labels, V: IsNumber](t1: Tensor[T, V], t2: Tensor[T, V]): Tensor[T, V] = Tensor(Jax.jnp.mod(t1.jaxValue, t2.jaxValue)) | ||
|
|
||
| /** Computes the remainder of dividing each element of a tensor by a scalar tensor. */ | ||
| def modScalar[T <: Tuple: Labels, V: IsNumber](t1: Tensor[T, V], s: Tensor0[V]): Tensor[T, V] = Tensor(Jax.jnp.mod(t1.jaxValue, s.jaxValue)) | ||
|
|
||
| // extension methods for the binary operations on two tensors | ||
| extension [T <: Tuple: Labels, V: IsNumber](t: Tensor[T, V]) | ||
|
|
||
| def +(other: Tensor[T, V]): Tensor[T, V] = add(t, other) | ||
| def -(other: Tensor[T, V]): Tensor[T, V] = subtract(t, other) | ||
| def *(other: Tensor[T, V]): Tensor[T, V] = multiply(t, other) | ||
| def %(other: Tensor[T, V]): Tensor[T, V] = mod(t, other) | ||
|
|
||
| // extension methods for the scalar operations. | ||
| extension [T <: Tuple: Labels, V: IsNumber](t: Tensor[T, V]) | ||
|
|
@@ -143,6 +150,7 @@ object ElementWiseOps: | |
|
|
||
| def *(using bc: Broadcast[T, O, V]): Tensor[bc.Out, V] = bc.applyTo(t, other)(multiply) | ||
| def scale(other: Tensor0[V]): Tensor[T, V] = multiplyScalar(t, other) | ||
| def %(using bc: Broadcast[T, O, V]): Tensor[bc.Out, V] = bc.applyTo(t, other)(mod) | ||
|
|
||
| // extension methods | ||
| extension [T <: Tuple: Labels, V: IsNumber](t: Tensor[T, V]) | ||
|
|
@@ -173,6 +181,15 @@ object ElementWiseOps: | |
| def sin: Tensor[T, V] = Tensor(Jax.jnp.sin(t.jaxValue)) | ||
| def cos: Tensor[T, V] = Tensor(Jax.jnp.cos(t.jaxValue)) | ||
| def tanh: Tensor[T, V] = Tensor(Jax.jnp.tanh(t.jaxValue)) | ||
| def arcsin: Tensor[T, V] = Tensor(Jax.jnp.arcsin(t.jaxValue)) | ||
| def arccos: Tensor[T, V] = Tensor(Jax.jnp.arccos(t.jaxValue)) | ||
| def arctan: Tensor[T, V] = Tensor(Jax.jnp.arctan(t.jaxValue)) | ||
| def floor: Tensor[T, V] = Tensor(Jax.jnp.floor(t.jaxValue)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Probably fine as-is due to JAX's default behavior. A cast to Int seems wrong. Does a |
||
| def ceil: Tensor[T, V] = Tensor(Jax.jnp.ceil(t.jaxValue)) | ||
| def round: Tensor[T, V] = Tensor(Jax.jnp.round(t.jaxValue)) | ||
| def isnan: Tensor[T, Bool] = Tensor(Jax.jnp.isnan(t.jaxValue)) | ||
| def isfinite: Tensor[T, Bool] = Tensor(Jax.jnp.isfinite(t.jaxValue)) | ||
| def nanToNum: Tensor[T, V] = Tensor(Jax.jnp.nan_to_num(t.jaxValue)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should provide arguments for https://docs.jax.dev/en/latest/_autosummary/jax.numpy.nan_to_num.html
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe like this: def nanToNum(valueForNan: Double, valueForPosInf: Double, valueForPosNegInf: Double): Tensor[T, V]
def nanToNum(valueForNan: Double): Tensor[T, V] = nanToNum(valueForNan, valueForNan, valueForNan)
``` |
||
|
|
||
| def approxEquals(other: Tensor[T, V], tolerance: Float = 1e-6f): Tensor0[Bool] = approxElementEquals(other, tolerance).all | ||
| def approxElementEquals(other: Tensor[T, V], tolerance: Float = 1e-6f): Tensor[T, Bool] = | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,22 @@ object ReductionOps: | |
| def argsort[L: Label](axis: Axis[L])(using ev: AxisIndex[T, L]): Tensor[T, Int32] = Tensor(Jax.jnp.argsort(t.jaxValue, axis = ev.index)) | ||
| def argsort: Tensor[T, Int32] = Tensor(Jax.jnp.argsort(t.jaxValue)) | ||
|
|
||
| /** sorts the tensor `t` along the specified axis */ | ||
| def sort[L: Label](axis: Axis[L])(using ev: AxisIndex[T, L]): Tensor[T, V] = Tensor(Jax.jnp.sort(t.jaxValue, axis = ev.index)) | ||
| def sort: Tensor[T, V] = Tensor(Jax.jnp.sort(t.jaxValue)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would not support the default to the last axis.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure about it. On one hand it is a confusing default behavior. On the other hand it gracefully handles the case of Tensor1. As far as I know, we cannot have separate extension methods with the same name on Tensor1 and generic Tensor. Argsort, argmin, etc all have this kind of Axis less version.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I did not express my review clearly before (too early in the morning :D) I would NOT support the default case. The user should always be explicit about which axis the sort is applied to: val t: Tensor2[Batch, TimeStep, Int32] = ???
t.sort(Axis[TimeStep])Even if Actually, we should be more extreme and define t.vapply(Axis[TimeStep])(_.sort)
// or
t.vmap(Axis[Batch])(_.sort)
t.sort // compile-error => axis param missingThis would be identical to how linear layers or softmax work now. val t: Tensor2[Batch, Feature, Float32] = ???
t.vmap(Axis[Batch])(linearLayer)
t.vapply(Axis[Feature])(softmax)def softmax[L: Label, V: IsFloating](t: Tensor1[L, V]): Tensor1[L, V] =
liftPyTensor(Jax.jnn.softmax(toPyTensor(t), axis = 0))
Note that some functions are more general than their minimal scope, like
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Additionally, |
||
|
|
||
| /** computes the cumulative sum of the tensor `t` along the specified axis. */ | ||
| def cumsum[L: Label](axis: Axis[L])(using ev: AxisIndex[T, L]): Tensor[T, V] = Tensor(Jax.jnp.cumsum(t.jaxValue, axis = ev.index)) | ||
| def cumsum: Tensor[T, V] = Tensor(Jax.jnp.cumsum(t.jaxValue)) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would return a flattened vector, and the type We could change the return value to Actually, should cumsum just be an operation on Tensor1? Similar to sort. t.vapply(Axis[A])(_.cumsum)This would allow: t.flatten.cumsumFor the default flatten case.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Motivation is similar: cumsum is an operation over a list of values, which is a Vector / Tensor1 in tensorland.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we decide what to do here, do the same for |
||
|
|
||
| /** computes the cumulative product of the tensor `t` along the specified axis. */ | ||
| def cumprod[L: Label](axis: Axis[L])(using ev: AxisIndex[T, L]): Tensor[T, V] = Tensor(Jax.jnp.cumprod(t.jaxValue, axis = ev.index)) | ||
| def cumprod: Tensor[T, V] = Tensor(Jax.jnp.cumprod(t.jaxValue)) | ||
|
|
||
| /** computes the discrete difference of the tensor `t` along the specified axis, reducing that axis' size by one. */ | ||
| def diff[L: Label](axis: Axis[L])(using ev: AxisIndex[T, L]): Tensor[T, V] = Tensor(Jax.jnp.diff(t.jaxValue, axis = ev.index)) | ||
| def diff: Tensor[T, V] = Tensor(Jax.jnp.diff(t.jaxValue)) | ||
|
|
||
| // --------------------------------------------------------- | ||
| // IsFloat operations (IsFloat or IsInt) | ||
| // --------------------------------------------------------- | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
V: IsNumbershould probably beIsFloatinghere? Already a bug for sin, cos, tanh, which currently work for int tenors, which should not be defined. Same for new functions.