Problem set #1 PHY256 (2021)

Getting used to python. Please turn in copies of your code and your code outputs to blackboard.

Problem 1. arctan2

Write a function (a subroutine) that takes as arguments two floats x,y corresponding to coordinates on the plane and returns an angle. The angle should be between 0 and $2 \pi$ so that the angle is modulo $2\pi$. The angle should satisfy $x = r \cos \theta$ and $y =r \sin \theta $ where $r = \sqrt{x^2 + y^2}$. The function should give an angle that is correct in all four quadrants of the Cartesian plane.

Check to find out what your routine returns if it is passed negative numbers or integers.

Check to find out what your routine returns if it is passed $x=0$, $y=0$.

Note there is a difference between % and fmod, see https://docs.python.org/2/library/math.html

It may be useful to look up the numpy function arctan2 https://numpy.org/doc/stable/reference/generated/numpy.arctan2.html

In [1]:
#tests and examples
import numpy as np  # load numpy
twopi = 2.0*np.pi  # 2*pi!
x = 8.0%twopi  # a modulo function
print('mod using % ', x)  # print x
y = np.fmod(8.0,twopi) # another modulo function
print('mod using fmod ', y)  # print y
mod using %  1.7168146928204138
mod using fmod  1.7168146928204138
In [2]:
# another example of mod functions
x = -8.0%twopi
print('mod using % ', x)

y = np.fmod(-7.,twopi)  #fmod is not the same as %
print('mod using fmod ' , y)
mod using %  4.5663706143591725
mod using fmod  -0.7168146928204138
In [3]:
# examples of arctan2 in the 4 quadrants
print(np.arctan2(0,-1), np.arctan2(0,1))
print(np.arctan2(1,0),np.arctan2(-1,0) )
3.141592653589793 0.0
1.5707963267948966 -1.5707963267948966
In [4]:
# what happens at the origin?
print(np.arctan2(0,0))
0.0
In [5]:
#What happens at infinity?
print(np.arctan2(np.Inf,0))
1.5707963267948966
In [7]:
# an example subroutine, a function that adds 1 to a number or array
# and returns that number (or array)
def add_one(x):  #note colon
    return x + 1.0;  # return a float! note indent
    
# test the subroutine to see what it does
print ("test: add_one", add_one(3.1))

z = np.array([3,5])
print(add_one(z))
test: add_one 4.1
[4. 6.]

Problem 2. allocating arrays

What is the difference between the following routines?

  • range
  • arange
  • zeros
  • linspace
  • array

Except for range, these are all part of numpy

https://numpy.org/doc/stable/reference/generated/numpy.linspace.html

a) Which ones can return integers and does the type depend on the arguments? For an array defined with one of these routines what is the length, value of minimum and maximum indices? Which ones return arrays?

b) Create an array of floating point numbers from 0 to 10 including 0 and 10 and with spacing 0.1.

In [8]:
#example of the range iterator and a for loop
for i in range(3):  # note colon
    print(i)   # note indent
    
0
1
2
In [9]:
# an example of the range iterator (that does not work)
for i in range(1,5,0.5):
    print(i)   
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-443a33deacdc> in <module>
      1 # an example of the range iterator (that does not work)
----> 2 for i in range(1,5,0.5):
      3     print(i)

TypeError: 'float' object cannot be interpreted as an integer
In [10]:
# an example of the range interator stepping down
for i in range(5,1,-1):   # start stop step with integers
    print(i)   
5
4
3
2
In [13]:
# an example of using the routine arange() to make an array
x = np.arange(1,10) # given integers,  start stop-increment, increment of 1 assumed
print(x)
print(type(x[0]))  # print the array type!
[1 2 3 4 5 6 7 8 9]
<class 'numpy.int64'>
In [15]:
?x  
# show information on array x
In [16]:
# an example of using arange 
x = np.arange(1.2,10,0.3)  # returns a float array
# arguments are: start stop-increment increment
print(x)
[1.2 1.5 1.8 2.1 2.4 2.7 3.  3.3 3.6 3.9 4.2 4.5 4.8 5.1 5.4 5.7 6.  6.3
 6.6 6.9 7.2 7.5 7.8 8.1 8.4 8.7 9.  9.3 9.6 9.9]
In [17]:
# an example using linspace and size
x = np.linspace(0,1,11)  # makes floats even if passed ints
# arguments are start, stop, number of elements
print(x)
print("size ", np.size(x))  #print the size of this array
[0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
size  11
In [18]:
# an example using arange
x = np.arange(0,3,0.3)
x    # should print the array
Out[18]:
array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7])
In [19]:
# an example using zeros, type() and indexing an element
z=np.zeros(10,dtype=int) # gives an array of integers
print(z)
type(type(z[0])) # print type
[0 0 0 0 0 0 0 0 0 0]
Out[19]:
type
In [20]:
?z

Problem 3. lists and arrays

a) What is the difference between a tuple, an array and a list?

b) Does your arctan function in the first problem operate on tuples? on arrays? or lists?

In [21]:
x = np.array([1.5,2.5,3.3,4])  # an array
In [8]:
?x
In [22]:
y = [1.0, 2.3, 5.8]  # a list
In [10]:
?y
In [23]:
z = (1.0, 2.3, 'ABC') # a tuple
In [12]:
?z

Problem 4. 2d arrays

Create a $N \times N$ 2d-array.
$$a = \left( \begin{array}{cccc} 1 & 2 & 3 & ... & N \\ N+1 & N+2 & N+3 & ... & 2N \\ 2N+ 1 & 2N+2 & 2N+3 & ... & 3N \\ ... \\ \end{array} \right) $$

What indices have the value 3? In other words what are the integers $i,j$ that give $a[i,j]=3$?

It is no longer recommended to use the class matrix, even for linear algebra. Instead 2d-arrays are recommended.

See if you can show your matrix as a color image.

In [24]:
# example of a 2d arrays
b = np.array([[1,2],[3,4]])
print(b)
print(b[0,1])  #right upper corner
# note first index seems to give the row
# and second index seems to give the column
[[1 2]
 [3 4]]
2
In [26]:
# an example of a 2d array and filling it with something
a = np.zeros((100,200))
for i in range(100):  # double for loops!
    for j in range(200):
        a[i,j] = i + j

# note range(100) goes from 0 to 99 and array indexing starts with 0 in python
In [27]:
# make it so that you can plot in the notebook 
%matplotlib inline
from matplotlib import pyplot as plt  #load plotting package

# show the matrix as an image with the routine imshow
plt.imshow(a)  # note that the top left corner has index 0,0
plt.text(5,10,'top left',color='white')
plt.colorbar(fraction=0.03)
#plt.contour(a,levels=10,colors='black')  # if you want contours too!
#plt.colorbar(fraction=0.02)
Out[27]:
<matplotlib.colorbar.Colorbar at 0x7fd019fcd1f0>

Problem 5. writing a function using strings

Write a subroutine that takes 3 arguments, a prefix string, a suffix string and an integer, and returns a string that starts with the prefix, ends with the suffix and has an integer in the middle that is comprised of six digits and instead of spaces has zeros. For example mysub(’a_’,’.txt’,6) should return ’a_000006.txt’. This is a way to make filenames for sequential outputs of a simulation or a sequential set of images that can later be turned into a movie!

see https://docs.python.org/3/library/string.html#format-string-syntax

In [28]:
# examples of string formating
str='a'+'b'+'c'  # string summation
print(str)
abc
In [29]:
# examples of string formating
print('{0:.2f}'.format(12.35799));   # 2 decimal places of a float printed
12.36
In [30]:
# examples of string formating
x=22; y=40;
print('hello{0:d} {1:d}'.format(x,y))  # two integers to format
hello22 40

Problem 6. Plotting a function of a function

Write a subroutine that computes $f(x) = \mu x^3(1-x^3)$. Here $\mu$ is a postive real number.

Make a plot showing $f(x)$ and $f^2(x) = f(f(x))$ and $y=x$ as a function of $x$ in the unit interval $[0,1]$.

Where $f(x)$ crosses the line $y=x$ you have a fixed point because $f(x) = x$.

Where $f^2(x)$ crosses $y=x$ you have a period 2 fixed point because $f^2(x) = x$.

For what values of parameter $\mu$ does $f^2(x)$ have two maxima rather than 1? You can answer this approximately. You don't need to find the range of $\mu$ to high precision.

This function is similar to the logistic function (see https://en.wikipedia.org/wiki/Logistic_map ). When $f^2(x)$ has two maxima a period doubling occurs. This means that there are attracting period two fixed points. For $\mu$ below this value there is only a single attracting fixed point. The function here has similar behavior to the logistic function. There is a universality in behavior, with interates of the function showing period doubling leading to chaos and appearance of period 3 islands after the onset of chaos.

In [37]:
# examples that plot! 
%matplotlib inline
import numpy as np
from matplotlib import pyplot as plt

x = np.linspace(0,1,101)  # an array  of x values
y = np.sin(1*np.pi*x) #an arbitrary function to plot
mu = 3.8
z = mu*x**3*(1-x**3) # another function to pot
plt.plot(x,y, 'g-',label='$sin(\pi x)$') # plot a green line
# the labelling is optional!
plt.plot(x,z, 'b-',label='$\mu x^3 (1-x^3)$')  # plot blue line
plt.plot(x,x, 'k:',label='x') # black line y=x
plt.xlabel('x');  # label the x axis
plt.ylabel('functions'); # labeling is good!  
plt.legend()  # add a legend
# fixed points are where the black line interects the green or blue ones 
Out[37]:
<matplotlib.legend.Legend at 0x7fd018fa6730>

Problem 7. Functions as arguments

Passing functions to functions. Write a routine that returns $f(g(x))$ that takes as arguments $f, g$, that are both functions $f(x), g(x)$. Then use it to plot something like $\sin(10x^2\exp(-x))$. with $f(x) = \sin (x)$ and $g(x) = 10 x^2\exp(-x)$. You will want to plot for a vector of $x$ values. You can make an array of x values using linspace or arange.

Your function should be defined like this

def f_of_g(f,g, ...):

....


where f,g are arguments that you expect will be function names.

If you have

def f(x):

....

def g(x):

...

def h(x):

...

You can compute $f(g(x))$ with f_of_g(f,g,x).

You can compute $f(h(x))$ with f_of_g(f,h,x).

You can compute $h(f(x))$ with f_of_g(h,f,x).

In [36]:
# an example where I pass a function name to another function 

# first I define a function 
def fsquare(x):
    return x**2

# here is a routine that illustrates that I can pass functions as arguments
# to other functions
# thos routine creates an array containing 0,1,2,3, .... N-1
# and operates on it with a function f
# arguments: f a function of a single argument
# arguments: N the number of integers for the array
# returns: the array f(0),f(1),f(2),f(3) ... f(N-1)
def ff_on_ints(f,N):
    x = np.arange(0,N)
    #print(x) for testing
    y = f(x)
    return y

# check to see that my routine works:
z=ff_on_ints(fsquare,10)
print(z)
[ 0  1  4  9 16 25 36 49 64 81]
In [ ]: