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
source: notebooks/lalinea_book/complex_tensors.clj