# Complex Numbers

#### steve_bank

##### Diabetic retinopathy and poor eyesight. Typos ...
I bet you are wondering what complex numbers are used for. A simple example with a series resistor capacitor low pass filter. Search on RC low pass fiiter for more information,

A little bit of applied math and Python coding.

A pure capacitance has a phase of -90 degrees or -j, and inductance +90 degrees or +j where j is the imaginary number sqrt(-1)
Impedance Z = real resistance +- imaginary reactance, a complex number/

Capacative reactance = 1/(2pi*f*c) = Xc
Capacative impedance Zc = rc - jXc where rc is the internal resistance of a capacitor.
The magnitude of the impedance Zc = sqrt(rc^2 +Xc^2)
The phase = imaginary/real
In an audio amplifier complex phase represents the shift in time of a sine wave as frequency increases.

For 2 series resistors across a battery the voltage out is vin*r2/(r1+r2). Resistors are considered complex with a zero imaginary part.

The output of two series resistors across a battery is vout = vbattery*r2/(r1 + r2). As resistors are real simple arithmetic works.

For an RC circuit vout = vin*Zc/(r + Zc) which involves complex numbers.

Python does complex math. At the end you only need to find the magnitude ad phase for display. purposes.

It is common to express the gain of a circuit in decibels. As a logarithmic transformation chains of gains can be added instead of multiplying in your head.

When plotting gain and phase it is common to plot log-normal if you run the code.

In the file the frequency associated with the -3db magnitude point is called the pole or break frequency.

Code:
#Python
#rc low pass
import os
import math
import cmath
os.chdir('c:\\python')

def save_nums(fname,delim,*args):
nrows = len(args[0])
# check number of rows the same
for i in range(len(args)):
if len(args[i]) != nrows : return 1
f = open(fname,'w')
for i in range(nrows):
s = ' ' # row string
j = 0
for x in args:
s += repr(x[i])
if j < len(args)-1:s += delim
j += 1
s += '\n'
f.write(s)
f.close()
return 0

def rc_lopass(n,r,rc,f,m,p,vin):
#simulated frequency sweep of an RC low pass filter
for i in range(n):
Xc = 1./(2.*math.pi*f[i]*c) # reacrance
Zc = rc -  1j*Xc # complex capcitive impedance
vout = vin*Zc/(r + Zc) # complex result
m[i] = 20*math.log10(abs(vout)/vin)  # gain ratio of output to input in decibels
p[i] = cmath.phase(vout)*360./(2.*math.pi) # phase shift  in degrees

def make_freqs(n,fstart,df,freqs):
# linear array of frequencies in Hertz
f = fstart
for i in range(n):
freqs[i] = f
f += df

fn = 'c:\\python\\lopass.txt'
n = 1000
r = 1000.  # series resitor real
rc = 10.  # capacitor internal resystance
c = 1.e-6  # capctnce
fpole = 1./(2*math.pi*r*c)  # -3db frequncy
print(fpole)
vin = 10.
f = n*[0]  # frequency sweep
m = n*[0] # magnitude frequncy response
p = n*[0]  # phase response
make_freqs(n,1,1,f)
rc_lopass(n,r,rc,f,m,p,vin)
s = save_nums(fn,'       \t',f,m,p)
print(s)

In other words, imaginary numbers are used for understanding time related effects at times that are not now.

In other words, imaginary numbers are used for understanding time related effects at times that are not now.

The C limit applies to circuits,nothing happens in zero time and there us always a tine delay between a change to an input and a change in an output.

Can't explain it metaphysically,mathematically
A*sin(2pi*f*t) an input to the filter.
f = frequency
A is the signal amplitude
G(f) is the gain of the filer at a frequency f
ph(f) is the phase of te output relative to the input signal at at a frequency f
G(f)*A(sin(2pi*f*t + ph(f)) is the output signal of the filter.

Plug in the values from the magnitude and phase data.

As f >> the pole frequency the pashe shift becomes 90 degrees or a cosine relative to the input sine, so the circuit is called an integrator, inst gal sine is -cosine. It used in analog computing as an integration.

For an electrical circuit the phase shift of a sine is a shift in time of the zero crossings of the signal.

To see it try this in Python or any language.

Code:
import math
n = 1000
t = n*[0]
y1 = n*[0]
y2 = n*[0]
f = 1
A = 10
G = .5
ph = -math.pi/4  # - 45 degrees
max_time = 2 #seconds
dt = max_time/(n-1)
for i in range(n):t[i] = i *dt
for i in range(n):
y1[i] = A*math.son(2*math.pi*f*t[i])
y2[i] = G*A*math.sin(2*math.pi*f*t[i] + ph)

Plot y1 and y2 together and compare input to output in amplitude and time. y2 represents for example the output of an amplifier for input y1. Regular audio amplifiers act as a low pass filter.

Python has a complex-number class that is a class of complex floating-point numbers. But it should be easy to write a class of arbitrary complex numbers.

The C++ Standard Template Library also has a complex-number class, but it is a template class, so one can also do complex integers and complex rational numbers if one wants.

Python also has a fractions class in its standard library, for doing rational numbers, and its integers are arbitrary-precision ones: bignums. However, C++ STL has neither fractions nor bignums, though it's easy to find libraries for doing both.

More challenging is some polynomial-extension class, like for Q(sqrt(2)), rational numbers extended with the square root of 2:
x = x0 + x1*sqrt(2) where both x0 and x1 are rational numbers.

One can start with extension with a single root, and stack multiple extensions to do multiple roots that are not related to each other, like sqrt(2) and sqrt(3).

For root r for polynomial with degree n, one expresses values as array (x(0), x(1), ..., x(n-1)) --
x = x(0) + x(1)*r + x(2)*r^2 + ... + x(n-1)*r^(n-1)

Addition and subtraction one does component by component. For multiplication, one must divide out the root's polynomial. For division, one finds x = a/b by solving b*x = a for the components of x. It's a system of linear equations, and one can use Gaussian elimination with pivoting for that. In fact, I've written that for the Python and C++ versions of my semisimple-Lie-algebras code.

Thus, for extension with sqrt(2), with values expressed as polynomials of r = sqrt(2), one divides out the polynomial r^2 - 2.