In MATLAB, and in programming in general, one of the most important tools is a class . Classes are special objects that combine structures (called properties for classes) and functions specifically designed for the class (called methods ). Understanding and using classes is important because it allows us to create objects for specific and specialized purposes beyond just having doubles, matrices, strings, etc.
Below is
ourComplex.m
, a very simple class based on complex numbers. We know that a complex number is written as $ x + yi $ where $ i = \sqrt{-1} $, and $ x $ and $ y $ represent the real and imaginary parts of the number, respectively.
To represent this sort of object in
MATLAB
, we want to treat the real and imaginary part as independent double precision values, keeping them separated.
classdef ourComplex
%OURCOMPLEX Our implementation of complex numbers
% We're going to be making a class that has most of the same features as
% the complex numbers class in MATLAB. Let's start with basic properties
% and implementing a constructor, display, and plus method.
properties
value
end
methods
function obj = ourComplex(num1,num2)
%OURCOMPLEX Construct an instance of the ourComplex class
% obj = OURCOMPLEX(num1,num2) takes real values a and b and constructs
% a complex number num1 + num2*i, where i is the imaginary unit sqrt(-1)
obj.value(1) = num1;
obj.value(2) = num2;
end
end
end
Creating the class object starts with a special method called a
constructor
. The constructor is a function that shares the same name as the class whose job it is to create an
instance
(distinct object) of the class. Constructors always appear first in the list of methods. The constructor for
ourComplex
is quite simple and only takes two input values:
a = ourComplex(1,-2)
What happens inside the class definition file is that an instance of the class (
obj
), which behaves like a structure named
obj
with the field
value
, is created. Just like with structures, we say
obj.value(1) = a;
to assign the number
num1
to the first entry of field value within the class object
obj
. This is done here by taking a 2-vector named value and using the first entry
value(1)
as $x$ and the second entry
value(2)
as $y$. We will add other methods to the class later to make sure it behaves like a typical complex number.
a = ourComplex(1,-2)
The constructor is called in the command line (or anywhere in
MATLAB
) like above. We treat it like a function, giving it inputs $x = 1$ and $y = -2$ for the real and imaginary parts, and assign its output to the variable
a
. This
a
is now an instance of the
ourComplex
class and will have any behaviors we have assigned to it. One of the things that we can do is make it display its values in a "prettier" way, so that it looks like $x + yi$. We're going to add a method called
disp
to accomplish this.
Any output to the
MATLAB
console is generated by a function called
disp
. Each of the normal types of objects that we have used in
MATLAB
thus far such as floats, matrices, strings, cell arrays, and structures have a
disp
function that controls how the are displayed in the console. By default, user defined classes look very similar to a structure, but we may write a custom
disp
method that changes this behavior.
function output = disp(obj)
%DISP displays ourComplex in "pretty" form
fprintf('%7.4f ', obj.value(1))
if obj.value(2) < 0
fprintf('-')
else
fprintf('+')
end
fprintf('%7.4fi \n', abs(obj.value(2)) )
end
This function is added below the constructor in the above code block. It has the following effect:
b = ourComplex(5,-pi)
We made a different
ourComplex
number and it displays according to the method now in the class definition file. This is an example of something called
operator overloading
, which we will discuss in more detail later. What matters right now is that we have a function that works only exactly on objects of the
ourComplex
class. Any other type of object will not behave properly. To demonstrate this more explicitly, let's add a
plus
method to our class.
We insert the following method below the constructor and display methods in our class definition file.
function output = plus(a,b)
%PLUS Addition method for ourComplex
% For this function, we want to make sure that our output is *also*
% a member of the class, so we use the constructor
output = ourComplex(a.value(1) + b.value(1), a.value(2) + b.value(2));
end
Now, we can add two
ourComplex
numbers.
a = ourComplex(1,-2);
b = ourComplex(5,-pi);
plus(a,b)
a + b
It works!
In
MATLAB
,a+b
is just a shorthand for calling the functionplus(a,b)
, so whenever you typea+b
, theplus
function is really what's being called.
The
plus
method is really important here, because is demonstrates how you need to be thinking about coding within a class.
First off, notice that it takes as input
a
and
b
, which are both
ourComplex
members. Inside the function, we type
a.value(1)
to refer to the first element of the property
value
of structure
a
. We imagine that $a = x_a + y_a i$ and that $b = x_b + y_b i$, and according to the summation rule for complex numbers, $a+b = (x_a + x_b) + (y_a + y_b)i$. Using this information, we wrote the algorithm for our
plus
method.
Secondly, we need to make sure that our answer is itself an
ourComplex
number. To make this happen, we call the constructor function of the class in the
plus
method to make a new
ourComplex
number and return that as the output. To verify that something is a member of a particular class,
MATLAB
has a built in function called
isa
:
isa(a+b,'ourComplex')
The
isa
function is a simple text comparison, and makes sure that the first argument
a+b
is a member of the class named
ourComplex
, which it is, meaning our
plus
method returned a member of the class.
Next, we add a method for
uminus
, which is short for "unary minus." It takes an
ourComplex
number $a = x + yi$ and returns its additive inverse $-a = -x - yi$. In
MATLAB
, this can be invoked by using the shorthand
-a
.
function output = uminus(a)
%UMINUS Unary minus method for ourComplex
output = ourComplex(-a.value(1), -a.value(2));
end
a
uminus(a)
-a
Now is where things start to get interesting. We have developed a couple of methods
plus
and
uminus
. Using these, we are able to form more complicated methods based on ones that we have previously written. For example, subtraction of two complex numbers $ a - b $ (a minus b) can be thought of as $ a + (-b) $ (a plus the additive inverse of b). Since
plus
and
uminus
already exist, we use these to define the
minus
operation for
ourComplex
.
function output = minus(a,b)
%Minus Subtraction method for ourComplex
% Combines plus and uminus methods to define subtraction
output = a + (-b);
end
The following inputs all, by design, produce the same results:
minus(a,b)
a + (-b)
a - b
We're starting to get somewhere, but something extremely important has been missing up until now: multiplication.
Multiplication for this example takes some special care because we actually have two different kinds of multiplication to deal with when using complex numbers. We have to worry about
both
multiplying an
ourComplex
number by a
double
, say by typing
2*a
,
and
multiplying two
ourComplex
numbers together
a*b
. This distinction is required since our method
mtimes
(the function that powers
a*b
, short for matrix times) will need to have different behavior if it is given two
ourComplex
numbers or a mix of a
double
and an
ourComplex
.
MATLAB
distinguishes betweenmtimes(a,b) = a*b
andtimes(a,b) = a.*b
, which we know from previous discussions are matrix multiplication and element-wise multiplication. If we were going to support matrices for our class, we would need to distinguish between the two of them for our work as well. However, we are going to for simplicity not support matrices so these two operations will do the same thing.
Setting up the method for multiplication requires us to check the inputs to see if one is a
double
or if both are
ourComplex
.
If we have a
double
$k$ and an
ourComplex
$a = x+yi$, then
k*a
is equal to $$ka = (ka) + (ky)i.$$
If there are two
ourComplex
values
a*b
, then $$(x_a + y_a i)(x_b + y_b i) = (x_a y_a - x_b y_b) + (x_a y_b + x_b y_a)i $$
We'll use the
isa
command we talked about earlier to help check the inputs.
function output = mtimes(a,b)
%MTIMES is the multiplication method for ourComplex
% output = MTIMES(a,b) multiplies
if isa(a,'ourComplex') %check if a ourComplex, then do what is necessary based on what b is
if isa(b,'ourComplex')
output = ourComplex(a.value(1)*b.value(1) - a.value(2)*b.value(2),...
a.value(1)*b.value(2) + a.value(2)*b.value(1));
else % b is a double
output = ourComplex(a.value(1)*b, a.value(2)*b);
end
else % a is a double, and b is ourComplex
output = ourComplex(a*b.value(1), a*b.value(2));
end
end
Let's incorporate this into our function and see if it works.
classdef ourComplex
%OURCOMPLEX Our implementation of complex numbers
% We're going to be making a class that has most of the same features as
% the complex numbers class in MATLAB. Let's start with basic properties
% and implementing a constructor, display, and plus method.
properties
value
end
methods
function obj = ourComplex(num1,num2)
%OURCOMPLEX Construct an instance of the ourComplex class
% obj = OURCOMPLEX(num1,num2) takes real values a and b and constructs
% a complex number num1 + num2*i, where i is the imaginary unit sqrt(-1)
obj.value(1) = num1;
obj.value(2) = num2;
end
function output = disp(obj)
%DISP displays ourComplex in "pretty" form
fprintf('%7.4f ', obj.value(1))
if obj.value(2) < 0
fprintf('-')
else
fprintf('+')
end
fprintf('%7.4fi \n', abs(obj.value(2)) )
end
function output = plus(a,b)
%PLUS Addition method for ourComplex
% For this function, we want to make sure that our output is *also*
% a member of the class, so we use the constructor
output = ourComplex(a.value(1) + b.value(1), a.value(2) + b.value(2));
end
function output = uminus(a)
%UMINUS Unary minus method for ourComplex
output = ourComplex(-a.value(1), -a.value(2));
end
function output = minus(a,b)
%Minus Subtraction method for ourComplex
% Combines plus and uminus methods to define subtraction
output = a + (-b);
end
function output = mtimes(a,b)
%MTIMES is the multiplication method for ourComplex
% output = MTIMES(a,b) multiplies
if isa(a,'ourComplex') %check if a ourComplex, then do what is necessary based on what b is
if isa(b,'ourComplex')
output = ourComplex(a.value(1)*b.value(1) - a.value(2)*b.value(2),...
a.value(1)*b.value(2) + a.value(2)*b.value(1));
else % b is a double
output = ourComplex(a.value(1)*b, a.value(2)*b);
end
else % a is a double, and b is ourComplex
output = ourComplex(a*b.value(1), a*b.value(2));
end
end
end
end
3*(1+2i)
3*ourComplex(1,2)
(1 + 2i)*(5 - pi*1i)
ourComplex(1,2)*ourComplex(5,-pi)
Victory!
It looks like this doing what we want. It'll take more testing to make
absolutely sure
, but we're in a good spot. The other thing we'll add is a simple method that makes
times
behave the same as
mtimes
, but I'll add that to the next version to save some space.
Here's what I want you to do to test your knowledge:
I am going to add a method that computes the real part of an
ourComplex
number called
real
(just like the
MATLAB
function). If $a = x + yi$, then $$ \text{Re}(a) = x.$$ It just returns the
double
that is
a
's real part. This
is not
an
ourComplex
number, so we won't use the constructor.
Your job is to add two methods:
imag
, which returns the imaginary part (just like the built in
MATLAB
function)
conj
, which returns the complex conjugate of an
ourComplex
number
If $a = x+yi$, then $$ \overline{a} = x - yi.$$
The
conj
method
will
be making an
ourComplex
object, so use the constructor in that method.
Here's
ourComplex.m
with the
real
method added, copy this and submit it with your
imag
and
conj
methods added to it.
classdef ourComplex
%OURCOMPLEX Our implementation of complex numbers
% We're going to be making a class that has most of the same features as
% the complex numbers class in MATLAB. Let's start with basic properties
% and implementing a constructor, display, and plus method.
properties
value
end
methods
function obj = ourComplex(num1,num2)
%OURCOMPLEX Construct an instance of the ourComplex class
% obj = OURCOMPLEX(num1,num2) takes real values a and b and constructs
% a complex number num1 + num2*i, where i is the imaginary unit sqrt(-1)
obj.value(1) = num1;
obj.value(2) = num2;
end
function output = disp(obj)
%DISP displays ourComplex in "pretty" form
fprintf('%7.4f ', obj.value(1))
if obj.value(2) < 0
fprintf('-')
else
fprintf('+')
end
fprintf('%7.4fi \n', abs(obj.value(2)) )
end
function output = plus(a,b)
%PLUS Addition method for ourComplex
% For this function, we want to make sure that our output is *also*
% a member of the class, so we use the constructor
output = ourComplex(a.value(1) + b.value(1), a.value(2) + b.value(2));
end
function output = uminus(a)
%UMINUS Unary minus method for ourComplex
output = ourComplex(-a.value(1), -a.value(2));
end
function output = minus(a,b)
%Minus Subtraction method for ourComplex
% Combines plus and uminus methods to define subtraction
output = a + (-b);
end
function output = mtimes(a,b)
%MTIMES Multiplication method for ourComplex
% output = MTIMES(a,b) multiplies
if isa(a,'ourComplex') %check if a ourComplex, then do what is necessary based on what b is
if isa(b,'ourComplex')
output = ourComplex(a.value(1)*b.value(1) - a.value(2)*b.value(2),...
a.value(1)*b.value(2) + a.value(2)*b.value(1));
else % b is a double
output = ourComplex(a.value(1)*b, a.value(2)*b);
end
else % a is a double, and b is ourComplex
output = ourComplex(a*b.value(1), a*b.value(2));
end
end
function output = times(a,b)
%TIMES Multiplication method for ourComplex
% Just copies mtimes
output = mtimes(a,b);
end
function number = real(a)
%REAL Returns real part of an ourComplex number
% I used number here to drive home that real DOES NOT return an ourComplex
number = a.value(1);
end
end
end
Testing the
real
method:
a = ourComplex(1,-2)
real(a)
Now it is your turn. After your methods are added, you can invoke them by typing things like
a = ourComplex(1,-2);
real(a)
a.real % we'll discuss this later
real(ourComplex(5,-pi))
imag(a)
a.imag
imag(ourComplex(3,-4.7))
conj(a)
a.conj
conj(ourComplex(-1.8, exp(1)))
Check your results against the
MATLAB
complex number behavior.
real(1-2i)
real(ourComplex(1,-2))
imag(1-2i)
imag(ourComplex(1,-2))
conj(1-2i)
conj(ourComplex(1,-2))