Skip to content
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

Support complex64 tensor - real scalar operations (like numpy does) #629

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 123 additions & 10 deletions src/arraymancer/tensor/operators_broadcasted.nim
Original file line number Diff line number Diff line change
Expand Up @@ -105,58 +105,58 @@ proc `/.=`*[T: SomeNumber|Complex[float32]|Complex[float64]](a: var Tensor[T], b
# # Broadcasting Tensor-Scalar and Scalar-Tensor

proc `+.`*[T: SomeNumber|Complex[float32]|Complex[float64]](val: T, t: Tensor[T]): Tensor[T] {.noinit.} =
## Broadcasted addition for tensor + scalar.
## Broadcasted addition for tensor + scalar of the same type.
returnEmptyIfEmpty(t)
result = t.map_inline(x + val)

proc `+.`*[T: SomeNumber|Complex[float32]|Complex[float64]](t: Tensor[T], val: T): Tensor[T] {.noinit.} =
## Broadcasted addition for scalar + tensor.
## Broadcasted addition for scalar + tensor of the same type.
returnEmptyIfEmpty(t)
result = t.map_inline(x + val)

proc `-.`*[T: SomeNumber|Complex[float32]|Complex[float64]](val: T, t: Tensor[T]): Tensor[T] {.noinit.} =
## Broadcasted substraction for tensor - scalar.
## Broadcasted substraction for tensor - scalar of the same type.
returnEmptyIfEmpty(t)
result = t.map_inline(val - x)

proc `-.`*[T: SomeNumber|Complex[float32]|Complex[float64]](t: Tensor[T], val: T): Tensor[T] {.noinit.} =
## Broadcasted substraction for scalar - tensor.
## Broadcasted substraction for scalar - tensor of the same type.
returnEmptyIfEmpty(t)
result = t.map_inline(x - val)

proc `*.`*[T: SomeNumber|Complex[float32]|Complex[float64]](val: T, t: Tensor[T]): Tensor[T] {.noinit.} =
## Broadcasted multiplication for tensor * scalar.
## Broadcasted multiplication for tensor * scalar of the same type.
returnEmptyIfEmpty(t)
result = t.map_inline(x * val)

proc `*.`*[T: SomeNumber|Complex[float32]|Complex[float64]](t: Tensor[T], val: T): Tensor[T] {.noinit.} =
## Broadcasted multiplication for scalar * tensor.
## Broadcasted multiplication for scalar * tensor of the same type.
returnEmptyIfEmpty(t)
result = t.map_inline(x * val)

proc `/.`*[T: SomeNumber|Complex[float32]|Complex[float64]](val: T, t: Tensor[T]): Tensor[T] {.noinit.} =
## Broadcasted division
## Broadcasted division for scalar / tensor of the same type.
returnEmptyIfEmpty(t)
when T is SomeInteger:
result = t.map_inline(val div x)
else:
result = t.map_inline(val / x)

proc `/.`*[T: SomeNumber|Complex[float32]|Complex[float64]](t: Tensor[T], val: T): Tensor[T] {.noinit.} =
## Broadcasted division
## Broadcasted division for tensor / scalar of the same type.
returnEmptyIfEmpty(t)
when T is SomeInteger:
result = t.map_inline(x div val)
else:
result = t.map_inline(x / val)

proc `^.`*[T: SomeFloat|Complex[float32]|Complex[float64]](t: Tensor[T], exponent: T): Tensor[T] {.noinit.} =
## Compute element-wise exponentiation: tensor ^ scalar.
## Compute element-wise exponentiation: tensor ^ scalar of the same type.
returnEmptyIfEmpty(t)
result = t.map_inline pow(x, exponent)

proc `^.`*[T: SomeFloat|Complex[float32]|Complex[float64]](base: T, t: Tensor[T]): Tensor[T] {.noinit.} =
## Broadcasted exponentiation: scalar ^ tensor.
## Broadcasted exponentiation: scalar ^ tensor of the same type.
returnEmptyIfEmpty(t)
result = t.map_inline pow(base, x)

Expand Down Expand Up @@ -194,6 +194,119 @@ proc `/.=`*[T: SomeNumber|Complex[float32]|Complex[float64]](t: var Tensor[T], v
t.apply_inline(x / val)


# #################################################
# # Mixed Complex64 tensor - real scalar operations
#
# It is always possible to operate on a Complex64 tensor with a real scalar
# without loss of precission, so we allow it without explicit type conversion.
# This makes complex tensor arithmetic more convenient to use.

proc `+.`*[T: SomeNumber](val: T, t: Tensor[Complex64]): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted addition for real scalar + Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
complex_val +. t

proc `+.`*[T: SomeNumber](t: Tensor[Complex64], val: T): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted addition for real scalar + Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t +. complex_val

proc `-.`*[T: SomeNumber](val: T, t: Tensor[Complex64]): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted subtraction for real scalar - Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
complex_val -. t

proc `-.`*[T: SomeNumber](t: Tensor[Complex64], val: T): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted subtraction for real scalar - Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t -. complex_val

proc `*.`*[T: SomeNumber](val: T, t: Tensor[Complex64]): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted multiplication for real scalar * Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
complex_val *. t

proc `*.`*[T: SomeNumber](t: Tensor[Complex64], val: T): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted multiplication for real scalar * Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t *. complex_val

proc `/.`*[T: SomeNumber](val: T, t: Tensor[Complex64]): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted division for real scalar / Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
complex_val /. t

proc `/.`*[T: SomeNumber](t: Tensor[Complex64], val: T): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted division for real scalar / Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t /. complex_val

proc `^.`*[T: SomeNumber](val: T, t: Tensor[Complex64]): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted exponentiation for real scalar ^ Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
complex_val ^. t

proc `^.`*[T: SomeNumber](t: Tensor[Complex64], val: T): Tensor[Complex64] {.noinit, inline.} =
## Broadcasted exponentiation for real scalar ^ Complex64 tensor
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t ^. complex_val

proc `+.=`*[T: SomeNumber](t: var Tensor[Complex64], val: T) {.inline.} =
## Complex64 tensor in-place addition with a real scalar.
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t +.= complex_val

proc `-.=`*[T: SomeNumber](t: var Tensor[Complex64], val: T) {.inline.} =
## Complex64 tensor in-place subtraction of a real scalar.
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t -.= complex_val

proc `*.=`*[T: SomeNumber](t: var Tensor[Complex64], val: T) {.inline.} =
## Complex64 tensor in-place multiplication with a real scalar.
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t *.= complex_val

proc `/.=`*[T: SomeNumber](t: var Tensor[Complex64], val: T) {.inline.} =
## Complex64 tensor in-place division by a real scalar.
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t /.= complex_val

proc `^.=`*[T: SomeNumber](t: var Tensor[Complex64], val: T) {.inline.} =
## Complex64 tensor in-place exponentiation by a real scalar.
##
## The scalar is automatically converted to Complex64 before the operation.
let complex_val = complex(float64(val))
t ^.= complex_val


# ##############################################
# Deprecated syntax

Expand Down
23 changes: 23 additions & 0 deletions tests/tensor/test_broadcasting.nim
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,29 @@ proc main() =
[1.0/20.0],
[1.0/30.0]].toTensor.asType(Complex[float64])

test "Implicit tensor-scalar broadcasting - Tensor[Complex64] - scalar operations":
block:
let a_c = [10, 20, 30, 40].toTensor().reshape(4,1).asType(Complex[float64])

check: complex(2.0) +. a_c +. complex(3.0) == 2 +. a_c +. 3
check: complex(2.0) -. a_c -. complex(3.0) == 2 -. a_c -. 3
check: complex(2.0) *. a_c *. complex(3.0) == 2 *. a_c *. 3
check: complex(2.0) /. a_c /. complex(3.0) == 2 /. a_c /. 3
check: complex(0.5) ^. a_c ^. complex(2.0) == 0.5 ^. a_c ^. 2

block:
var a_c = complex([10.0, 20.0, 30.0, 40.0].toTensor,
[1.0, 2.0, 3.0, 4.0].toTensor)

a_c +.= 2
a_c -.= 3.0
a_c *.= 4
a_c /.= 2.0
a_c ^.= 2
let expected = complex([320.0, 1428.0, 3328.0, 6020.0].toTensor,
[72.0, 304.0, 696.0, 1248.0].toTensor)
check: a_c.mean_absolute_error(expected) < 1e-12

test "Implicit broadcasting - Sigmoid 1 ./ (1 +. exp(-x)":
block:
proc sigmoid[T: SomeFloat](t: Tensor[T]): Tensor[T]=
Expand Down
Loading