Skip to content

Complex Numbers in STACK

Complex numbers, especially the display of complex numbers, is a surprisingly subtle issue. This is because there is some genuine ambiguity in whether is a single object or the sum of two parts. In mathematics we use this ambiguity to our advantage, but in online assessment we need to be more precise. There are also issues of unary minus, e.g. not displaying . Similarly we typically do not display numbers like , unless of course we want to at which point we need the option to do so!

The general rules when displaying a complex number in Cartesian form "" are

  1. the real part should always appear to the left of the imaginary part;
  2. (or whatever symbol is being used for the imaginary unit) should appear on the right of its coefficient if and only if the coefficient is a numerical value. By numerical value, we mean something like but not things like , even if is a constant.

Some examples:

  • 3+2*%i should display as .
  • 3-%i should display as .
  • -a+b*%i should display as .
  • -b*%i should display as (not normally as ).

STACK provides two functions, one which simplifies and one which does not.

  1. display_complex(ex) takes an expression ex and tries to display this as a complex number obeying the above rules. In particular, this function makes use of Maxima's realpart and imagpart function to split up ex into real and imaginary parts. To do this it must assume simp:true, and so the real and imaginary part will be simplified. For example, display_complex(1+2*%i/sqrt(2)) is displayed as . If you really want then you will need to use the non-simplifying alternative below. This function respects normal conventions, e.g. when realpart returns zero this function will not print , it just prints , etc.
  2. disp_complex(a, b) assumes a is the real part and b is the imaginary part (no checking is done). This function (mostly) does not simplify its arguments. So disp_complex(0, 2) will appear as ; disp_complex(2/4, 1) will appear as ; and disp_complex(2, 2/sqrt(2)) will appear as . Use the atom null if you do not want to print a zero for the real part, or print one times the imaginary part. disp_complex(null, 2) will appear as and disp_complex(null, null) will appear as just . Think of null as a non-printable unit (additive or multiplicative).

There is one exception. In order to pull out a unary minus to the front, disp_complex(a, b) will simplify b if b is not a number and it contains a unary minus. So, for example disp_complex(a, (-b^2)/b) is displayed . (We might be able to fix this but this edge case requires disproportionate effort: ask the developers if this is essential).

You cannot use these functions to display complex numbers in this form , both these function will always display as .

Display respects the multiplication sign used elsewhere within expressions, so that you may have rather than .

Note that the function display_complex(ex) returns the inert form disp_complex(a, b). The expression disp_complex(a, b) is an "inert form", which is only used to fine-tune the display. This function is not actually defined and so Maxima always returns it unevaluated. To remove the inert form from an expression, which is needed to manipulate this further, use remove_disp_complex, e.g., with the following.

p1:disp_complex(a, b);
p2:ev(p1, disp_complex=remove_disp_complex);

(Because null has two different meanings within an expression it isn't sufficient to just define disp_complex(ex1, ex2) := ex1+ex2*%i.)

There are occasions when you will need to explicitly add brackets to the displayed form of a complex number, e.g. to emphasise it is a single entity. To add brackets there is a further "inert form" disp_parens which does nothing but add parentheses when the expression is displayed with the tex() function. For example,

p1:disp_parens(display_complex(1+%i))*x^2+disp_parens(display_complex(1-%i));

will display as . To remove these inert forms evaluate

p2:ev(p1, disp_complex=remove_disp_complex, disp_parens=lambda([ex],ex));

You must remove inert forms before expressions are evaluated by the potential response tree, for example in the feedback variables. For example, disp_complex(a, b) is not algebraically equivalent to a+b*%i.

Polar and Exponential form

A complex number written as is in exponential form or polar form. The Maxima function polarform re-writes a complex number in this form, however with simp:false it does not simplify the expressions for the modulus or argument (in STACK). Attempting to re-simplify the expression only returns the number to Cartesian form!

As a minimal example, try the following.

simp:false;
p1:polarform(1+%i);
p2:ev(polarform(1+%i), simp);
p3:ev(p2, simp);

First we have p1 is . Of course, we really need some simplification of the and the values.

Notice the difference between p2: , and p3: (which of course is not even either!).

The problem is that in this case ev( ... , simp) is not idempotent, (i.e. in all cases) and the PHP-maxima connection inevitably passes an expression to and from Maxima multiple times. If simp:true then we get multiple simplifications, in this example back to p3.

Instead, use polarform_simp to rewrite the expression in polar form, and do some basic simplification of and .

simp:false;
p1:polarform_simp(1+%i);

returns p1 as .

Here are some design choices.

  1. Positive numbers are returned as real numbers, not as . E.g. polarform_simp(3) is .
  2. If then this is not displayed. E.g. polarform_simp(1/sqrt(2)*(-1+%i)) is .

If question level simplification is on, then the value will probably get re-simplified to Cartesian form.

The predicate complex_exponentialp(ex) determines if is written in complex exponential form, . Note this test is strict

  1. we must have ;
  2. we must have .
  3. we expect negative real numbers to be written as .

This predicate needs simp:false. In particular do not test using the ATAlgEquiv test, which always simplifies its arguments. Instead test with ATCasEqual(complex_exponentialp(ans1),true) to avoid automatic simplification of ans1 back to Cartesian form before applying the predicate!

An example question is given in the stack library under Topics\Complex_cube_roots.xml.