Skip to content

Commit

Permalink
Complex enhancements (#630)
Browse files Browse the repository at this point in the history
* Add a single tensor version of the complex() procedure.

Note that you while can also convert a real tensor into a complex tensor by means of the `asType` procedure, this has the advantage that you can use the same function to combine a real and imaginary tensor or a single real tensor into a complex tensor.

Another advantage is that this function will automatically use the right Complex type for the output tensor, leading to more generic code.

That being said, `asType` can still be useful when you want to control the type of the Complex tensor.

* Accept Tensor[int] as inputs of the complex() procedures

This avoids unnecessary intermediate conversions from int to float and then to complex.
  • Loading branch information
AngelEzquerra authored Mar 2, 2024
1 parent d7b1fff commit 282cd70
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 12 deletions.
34 changes: 31 additions & 3 deletions src/arraymancer/tensor/complex.nim
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,40 @@ import
./accessors,
./higher_order_applymap

proc complex*[T: SomeFloat](re: Tensor[T], im: Tensor[T]): Tensor[Complex[T]] {.inline, noinit.} =
proc complex*[T: SomeNumber](re: Tensor[T], im: Tensor[T]): auto {.inline, noinit.} =
## Create a new, complex Tensor by combining two real Tensors
##
## The first input Tensor is copied into the real part of the output Tensor,
## while the second input Tensor is copied into the imaginary part
map2_inline(re, im, complex(x, y))
## while the second input Tensor is copied into the imaginary part.
##
## If the inputs are integer Tensors, the output will be a Tensor of
## Complex64 to avoid any loss of precision.
when T is SomeInteger:
map2_inline(re, im, complex(float64(x), float64(y)))
else:
map2_inline(re, im, complex(x, y))

proc complex*[T: SomeNumber](re: Tensor[T]): auto {.inline, noinit.} =
## Create a new, complex Tensor from a single real Tensor
##
## The input Tensor is copied into the real part of the output Tensor,
## while the imaginary part is set to all zeros.
##
## If the input is an integer Tensor, the output will be a Tensor of
## Complex64 to avoid any loss of precision. If you want to convert it
## into a Tensor of Complex32, you can use `.asType(Complex32)` instead.
##
## Note that you can also convert a real tensor into a complex tensor by
## means of the `asType` procedure. However, this has the advantage that
## you can use the same function to combine a real and imaginary tensor
## or a single real tensor into a complex tensor.
## Another advantage is that this function will automatically use the right
## Complex type for the output tensor, leading to more generic code. Use
## `asType` only when you want to control the type of the Complex tensor.
when T is SomeInteger:
map_inline(re, complex(float64(x)))
else:
map_inline(re, complex(x))

proc real*[T: SomeFloat](t: Tensor[Complex[T]]): Tensor[T] {.inline, noinit.} =
## Get the real part of a complex Tensor (as a float Tensor)
Expand Down
45 changes: 36 additions & 9 deletions tests/tensor/test_complex.nim
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,43 @@ import std/[unittest, math]
proc main() =
suite "Basic Complex Tensor Operations":
test "Complex Tensor Creation":
var re = [1.0, -10, 20].toTensor
var im = [-300.0, 20, -1].toTensor
var c = complex(re, im)
var expected_c = [
complex(1.0, -300.0),
complex(-10.0, 20.0),
complex(20.0, -1.0),
].toTensor
block:
var re = [1.0, -10, 20].toTensor
var im = [-300.0, 20, -1].toTensor
var c = complex(re, im)
var expected_c = [
complex(1.0, -300.0),
complex(-10.0, 20.0),
complex(20.0, -1.0),
].toTensor

check: c == expected_c
check: c == expected_c

block: # Create a complex tensor from 2 real integer tensors
var re = [1, -10, 20].toTensor
var im = [-300, 20, -1].toTensor
var c = complex(re, im)
var expected_c = [
complex(1.0, -300.0),
complex(-10.0, 20.0),
complex(20.0, -1.0),
].toTensor

check: c == expected_c

block: # Single tensor to complex conversion
var re_int = [1, -10, 20].toTensor
var re_float64 = re_int.asType(float64)
var re_float32 = re_int.asType(float32)
var c64_from_int = complex(re_int)
var c64 = complex(re_float64)
var c32 = complex(re_float32)
var expected_c64 = re_float64.asType(Complex64)
var expected_c32 = re_float32.asType(Complex32)

check: c64 == expected_c64
check: c64_from_int == expected_c64
check: c32 == expected_c32

test "Get the Real and Imaginary Components of a Complex Tensor":
var c = [
Expand Down

0 comments on commit 282cd70

Please sign in to comment.