Visualization of complex functions

f[z_] := z^4;
ParametricPlot[{Re[f[x+I*y]],Im[f[x+I*y]]},
{x,0,1},{y,0,1},AspectRatio->Automatic,
ColorFunctionScaling->False,Mesh->True,
ColorFunction -> (ColorData["DeepSeaColors"][#3]&),
PlotRange->All, Frame->False,Axes->False]

The image of a function

If f:ℂ->ℂ then, as ℂ is geometrically two-dimensional, we cannot “graph” it, in the technical sense of the word - it’s graph would live in four dimensions.  One standard way to visualize a complex function is to simply illustrate its effect on some simple domain in the plane.  This is analogous to thinking of the image of an interval under the square function like so.

dom = Graphics[{
Point[Table[{x,0},{x,0,2,0.1}]]},
Axes -> True,AxesStyle -> Directive[Thin,Opacity[0.5]],
PlotRange -> {{-0.1,4.1},{-0.2,0.2}},
Ticks -> {Range[0,4],{#,""}& /@ Range[-0.2,0.2,0.1]}];
img = Graphics[{
Point[Table[{x^2,0},{x,0,2,0.1}]]},
Axes -> True,AxesStyle -> Directive[Thin,Opacity[0.5]],
PlotRange -> {{-0.1,4.1},{-0.2,0.2}},
Ticks -> {Range[0,4],{#,""}& /@ Range[-0.2,0.2,0.1]}];
Rasterize[GraphicsGrid[{{dom},{img}}],
RasterSize->1200,ImageSize->400]

Here, for example, is the effect of f(z)=z-(3/2)+(i/2) on the unit square.

f[z_] = z-3/2+I/3;
Labeled[Row[{
ParametricPlot[{x,y},{x,0,1},{y,0,1},
Axes -> True,Frame -> True,Mesh->5,ColorFunction ->Function[{x,y,u,v},ColorData["DarkRainbow"][u]],
PlotRange -> {{-2,2},{-0.5,2}}, ImageSize -> 300,
GridLines -> {Range[-1,1],Range[-1,1]},
GridLinesStyle -> Directive[Dashing[{0.02,0.01}],LightGray],
ImagePadding -> 22,
PlotStyle -> Thick],
ParametricPlot[{Re[#],Im[#]}&[f[x+I*y]],{x,0,1},{y,0,1},
Axes -> True,Frame -> True,Mesh->5,ColorFunction ->Function[{x,y,u,v},ColorData["DarkRainbow"][u]],
PlotRange -> {{-2,2},{-0.5,2}}, ImageSize -> 300,
GridLines -> {Range[-1,1],Range[-1,1]},
GridLinesStyle -> Directive[Dashing[{0.02,0.01}],LightGray],
ImagePadding -> 22,
PlotStyle -> Thick]
}], "The image of the unit square under \!\(\*Cell[TextData[Cell[BoxData[FormBox[RowBox[{"z", "->", RowBox[{"z", "-", 
FractionBox["3", "2"], "+", 
FractionBox["I", "2"]}]}], TraditionalForm]],ExpressionUUID->"90a8fe3c-9829-48e4-b61c-2b2a3260ab0c"]],ExpressionUUID->"d53e5144-dbae-4a06-976c-7b1c71f9083a"]\)"]

Note that the square is simply shifted to the left by 3/2 and up 1/2.  Here’s the image of the unit square under (z^2).

f[z_] = z^2;
Labeled[Row[{
ParametricPlot[{x,y},{x,0,1},{y,0,1},
Axes -> True,Frame -> True,Mesh->5,ColorFunction ->Function[{x,y,u,v},ColorData["DarkRainbow"][u]],
PlotRange -> {{-2,2},{-0.5,2}}, ImageSize -> 300,
GridLines -> {Range[-1,1],Range[-1,1]},
GridLinesStyle -> Directive[Dashing[{0.02,0.01}],LightGray],
ImagePadding -> 22,
PlotStyle -> Thick],
ParametricPlot[{Re[#],Im[#]}&[f[x+I*y]],{x,0,1},{y,0,1},
Axes -> True,Frame -> True,Mesh->5,ColorFunction ->Function[{x,y,u,v},ColorData["DarkRainbow"][u]],
PlotRange -> {{-2,2},{-0.5,2}}, ImageSize -> 300,
GridLines -> {Range[-1,1],Range[-1,1]},
GridLinesStyle -> Directive[Dashing[{0.02,0.01}],LightGray],
ImagePadding -> 22,
PlotStyle -> Thick]
}], "The image of the unit square under z->(z^2)"]

In order to get a grip on this, we can look at what happens to the boundary.  Note that

f(u+i v)=(((u+i v))^2)=((u^2)-(v^2))+(2u v)i.

If we set u=0 or 1, we parametrize the image of one of the vertical sides.  If we set v=0 or 1, we parametrize the image of one of the horizontal sides.  For example, if u=1, our parametrization is f(1+i v)=(1-(v^2))+2v i.  This parametrizes a sideways parabola - we get only that part where 0≤v≤1.  We can visualize all four sides as follows:

f[z_] := (z^2);
side1=ParametricPlot[Tooltip[{Re[#],Im[#]}&[f[u+0I]], "side 1"],{u,0,1},
Axes->None,PlotStyle -> Thick,
ColorFunction -> Function[{x,y,u,v},ColorData["DarkRainbow"][u]]];
side2=ParametricPlot[Tooltip[{Re[#],Im[#]}&[f[1+v*I]], "side 2"],{v,0,1},Axes->None,PlotStyle -> Thick,
ColorFunction -> Function[{x,y,u,v},ColorData["DarkRainbow"][1]]];
side3=ParametricPlot[Tooltip[{Re[#],Im[#]}&[f[u+1*I]], "side 3"],{u,0,1},Axes->None,PlotStyle -> Thick,
ColorFunction -> Function[{x,y,u,v},ColorData["DarkRainbow"][u]]];
side4=ParametricPlot[Tooltip[{Re[#],Im[#]}&[f[0+v*I]], "side 4"],{v,0,1},Axes->None,PlotStyle -> Thick,
ColorFunction -> Function[{x,y,u,v},ColorData["DarkRainbow"][0]]];
Labeled[
Show[{side1,side2,side3,side4},PlotRange -> All, 
Epilog -> {PointSize[Large],Point[{{0,0},{1,0},{0,2},{-1,0}}]}],
"The image of the four sides of the unit square under z->(z^2)"]

Basic code

It’s easy to generate these types of images on the computer. Here’s some simple Mathematica code for that purpose.

Cartesian domains

The basic command is going to be

ParametricPlot[{Re[f[x+I*y]],Im[f[x+I*y]]},{x,a,b},{y,c,d}]

where f is the complex function we want to plot and a,b and c,d define the bounds of the rectangle we want to show.  Note that we let Mathematica sort out the real and imaginary parts of f for us using Re an Im.

The identity function

The identity function f(z)=z should just leave the domain alone.  That makes it a natural first example so that we can just see how the code works.

f[z_] := z;
ParametricPlot[{Re[f[x+I*y]],Im[f[x+I*y]]},
{x,0,1},{y,0,1},PlotRange -> {{-0.5,1.5},{-0.5,1.5}}]

Note that I used the PlotRange command so I didn’t get just one big square.  There’s loads of options; I like to add color and a mesh.

f[z_] := z;
ParametricPlot[{Re[f[x+I*y]],Im[f[x+I*y]]},
{x,0,1},{y,0,1},PlotRange -> {{-0.5,1.5},{-0.5,1.5}},
ColorFunction -> (ColorData["DarkRainbow"][#1]&), Mesh->True]

Constant multiplication

Multiplication by a constant should rotate and dilate.

f[z_] := (1+I)z;
ParametricPlot[{Re[f[x+I*y]],Im[f[x+I*y]]},
{x,0,1},{y,0,1}, Mesh->True]

The square function

We’ve seen this one already.

f[z_] := (z^2);
ParametricPlot[{Re[f[x+I*y]],Im[f[x+I*y]]},
{x,0,1},{y,0,1}, Mesh->True,
ColorFunction -> (ColorData["DarkRainbow"][#3]&)]

Polar domains

Now, the basic command is going to be

ParametricPlot[{Re[f[r*(E^(I*θ))]],Im[f[r*(E^(I*θ))]]},{r,a,b},{theta,α,β}]

where f is the complex function we want to plot and a,b and α,β define the bounds of the polar region we want to show. Note that we write z=(e^(i θ)), just as we would mathematically, and we still let Mathematica sort out the real and imaginary parts of f for us.

The identity function

Again, the identity function is a good place to start.

f[z_] := z;
ParametricPlot[{Re[f[r*Exp[I*theta]]],Im[f[r*Exp[I*theta]]]},
{r,0,1},{theta,0,Pi/2}, Mesh->True]

The square function

Recall that (z^2) doubles the argument of z.

f[z_] := (z^2);
ParametricPlot[{Re[f[r*(E^(I*θ))]],Im[f[r*(E^(I*θ))]]},{r,0,1},{θ,0,π/2},
ColorFunction -> (ColorData["DarkRainbow"][#3]&), Mesh->True]

A Möbius transformation

We’ll have fun with these!

f[z_] := I((1+z)/(1-z));
ParametricPlot[{Re[f[r*(E^(I*θ))]],Im[f[r*(E^(I*θ))]]},
{r,0,1},{θ,0,2π}, PlotPoints -> 50,
PlotRange -> {{-5,5},{-1,5}},Mesh->True,
ColorFunction -> (ColorData["DarkRainbow"][#3]&)]

Domain coloring

Another way to visualize a complex function is via domain coloring. We shade each complex number z according to the argument of f(z). Here’s a quick implementation:

domainColoring[expr_,{z_,min_,max_}, opts___] := Module[{cF},
cF = Function[expr /. z->#];
DensityPlot[Arg[cF[x+I*y]], {x,Re[min],Re[max]},{y,Im[min],Im[max]},
MeshFunctions->{Arg[cF[#1+I*#2]]&,Abs[cF[#1+I*#2]]&},
MeshStyle -> {Directive[Opacity[0.4], Thickness[0.001]]},
opts, Mesh -> True, ColorFunction -> Hue, AspectRatio->Automatic, PlotPoints -> 101]
];

The visualization of the identity function should just be a standard color wheel.

domainColoring[z,{z,-1-I,1+I}]

We should see that same picture wrapped around 3 times for (z^3).

domainColoring[(z^3),{z,-1-I,1+I}]

Other functions are more fun.

domainColoring[(z^3)+1,{z,-2-2I,2+2I},
Mesh -> {Range[-12,12,0.5]}]

domainColoring[Sin[z],{z,-Pi(1+I/2),Pi(1+I/2)} ]

Harmonic conjugates

Finally, we can always plot the real and imaginary parts

harmonicPlot[f_,{var_,min_,max_}, 
options___] := Module[
{u,v,realContours, imaginaryContours},

u[x_,y_] := Re[f /. var -> x+I y];
v[x_,y_] := Im[f /. var -> x+I y]; 
realContours = ContourPlot[u[x,y],
{x,Re[min],Re[max]},{y,Im[min],Im[max]}, options,
ContourShading -> False, ContourStyle->Black];
imaginaryContours = ContourPlot[v[x,y],
{x,Re[min],Re[max]},{y,Im[min],Im[max]},options,
ContourShading -> False, ContourStyle->Black];
Show[{realContours,imaginaryContours}, AspectRatio->Automatic] 
];
harmonicPlot[z^2,{z,-1-I,1+I}]

harmonicPlot[Sin[z],{z,-Pi-I*Pi/2,Pi+I*Pi/2}]

I wonder why those contours meet at right angles?

A complex coloring book

Julia Barnes from WCU spoke last semester on ways to visualize complex functions and mentioned her very fun book A Coloring Book of Complex Function Representations. It’s got a number of images that look like this one that are, evidently, great for coloring. As stated, that image is a contour plot of Re((f^3)(z)) where

f[z_] = (z^3)+((-0.2+0.11I)/(z^3));

Here’s how to generate such an image, though this does take a little while:

pic = ContourPlot[Re[f@f@f[x+I*y]],{x,-1.3,1.3},{y,-1.3,1.3},
ContourShading->False, Contours->{-2,0,2}, ContourStyle->Thick,
MaxRecursion->4, ImageSize->1600,PlotRangePadding->None, FrameTicks->None,
FrameStyle->Thickness[0.005]];
Rasterize[pic, RasterSize->1600, ImageSize->800]

Let’s try to get a grip on what’s going on in this picture. 

First off, note that Re(f(x+i y)) is a real valued function of two real variables so we can visualize it using techniques learned in Calculus III. For example, we could generate a contour plot:

ContourPlot[Re[f[x+I*y]],{x,-1.3,1.3},{y,-1.3,1.3},
ContourShading->False, Contours->{-2,0,2}, 
MaxRecursion->3, ImageSize->600]

We can also see how these fit on a 3D surface:

Plot3D[Re[f[x+I*y]],{x,-1.3,1.3},{y,-1.3,1.3},
Mesh->{{-2,0,2}}, MaxRecursion->4, MeshFunctions -> {#3&},
 ClippingStyle->None, PlotRange->{-10,10}, PlotPoints -> 50,
ImageSize->600, MeshStyle->Thick]

Again, these plots display just the real part of the function. It’d be nice to see how that fits into the whole complex picture. We can do so using domain coloring:

domainColoring[expr_,{z_,min_,max_}, opts___] := Module[{cF},
cF = Function[expr /. z->#];
DensityPlot[Arg[cF[x+I*y]], {x,Re[min],Re[max]},{y,Im[min],Im[max]},
MeshFunctions->{Arg[cF[#1+I*#2]]&,Abs[cF[#1+I*#2]]&},
MeshStyle -> {Directive[Opacity[0.4], Thickness[0.001]]},
opts, Mesh -> True, ColorFunction -> Hue, AspectRatio->Automatic, PlotPoints -> 101]
];
Row[{
domainColoring[f[z],{z,-1.3-1.3I,1.3+1.3I}, ImageSize -> 500],
domainColoring[z,{z,-1.3-1.3I,1.3+1.3I}, ImageSize -> 500]
}]

This indicates clearly that there are six points that map to zero. Since Re(0)=0, the curves that extend out from the origin in the image are exactly the contours Re(f(x+i y))=0 and must pass through those points. Zero itself appears to be a pole. We can understand those facts by examining the rational form of f:

Together[f[z]]

So I guess those six pre-images of zero are exactly the sixth roots of -0.2+0.11 i.

Now, what’s with this (f^3) business? Well, (f^n)stands for the (n^th) iterate of f. Thus, (f^2)=f∘f, (f^3)=f∘f∘f, and more generally (f^n)=f∘f∘f∘⋯∘f with n occurrences of f. 

As a next step, let’s think about what (f^2) might look like. In the following code, f@f is Mathematica input for composition.

Rasterize[ContourPlot[Re[f@f[x+I*y]],{x,-1.3,1.3},{y,-1.3,1.3},
ContourShading->False, Contours->{-2,0,2}, 
MaxRecursion->4, ImageSize->600], RasterSize->1200]

The 6 six leafed flowers we see surrounding the big flower are place at the roots of f. If (Subscript[z, r]) denotes any one of those points, then f((Subscript[z, r]))=0, so f(f((Subscript[z, r])))=f(0)=∞. Thus, those points are poles of (f^2) that look like the poles of f which is how they occur. When we take it to (f^3), we find that there are six points that map to each of those six points which is how even more flowers appear.

You know, as long as we’re supposed to color this thing anyway, why wait for the kids to do it?

d = ImageData[pic] /. {{0.`,0.`,0.`,0.0`} -> 0, {0.0,0.0,0.0,_?(#>0&)} :>1};
pic2 = ArrayPlot[d, Frame->False];
m = MorphologicalComponents[pic2,0.5];
Colorize[m, ImageSize->800]