4. Functions and Classes: Exercise Solutions

4. Functions and Classes: Exercise Solutions#

import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

1. Functions#

Define the following functions and show some sample outputs.

  1. Factorial of n: \(1 \times 2 \times \cdots \times n\).

def factorial(n):
    """factorial of n"""
    f = 1
    for i in range(n):
        f = f * (i+1)
    return f
def factorial(n:int):
    """factorial of n"""
    if n < 0 or type(n)!=int:
        print('n should be non-negative integer.')
        return
    elif n==0:
        return 1
    else:
        return factorial(n-1)*n
factorial(-1)
n should be non-negative integer.
for n in range(10):
    print(factorial(n))
1
1
2
6
24
120
720
5040
40320
362880
  1. For a circle of radius r (default r=1), given x coordinate, return possible y coordinates (i.e., both positive and negative).

def circley(x, r=1):
    """y coordinate for given x for a circle of radius r"""
    if abs(x) > r:
        return
    elif abs(x) == r:
        return 0.
    else:
        y = np.sqrt(r**2 - x**2)
        return y, -y
X = np.linspace(-1.2, 1.2, 13)
for x in X:
    print(circley(x))
None
0.0
(np.float64(0.5999999999999999), np.float64(-0.5999999999999999))
(np.float64(0.8), np.float64(-0.8))
(np.float64(0.916515138991168), np.float64(-0.916515138991168))
(np.float64(0.9797958971132712), np.float64(-0.9797958971132712))
(np.float64(1.0), np.float64(-1.0))
(np.float64(0.9797958971132712), np.float64(-0.9797958971132712))
(np.float64(0.9165151389911681), np.float64(-0.9165151389911681))
(np.float64(0.8), np.float64(-0.8))
(np.float64(0.6000000000000003), np.float64(-0.6000000000000003))
(np.float64(2.1073424255447017e-08), np.float64(-2.1073424255447017e-08))
None
for i in range(-6, 7):
    print(circley(0.2*i))
None
0.0
(np.float64(0.5999999999999999), np.float64(-0.5999999999999999))
(np.float64(0.7999999999999999), np.float64(-0.7999999999999999))
(np.float64(0.916515138991168), np.float64(-0.916515138991168))
(np.float64(0.9797958971132712), np.float64(-0.9797958971132712))
(np.float64(1.0), np.float64(-1.0))
(np.float64(0.9797958971132712), np.float64(-0.9797958971132712))
(np.float64(0.916515138991168), np.float64(-0.916515138991168))
(np.float64(0.7999999999999999), np.float64(-0.7999999999999999))
(np.float64(0.5999999999999999), np.float64(-0.5999999999999999))
0.0
None
  1. Draw a star-like shape with n vertices, every m-th vertices connected, with default of n=5 and m=2.

def star(n=5, m=(n-1)//2, r=1):
    """Draw a star with n>=5 vertices."""
    if n < 3:
        print('n should be 3 or more.')
        return
    if m == None:
        m = (n-1)//2
    xy = np.zeros((n+1, 2))
    th = 2*np.pi*m/n
    for i in range(n):
        xy[i] = [r*np.sin(i*th), r*np.cos(i*th)]
    xy[-1] = xy[0]
    plt.plot(xy[:,0], xy[:,1])
    plt.axis('square')
    plt.axis('off')
N = 10
M = 7
for n in range(5, 5+N):
    for m in range(1, min((n+1)//2, M)):
        plt.subplot(M, N, (m-1)*N + n-4)
        star(n, m)
_images/b8596de585a971359147913f545e51ae18e9120a3dddf11f7991a6396415a901.png
  1. Any function of your interest

2. Classes#

  1. Extend the Cell, gCell or Culture class with new methods, such as:

  • cells with other shapes, e.g. ellipse, polygon,…

  • move to avoid overlap with another cell

  • connect with another cell like a neuron

  • growth based on the environment, e.g., number of neighbors

# example of drawing an ellipse
import matplotlib.patches as pat
p = pat.Ellipse(xy=(1,2), width=2, height=1, angle=30)
plt.gca().add_patch(p)
plt.axis('equal');
_images/b9502b3c90037a32025c13aec8c11a19b6a0b2c353b7bddf6a46794ebadbe383.png
# example of drawing a polygon
xy = np.array([[np.sin(th),np.cos(th)] for th in np.arange(0, 4*np.pi, np.pi*4/5)])
p = pat.Polygon(xy)
plt.gca().add_patch(p)
plt.axis('equal');
_images/4f7908d22864b8bc094d65e3efd9f84108c130a5ed1d97f03dd85887773f822a.png
class Cell:
    """Class for a cell"""

    def __init__(self, position = [0,0], radius=0.1, color=[1,0,0,0.5]):
        """Make a new cell"""
        self.position = np.array(position)
        self.radius = radius
        self.color = color
     
    def show(self):
        """Visualize as a circule"""
        c = plt.Circle(self.position,self.radius,color=self.color)
        plt.gca().add_patch(c)
        plt.axis('equal')
class Neuron(Cell):
    """Neuron class based on Cell class"""
    
    def __init__(self, position=[0,0], radius=0.2, color=[1,0,0,0.5]):
        """Create a neuron"""
        super().__init__(position=position, radius=radius, color=color)  # call the constructor of Cell class
        self.naxon = 0  # number of axons
        self.axon = []  # base and termian positions of axons
    
    def connect(self, target):
        """Connect axon to a target neuron"""
        vec = target.position - self.position  # vector to the target center
        vec = vec/np.sqrt(sum(vec**2))  # normalize to length 1
        base = self.position + self.radius*vec  # base of the axon
        term = target.position - target.radius*vec  # synaptic terminal
        self.axon.append(np.array([base,term]))
        self.naxon += 1   # increment the axon number
    
    def show(self):
        """show the neuron with axons"""
        super().show()  # show the cell body
        for i in range(self.naxon):
            plt.plot(self.axon[i][:,0], self.axon[i][:,1], c=self.color)  # axon
            plt.plot(self.axon[i][-1,0], self.axon[i][-1,1], 'o', c=self.color)  # synaptic terminal
# Neurons with no connections
n1 = Neuron()
n2 = Neuron([1,0], color='g')
n3 = Neuron([0.5,1], color='b')
n1.show(); n2.show(); n3.show();
_images/e849511df86e5d1d1a08aa54001c27ec089362fe5cb50d2e84950670debb86cb.png
# Triangular connections
n1.connect(n2)
n2.connect(n3)
n3.connect(n1)
n1.show(); n2.show(); n3.show();
_images/fd029d145ff29d8cd62486e391156af8e6b09f69a1a35fc52753402c69695bea.png
  1. Save the defined class(es) as a module cell.py.

  1. Import the module and test how it works.

import cell
import importlib
importlib.reload(cell) # This is needed after updating a module
<module 'cell' (namespace) from ['/Users/doya/OIST Dropbox/kenji doya/Python/iSciComp/cell']>
# Neurons with no connections
n1 = cell.Neuron()
n2 = cell.Neuron([1,0], color='g')
n3 = cell.Neuron([0.5,1], color='b')
# Triangular connections
n1.connect(n3)
n2.connect(n1)
n3.connect(n2)
n1.show(); n2.show(); n3.show();
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
Cell In[20], line 2
      1 # Neurons with no connections
----> 2 n1 = cell.Neuron()
      3 n2 = cell.Neuron([1,0], color='g')
      4 n3 = cell.Neuron([0.5,1], color='b')

AttributeError: module 'cell' has no attribute 'Neuron'