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 analagous 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]]]]]\)"]

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 compouter. 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?