1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
|
\chapter{Complex matrices}
\label{chap:complex}
\section{Introduction}
\label{sec:cmplx-intro}
Native support for complex matrices was added to gretl in version
2019d.\footnote{Prior to that release gretl offered improvised support
for some complex functionality; see section~\ref{sec:cmplx-compat}
for details.} Not all of hansl's matrix functions accept complex
input, but we have enabled a sizable subset of these functions which
should suffice for most econometric purposes.
Complex numbers are not used in most areas of econometrics, but there
are a few notable exceptions: among these, complex numbers allow for
an elegant treatment of univariate spectral analysis of time series,
and become indispensable if you consider multivariate spectral
analysis---see for example \cite{shumwaystoffer2017}. A more recent
example is the numerical solution of linear models with rational
expectations, which are widely used in modern macroeconomics, for
which the complex Schur factorization has become the tool of choice
\citep{klein2000}.
A first point to note is that complex values are treated as a special
case of the hansl \texttt{matrix} type; there's no \texttt{complex}
type as such. Complex scalars fall under the \texttt{matrix} type as
$1 \times 1$ matrices; the hansl \texttt{scalar} type is only for real
values (as is the \texttt{series} type). A $1 \times 1$ complex matrix
should do any work you might require of a complex scalar.
Before we proceed to the details of complex matrices in gretl, here's
a brief reminder of the relevant concepts and notation. Complex
numbers are pairs of the form $a + b\,i$ where $a$ and $b$ are real
numbers and $i$ is defined as the square root of $-1$: $a$ is the real
part and $b$ the imaginary part. One can specify a complex number
either via $a$ and $b$ or in ``polar'' form. The latter pertains to
the complex plane, which has the real component on the horizontal axis
and the imaginary component on the vertical. The polar representation
of a complex number is composed of the length $r$ of the ray from the
origin to the point in question and the angle $\theta$ subtended
between the positive real axis and this ray, measured
counter-clockwise in radians. In polar form the complex number
$z = a + b\,i$ can be written as
\[
z = |z|\,(\cos \theta + i\,\sin \theta) = |z|\,e^{i\theta}
\]
where $|z| = r = \sqrt{a^2 + b^2}$ and $\theta = \tan^{-1}(b/a)$. The
quantity $|z|$ is known as the modulus of $z$, and $\theta$ as its
complex ``argument'' (or sometimes ``phase''). The notation $\bar{z}$
is used for the complex conjugate of $z$: if $z = a + b\,i$, then
$\bar{z} = a - b\,i$.
\section{Creating a complex matrix}
\label{sec:cmplx-create}
The standard constructor for complex matrices is the \cmd{complex()}
function. This takes two arguments, giving the real and imaginary
parts respectively, and sticks them together, as in
\begin{code}
C = complex(A, B)
\end{code}
Four cases are supported, as follows.
\begin{itemize}
\item \texttt{A} and \texttt{B} are both $m \times n$ real matrices
Then \texttt{C} is an $m \times n$ complex matrix such that
$c_{kj} = a_{kj} + b_{kj}\,i$.
\item \texttt{A} and \texttt{B} are both scalars: \texttt{C} is a
$1 \times 1$ complex matrix such that $c = a + b\,i$.
\item \texttt{A} is an $m \times n$ real matrix and \texttt{B} is a
scalar: \texttt{C} is an $m \times n$ matrix such that
$c_{kj} = a_{kj} + b\,i$.
\item \texttt{A} is a scalar and \texttt{B} is an $m \times n$ real
matrix: \texttt{C} is an $m \times n$ matrix such that
$c_{kj} = a + b_{kj}\,i$.
\end{itemize}
In addition, complex matrices may naturally arise as the result of
certain computations.
With both real and complex matrices in circulation, one may wish to
determine whether a particular matrix is complex. The function
\cmd{iscomplex()} can tell you. Passed an identifier, it returns
non-zero if it names a complex matrix, 0 if it names a real matrix, or
\texttt{NA} otherwise. The non-zero return value is either 1 or 2,
with the following interpretation:
\begin{itemize}
\item 1 indicates that the matrix is ``nominally complex'' (each
element is represented as having a real part and an imaginary part)
but all imaginary parts are zero.
\item 2 indicates that at least one element has a non-zero imaginary
part.
\end{itemize}
The following code snippet illustrates the point.
\begin{code}
matrix z1 = complex(1,0)
scalar a = iscomplex(z1)
matrix z2 = complex(1,1)
scalar b = iscomplex(z2)
printf "a = %d, b = %d\n", a, b
\end{code}
The code above gives
\begin{code}
a = 1, b = 2
\end{code}
\section{Indexation}
Indexation of complex matrices works as with real matrices, on the
understanding that each element of a complex matrix is a complex
pair. So for example \texttt{C[i,j]} gets you the complex pair at row
\texttt{i}, column \texttt{j} of \texttt{C}, in the form of a
$1 \times 1$ complex matrix.
If you wish to access just the real or imaginary part of a given
element, or range of elements, you can use the functions \cmd{Re()}
or \cmd{Im()}, as in
\begin{code}
scalar rij = Re(C[i,j])
\end{code}
which gets you the real part of $c_{ij}$.
In addition the dummy selectors \cmd{real} and \cmd{imag} can be
used to assign to just the real or imaginary component of a complex
matrix. Here are two examples:
\begin{code}
# replace the real part of C with random normals
C[real] = mnormal(rows(C), cols(C))
# set the imaginary part of C to all zeros
C[imag] = 0
\end{code}
The replacement must be either a real matrix of the same dimensions as
the target, or a scalar.
Further, the \cmd{real} and \cmd{imag} selectors may be combined
with regular selectors to access specific portions of a complex matrix
for either reading or writing. Examples:
\begin{code}
# retrieve the real part of a submatrix of C
matrix R = C[1:2,1:2][real]
# set the imaginary part of C[3,3] to y
C[3,3][imag] = y
\end{code}
\section{Operators}
\label{sec:cmplx-ops}
Most of the operators available for working with real matrices are
also available for complex ones; this includes the ``dot-operators''
which work element-wise or by ``broadcasting'' vectors. Moreover,
mixed operands are accepted, as in \texttt{D = C + A} where \texttt{C}
is complex and \texttt{A} real; the result, \texttt{D}, will be
complex. In such cases the real operand is treated as a complex matrix
with an all-zero imaginary part.
The operators \textit{not} defined for complex values are:
\begin{itemize}
\item Those that include the inequality tests ``\verb+>+'' or
``\verb+<+'', since complex values as such cannot be compared as
greater or lesser (though they can be compared as equal or not
equal).
\item The (real) modulus operator (percent sign), as in \texttt{x \%
y} which gives the remainder on division of \texttt{x} by
\texttt{y}.
\end{itemize}
As for real matrices, the transposition operator ``\cmd{'}'' is
available in both unary form, as in \texttt{B = A'}, and binary form,
as in \texttt{C = A'B} (transpose-multiply). But note that for complex
\texttt{A} this means the conjugate transpose, $A^\mathrm{H}$. If you
need the non-conjugated transpose you can use \cmd{transp()}.
You may wish to note: although none of gretl's explicit regression
functions (or commands) accept complex input you can calculate
parameter estimates for a least-squares regression of complex $Y$
($T \times 1$) on complex $X$ ($T \times k$) via \verb|B = X \ Y|.
\section{Functions}
\label{sec:cmplx-funcs}
To give an idea of what works, and what doesn't, for complex
matrices, we'll walk through the hansl function-space using the
categories employed in gretl's online ``Function reference'' (under the
\textsf{Help} menu in the GUI program).
\subsection{Linear algebra}
The functions that accept complex arguments are: \cmd{cholesky},
\cmd{det}, \cmd{ldet}, \cmd{eigen}, \cmd{eigensym} (for Hermitian
matrices), \cmd{fft}, \cmd{ffti}, \cmd{inv}, \cmd{ginv}, \cmd{hdprod},
\cmd{mexp}, \cmd{mlog}, \cmd{qrdecomp}, \cmd{rank}, \cmd{svd},
\cmd{tr}, and \cmd{transp}. Note, however, that \cmd{mexp} and
\cmd{mlog} require that the input matrix be diagonalizable, and
\cmd{cholesky} requires a positive definite Hermitian matrix.
In addition there are the complex-only functions \cmd{ctrans}, which
gives the conjugate transpose,\footnote{The \cmd{transp} function
gives the straight (non-conjugated) transpose of a complex matrix.}
and \cmd{schur} for the Schur factorization.
\subsection{Matrix building}
Given what was said in section~\ref{sec:cmplx-create} above, several
of the functions in this category should be thought of as applying to
the real or imaginary part of a complex matrix (for example,
\cmd{ones} and \cmd{mnormal}), and are of course usable in that
way. However, some of these functions can be applied to complex
matrices as such, namely, \cmd{diag}, \cmd{diagcat},
\cmd{lower}, \cmd{upper}, \cmd{vec}, \cmd{vech} and
\cmd{unvech}.
Please note: when \cmd{unvech} is applied to a suitable real
vector it produces a symmetric matrix, but when applied to a complex
vector it produces a Hermitian matrix.
The only functions \textit{not} available for complex matrices are
\cmd{cnameset} and \cmd{rnameset}. That is, you cannot name the
columns or rows of such matrices (although this restriction could
probably be lifted without great difficulty).
\subsection{Matrix shaping}
The functions that accept complex input are: \cmd{cols},
\cmd{rows}, \cmd{mreverse}, \cmd{mshape}, \cmd{selifc},
\cmd{selifr} and \cmd{trimr}.
The functions \cmd{msortby}, \cmd{sort} and \cmd{dsort} are
excluded for the reason mentioned in section~\ref{sec:cmplx-ops}.
\subsection{Statistical}
Supported for complex input: \cmd{meanc}, \cmd{meanr},
\cmd{sumc}, \cmd{sumr}, \cmd{prodc} and \cmd{prodr}. And
that's all.
\subsection{Mathematical}
In the matrix context, these are functions that are applied element by
element. For complex input the following are supported: \cmd{log},
\cmd{exp} and \cmd{sqrt}, plus all of the trigonometric
functions with the exception of \cmd{atan2}.
In addition there are the complex-only functions \cmd{cmod}
(complex modulus, also accessible via \cmd{abs}), \cmd{carg}
(complex argument), \cmd{conj} (complex conjugate), \cmd{Re}
(real part) and \cmd{Im} (imaginary part). Note that
$\mbox{carg}(z) = \mbox{atan2}(y,x)$ for $z=x +
y\,i$. Listing~\ref{ex:complex-modes} illustrates usage of \cmd{cmod}
and \cmd{carg}.
\begin{script}[htbp]
\scriptinfo{complex-modes}{Variant representations of complex numbers. We picked 8
points on the unit circle in the complex plane, so their modulus
is constant and equal to 1. The \texttt{Polar} matrix below shows
that the complex argument is expressed in radians; multiplying by
180/$\pi$ gives degrees. The \texttt{chk} matrix verifies that
we can retrieve the orginal representation of the complex values
from the polar form in either of the two ways mentioned at the
start of the chapter: $z = |z|\,(\cos \theta + i\,\sin \theta)$ or
$z = |z|\,e^{i\theta}$.}
\begin{scode}
# complex values in a + b*i form
scalar rp5 = sqrt(0.5)
matrix A = {1, rp5, 0, -rp5, -1, -rp5, 0, rp5}'
matrix B = {0, rp5, 1, rp5, 0, -rp5, -1, -rp5}'
matrix Z = complex(A, B)
# calculate modulus and argument
matrix zmod = cmod(Z)
matrix theta = carg(Z)
matrix Polar = zmod ~ theta ~ (theta * 180/$pi)
cnameset(Polar, "modulus radians degrees")
printf "%12.4f\n", Polar
# reconstitute the original Z matrix in two ways
matrix Z1 = zmod .* complex(cos(theta), sin(theta))
matrix Z2 = zmod .* exp(complex(0, theta))
matrix chk = Z ~ Z1 ~ Z2
print chk
\end{scode}
Printing of \texttt{Polar} and \texttt{chk}
\begin{outbit}
modulus radians degrees
1.0000 0.0000 0.0000
1.0000 0.7854 45.0000
1.0000 1.5708 90.0000
1.0000 2.3562 135.0000
1.0000 3.1416 180.0000
1.0000 -2.3562 -135.0000
1.0000 -1.5708 -90.0000
1.0000 -0.7854 -45.0000
1.00000 + 0.00000i 1.00000 + 0.00000i 1.00000 + 0.00000i
0.70711 + 0.70711i 0.70711 + 0.70711i 0.70711 + 0.70711i
0.00000 + 1.00000i 0.00000 + 1.00000i 0.00000 + 1.00000i
-0.70711 + 0.70711i -0.70711 + 0.70711i -0.70711 + 0.70711i
-1.00000 + 0.00000i -1.00000 + 0.00000i -1.00000 + 0.00000i
-0.70711 - 0.70711i -0.70711 - 0.70711i -0.70711 - 0.70711i
0.00000 - 1.00000i 0.00000 - 1.00000i 0.00000 - 1.00000i
0.70711 - 0.70711i 0.70711 - 0.70711i 0.70711 - 0.70711i
\end{outbit}
\end{script}
\subsection{Transformations}
In this category only two functions can be applied to complex
matrices, namely \cmd{cum} and \cmd{diff}.
\section{File input/output}
Complex matrices are stored and retrieved correctly in the XML
serialization used for gretl session files (\texttt{*.gretl}).
The functions \cmd{mwrite} and \cmd{mread} work in two modes:
binary mode if the filename ends with ``\texttt{.bin}'' and text mode
otherwise. Both modes handle complex matrices correctly if both the
writing and the reading are to be done by gretl, but for exchange of
data with ``foreign'' programs text mode will \textit{not} work for
complex matrices as a whole. The options are:
\begin{itemize}
\item In text mode, use \cmd{mwrite} and \cmd{mread} on the two
parts of a complex matrix separately, and reassemble the matrix in
the target program.
\item Use binary mode (on the whole matrix), if this is supported for
the given foreign program.
\end{itemize}
At present binary mode transfer of complex matrices is supported for
\textsf{octave}, \textsf{python} and \textsf{julia}.
Listing~\ref{ex:complex-io} shows some examples: we export a complex matrix
to each of these programs in turn; calculate its inverse in the
foreign program; then verify that the result as imported back into
gretl is the same as that calculated in gretl.
\begin{script}[htbp]
\scriptinfo{complex-io}{Exporting and importing complex matrices}
\begin{scode}
set seed 34756
matrix C = complex(mnormal(3,3), mnormal(3,3))
D = inv(C)
mwrite(C, "C.bin", 1)
foreign language=octave
C = gretl_loadmat('C.bin');
gretl_export(inv(C), 'oct_D.bin');
end foreign
oct_D = mread("oct_D.bin", 1)
eval D - oct_D
foreign language=python
import numpy as np
C = gretl_loadmat('C.bin')
gretl_export(np.linalg.inv(C), 'py_D.bin')
end foreign
py_D = mread("py_D.bin", 1)
eval D - py_D
foreign language=julia
C = gretl_loadmat("C.bin")
gretl_export(inv(C), "jl_D.bin")
end foreign
jl_D = mread("jl_D.bin", 1)
eval D - jl_D
\end{scode}
\end{script}
\section{Backward (in)compatibility}
\label{sec:cmplx-compat}
Prior to version 2019d gretl did not provide native support for
complex matrices. It did, however, offer an improvised representation
of such matrices for certain restricted purposes, taking the form of
an expanded regular gretl matrix with real values and imaginary parts
in odd- and even-numbered columns, respectively. The functions
\cmd{fft}, \cmd{eigengen} and \cmd{polroots} returned matrices in this
special form, and the functions \cmd{cmult} and \cmd{cdiv} operated on
such matrices.
As of version 2022b, \cmd{fft} and \cmd{polroots} have been redefined
to work with ``proper'' complex matrices as described above. The other
affected functions are deprecated and will be removed or redefined in
a subsequent release. If you have any hansl code using the legacy
representation the following brief porting guide may be helpful.
\subsection{Porting old complex code}
\texttt{cmult} and \texttt{cdiv}: These functions performed
element-wise multiplication and division of complex column vectors in
the old two-column form. The statements
\begin{code}
# old element-wise operations
c1 = cmult(a1, b1)
d1 = cdiv(a1, b1)
\end{code}
can be updated as
\begin{code}
# new element-wise operations
c2 = a2 .* b2
d2 = a2 ./ b2
\end{code}
(where \texttt{a2} and \texttt{b2} are new-style complex vectors or
matrices). The following statements
\begin{code}
c3 = a2 * b2
d3 = a2 / b2
\end{code}
are also valid but have different effects, the first performing
standard (rather than element-wise) multiplication of matrices
(complex or real) and the second performing ``right division'',
equivalent to \texttt{a2 * inv(b2)}. Note that while the return value
from \texttt{cmult} and \texttt{cdiv} could be either a real vector or
a (two column) complex vector, the new-style operations yield a
nominally complex result if at least one of the operands is complex,
even if the result has an all-zero imaginary part.
A piece of code that appears in some contexts (such as calculation of
a periodogram) is as follows: given a complex vector, \texttt{v},
compute a vector \texttt{w} holding the squared moduli of the elements
of \texttt{v}. The old-style code to accomplish this was
\begin{code}
# legacy: v has two columns
w = sumr(v.^2)
\end{code}
and the new replacement is
\begin{code}
# current: v has a single complex column
w = abs(v).^2
\end{code}
where \texttt{abs} gives the complex modulus.
\texttt{eigengen}: Most uses of this legacy function simply retrieve
the eigenvalues of a general (that is, not symmetric) matrix, and do
not exploit the option of retrieving eigenvectors. In that context it
is straightforward to substitute a call to the new function
\texttt{eigen}. The only point to note is that \texttt{eigen} returns
a new-style complex vector; if you have need to convert this to the
legacy representation you can use the \texttt{cswitch} function, which
is documented in the \GCR. In brief, the following code gives you the
legacy equivalent of a new-style complex vector \texttt{v}:
\texttt{newvec}.
\begin{code}
if v[imag] == 0
oldv = v[real]
else
oldv = cswitch(v, 2)
endif
\end{code}
\texttt{polroots}: This function now returns a new-style complex
vector. As with \texttt{eigengen}, you can use \texttt{cswitch} to
convert the vector if necessary.
%%% Local Variables:
%%% mode: latex
%%% TeX-master: "gretl-guide"
%%% End:
|