5 Complex Tensors
La Linea’s computation API (la/) is polymorphic over the number field. Functions like el/+, el/*, la/dot, la/mmul, and la/transpose work uniformly on both real tensors and ComplexTensors. Field-aware operations like el/re, el/im, el/conj are identity on reals and meaningful on complex. You construct in the appropriate field with t/, then compute with la/.
ComplexTensor wraps a dtype-next tensor whose last dimension is 2 — interleaved complex number pairs (real/imaginary). The re and im functions always slice the last axis, returning zero-copy tensor views.
| Underlying shape | Complex interpretation | re / im returns |
|---|---|---|
[2] |
scalar complex number | double |
[n 2] |
complex vector, length n | [n] tensor view |
[r c 2] |
complex r * c matrix | [r c] tensor view |
The interleaved layout matches EJML’s ZMatrixRMaj, enabling zero-copy bridging to complex linear algebra.
(ns lalinea-book.complex-tensors
(:require
;; La Linea (https://github.com/scicloj/lalinea):
[scicloj.lalinea.linalg :as la]
[scicloj.lalinea.tensor :as t]
[scicloj.lalinea.elementwise :as el]
;; Dataset manipulation (https://scicloj.github.io/tablecloth/):
[tablecloth.api :as tc]
;; Interactive Plotly charts (https://scicloj.github.io/tableplot/):
[scicloj.tableplot.v1.plotly :as plotly]
;; Visualization annotations (https://scicloj.github.io/kindly-noted/):
[scicloj.kindly.v4.kind :as kind]))Construction
From separate real and imaginary parts
(t/complex-tensor [1.0 2.0 3.0] [4.0 5.0 6.0])#la/C [:float64 [3] [1.000 + 4.000 i 2.000 + 5.000 i 3.000 + 6.000 i]](let [ct (t/complex-tensor [1.0 2.0 3.0] [4.0 5.0 6.0])]
{:re (el/re ct)
:im (el/im ct)}){:re #la/R [:float64 [3] [1.000 2.000 3.000]],
:im #la/R [:float64 [3] [4.000 5.000 6.000]]}Wrapping an existing tensor (zero-copy)
(t/complex-tensor (t/matrix [[1.0 2.0] [3.0 4.0]]))#la/C [:float64 [2] [1.000 + 2.000 i 3.000 + 4.000 i]]Real-only construction
(t/complex-tensor-real [5.0 6.0 7.0])#la/C [:float64 [3] [5.000 + 0.000 i 6.000 + 0.000 i 7.000 + 0.000 i]]Scalar complex numbers
(t/complex 3.0 4.0)#la/C [:float64 [] [3.000 + 4.000 i]][(el/re (t/complex 3.0 4.0)) (el/im (t/complex 3.0 4.0))][3.0 4.0]Matrix construction
(t/complex-tensor [[1.0 2.0] [3.0 4.0]]
[[5.0 6.0] [7.0 8.0]])#la/C [:float64 [2 2]
[[1.000 + 5.000 i 2.000 + 6.000 i]
[3.000 + 7.000 i 4.000 + 8.000 i]]]
Element access
ComplexTensors implement Counted, Indexed, IFn, and Seqable.
Indexing a vector returns a scalar:
(let [ct (t/complex-tensor [1.0 2.0 3.0] [4.0 5.0 6.0])]
[(el/re (ct 0)) (el/im (ct 0))])[1.0 4.0]Indexing a matrix returns a vector (one row):
(let [ct (t/complex-tensor [[1.0 2.0] [3.0 4.0]]
[[5.0 6.0] [7.0 8.0]])]
(el/re (ct 0)))#la/R [:float64 [2] [1.000 2.000]]Nested access reaches scalars:
(let [ct (t/complex-tensor [[1.0 2.0] [3.0 4.0]]
[[5.0 6.0] [7.0 8.0]])]
[(el/re ((ct 1) 1)) (el/im ((ct 1) 1))])[4.0 8.0]Complex arithmetic
Pointwise multiply
\((a+bi)(c+di) = (ac - bd) + (ad + bc)i\)
(let [a (t/complex-tensor [1.0 2.0] [3.0 4.0])
b (t/complex-tensor [5.0 6.0] [7.0 8.0])]
{:re (el/re (el/* a b))
:im (el/im (el/* a b))}){:re #la/R [:float64 [2] [-16.00 -20.00]],
:im #la/R [:float64 [2] [22.00 40.00]]}\((1+3i)(5+7i) = -16 + 22i\), \((2+4i)(6+8i) = -20 + 40i\)
Complex numbers live in the plane. Multiplying by \(i\) rotates 90° counterclockwise:
(let [z-re 3.0 z-im 1.0
;; z * i = (3+i)(0+i) = -1+3i
p-re -1.0 p-im 3.0]
(-> (tc/dataset {:re [z-re 0.0 p-re]
:im [z-im 1.0 p-im]
:label ["z = 3+i" "w = i" "z*w = -1+3i"]})
(plotly/base {:=x :re :=y :im :=color :label})
(plotly/layer-point {:=mark-size 12})
plotly/plot))Conjugate
(let [ct (el/conj (t/complex-tensor [1.0 2.0] [3.0 -4.0]))]
{:re (el/re ct)
:im (el/im ct)}){:re #la/R [:float64 [2] [1.000 2.000]],
:im #la/R [:float64 [2] [-3.000 4.000]]}Conjugation reflects a point across the real axis:
(let [z-re 2.0 z-im 3.0]
(-> (tc/dataset {:re [z-re z-re]
:im [z-im (- z-im)]
:label ["z = 2+3i" "conj(z) = 2-3i"]})
(plotly/base {:=x :re :=y :im :=color :label})
(plotly/layer-point {:=mark-size 12})
plotly/plot))Magnitude
(let [m (el/abs (t/complex-tensor [3.0 0.0] [4.0 1.0]))]
[(double (m 0)) (double (m 1))])[5.0 1.0]\(|3+4i| = 5\), \(|0+i| = 1\)
Hermitian inner product
\(\langle a, b \rangle_H = \sum_i a_i \cdot \overline{b_i}\)
(let [a (t/complex-tensor [3.0 1.0] [4.0 2.0])
d (la/dot a a)]
{:norm-sq (double (el/re d)) :im-part (double (el/im d))}){:norm-sq 30.0, :im-part 0.0}\(|3+4i|^2 + |1+2i|^2 = 25 + 5 = 30\)
Complex matrix operations via EJML
ComplexTensors plug into la/mmul, la/transpose, la/det, la/trace, la/invert — all backed by EJML’s ZMatrixRMaj.
Matrix multiply:
(la/mmul (t/complex-tensor [[1.0 0.0] [0.0 1.0]]
[[0.0 0.0] [0.0 0.0]])
(t/complex-tensor [[0.0 1.0] [1.0 0.0]]
[[0.0 0.0] [0.0 0.0]]))#la/C [:float64 [2 2]
[[0.000 + 0.000 i 1.000 + 0.000 i]
[1.000 + 0.000 i 0.000 + 0.000 i]]]
Conjugate transpose (Hermitian adjoint):
(la/transpose (t/complex-tensor [[1.0 2.0] [3.0 4.0]]
[[5.0 6.0] [7.0 8.0]]))#la/C [:float64 [2 2]
[[1.000 - 5.000 i 3.000 - 7.000 i]
[2.000 - 6.000 i 4.000 - 8.000 i]]]
Re is transposed; Im is negated and transposed:
Determinant:
(la/det (t/complex-tensor [[1.0 3.0] [5.0 7.0]]
[[2.0 4.0] [6.0 8.0]]))#la/C [:float64 [] [-8.882E-16 - 16.00 i]]\(\det(A) = (1+2i)(7+8i) - (3+4i)(5+6i) = -16i\)
Inverse:
(let [A (t/complex-tensor [[1.0 2.0] [3.0 4.0]]
[[0.5 1.0] [1.5 2.5]])
Ainv (la/invert A)
product (la/mmul A Ainv)
re-part (el/re product)
im-part (el/im product)]
(and (< (el/reduce-max (el/abs (el/- re-part (t/eye 2)))) 1e-10)
(< (el/reduce-max (el/abs im-part)) 1e-10)))true