The square root of negative 1 is:
(-1) ** 0.5
(6.123233995736766e-17+1j)
This is a complex number where the real component is effectively 0 and the imaginary component is 1j where the definition of j is j ** 2 == -1.
The value for each component in a complex number is a floating point number. Because these are floating numbers, which recall are shown in decimal but encoded in binary, recursive rounding issues occur. The real component is effectively zero with the small exponent indicating the precision error of a floating point number.
Complex numbers can be constructed using a real number and imaginary number with j suffix. These numbers can be provided as integers or floats, however when an integer is provided it will be cast into a float. i.e. the components of a complex number are always floats.
num1 = (0 + 1j)
num2 = (1 + 2j)
These complex numbers can be visualised as separate components on an x, y axis where x is the real axis and y is the imaginary axis:
These components can be accessed using the real (magenta line) and imag (cyan line) attributes respectively:
num2.real
1
num2.imag
2
In numeric operations such as addition and subtraction, the real and imaginary components are individually treated. Pictorially this can be visualised as the addition of the magenta components for the real x axis alongside addition of the magenta components for the complex y axis:
num1 + num2
(0 + 1j) + (1 + 2j)
(0 + 1) + (1j + 2j)
(1) + (3j)
(1 + 3j)
When multiplied or divided, there is a real term, complex term and complex squared term:
num1 * num1
(0 + 1j) * (0 + 1j)
(0 * 0) + (1j * 1j)
(0) + (1j**2)
Because the definition of a complex number is j**2 == -1, this becomes:
(-1 + 0j)
A complex number has a complex conjugate. Notice that the real component of the complex conjugate shown in cyan is unaltered, however the sign of the imaginary component shown in cyan is changed. This effectively mirrors, the complex number over the y-axis:
num2 = (1 + 2j)
num2con = num2.conjugate()
num2con
(1 - 2j)
A complex number multiplied by its own complex conjugate gives the magnitude of the complex number. The magnitude is a real number with no complex component:
num2 * num2con
(1 + 2j) * (1 - 2j)
(1 * 1) + (1 * -2j) + (2j * 1) + (2j * -2j)
(1) + (-2j) + (2j) + (-4j**2)
Notice that the two complex terms cancel each other out:
(1) + (-4j**2)
And since j**2 == -1:
(1) + (-(-4))
5
Initialisation Signature
? complex
Init signature: complex(real=0, imag=0) Docstring: Create a complex number from a real part and an optional imaginary part. This is equivalent to (real + imag*1j) where imag defaults to 0. Type: type Subclasses:
The initialisation signature has two named input arguments, each which have a default value of 0. num1 can be created using the complex class directly:
num1 = complex(real=0, imag=1)
Since the default value of the real component is 0, the following also works:
num1 = complex(imag=1)
This can also be created using:
num1 = (0 + 1j)
Or without the parenthesis:
num1 = 0 + 1j
Finally since the real component is zero, only the imaginary component can be specified:
num1 = 1j
Data Model Identifiers
The identifiers and data model identifiers for the complex class can be viewed using:
help(complex)
Help on class complex in module builtins: class complex(object) | complex(real=0, imag=0) | | Create a complex number from a real part and an optional imaginary part. | | This is equivalent to (real + imag*1j) where imag defaults to 0. | | Methods defined here: | | __abs__(self, /) | abs(self) | | __add__(self, value, /) | Return self+value. | | __bool__(self, /) | True if self else False | | __complex__(self, /) | Convert this value to exact type complex. | | __eq__(self, value, /) | Return self==value. | | __format__(self, format_spec, /) | Convert to a string according to format_spec. | | __ge__(self, value, /) | Return self>=value. | | __getattribute__(self, name, /) | Return getattr(self, name). | | __getnewargs__(self, /) | | __gt__(self, value, /) | Return self>value. | | __hash__(self, /) | Return hash(self). | | __le__(self, value, /) | Return self<=value. | | __lt__(self, value, /) | Return self<value. | | __mul__(self, value, /) | Return self*value. | | __ne__(self, value, /) | Return self!=value. | | __neg__(self, /) | -self | | __pos__(self, /) | +self | | __pow__(self, value, mod=None, /) | Return pow(self, value, mod). | | __radd__(self, value, /) | Return value+self. | | __repr__(self, /) | Return repr(self). | | __rmul__(self, value, /) | Return value*self. | | __rpow__(self, value, mod=None, /) | Return pow(value, self, mod). | | __rsub__(self, value, /) | Return value-self. | | __rtruediv__(self, value, /) | Return value/self. | | __sub__(self, value, /) | Return self-value. | | __truediv__(self, value, /) | Return self/value. | | conjugate(self, /) | Return the complex conjugate of its argument. (3-4j).conjugate() == 3+4j. | | ---------------------------------------------------------------------- | Static methods defined here: | | __new__(*args, **kwargs) from builtins.type | Create and return a new object. See help(type) for accurate signature. | | ---------------------------------------------------------------------- | Data descriptors defined here: | | imag | the imaginary part of a complex number | | real | the real part of a complex number
Most of these operators behave consistently to their counterparts seen in the float class and int class working on the real and imag components as seen above.
Some of the comparison operators however will not work with complex numbers as there are two components and one component does not take precedence over the other:
(3 + 2j) > (2 + 3j)
TypeError: '>' not supported between instances of 'complex' and 'complex'
The is equal comparison operator on the other hand will work as it checks whether each component is equal and only returns True when both components are equal. The is not equal comparison operator will likewise work:
(3 + 2j) == (2 + 3j)
False
The float and the int classes are setup to work consistently with complex numbers and each have a real and imag attribute. The real attribute (magenta line) is the same as the int or float respectively. The imag attribute (cyan line) on the other hand is 0 for all instances of these classes. All instances of the int and float classes can therefore be conceptualised as numbers that lie on the x-axis:
num3 = 2
num3.real
2
num3.imag
0