Assignment 6 Feedback

Closed at 2pm Monday 20th; I can't accept late submissions unfortunately.

For Assignment 7, due 2 pm Monday, beware not to overcomplicate things and avoid premature optimization.

This would get all the marks and can be made in < 5 seconds

Numpy vectorization

  def findPi(n_points):
      rs = numpy.random.uniform(0, 1, size=(n_points,2))
      return 4 * np.sum(np.linalg.norm(rs, axis = 1) < 1)/n_points
  
  def integrate(n_points, dim):
      rs = numpy.random.uniform(-1, 1, size=(n_points,dim))
      return np.sum(np.linalg.norm(rs, axis = 1) < 1)/n_points * 2**dim]]
      

Can get to $10^8$ in ~ 25 seconds with chunking but this is close to the cut off.

Numpy vectorization

  def integrate(n_points, dim, chunk_size=1000000):
      # If less than 1,000,000 points operate in one go
      if n_points <= chunk_size:
          rs = np.random.uniform(-1, 1, size=(n_points, dim))
          return np.sum(np.linalg.norm(rs, axis=1) < 1) / n_points * 2**dim

      # Else do a for loop of 1,000,000 points at a time
      total_sum = 0
      num_chunks = int(np.ceil(n_points / chunk_size))
      for i in range(num_chunks):
          remaining_points = min(chunk_size, n_points - i * chunk_size)
          rs = np.random.uniform(-1, 1, size=(remaining_points, dim))
          # Add to the total sum here
          total_sum += np.sum(np.linalg.norm(rs, axis=1) < 1)
      return total_sum / n_points * 2**dim
      

What is wrong here?

What is wrong here?

What is wrong here?

What is wrong here?

What is wrong here?

This would get all the marks.

This would get all the marks.

This would get all the marks.

This would get all the marks.

This would get all the marks.

Object oriented programming

Systems Modeling: OOP is beneficial for modeling complex systems with numerous interacting components, such as simulations of physical phenomena where entities can be modeled as objects.

Code Reusability and Extensibility: In projects where you'll be building upon existing models or simulations, OOP allows for easier extension and modification of code through inheritance and polymorphism.

Not better or worse but another approach / tool.

As is tradition, we'll use cats to explain OOP.

The Cat Constructor: __init__

All python classess need the '__init__' constructor.

This constructor initializes a new cat instance with attributes like name, color, and age.

class Cat:
  def __init__(self, name, color, age):
      self.name = name
      self.color = color
      self.age = age

newCat = Cat('larry', 'black', 3)
print(newCat.name)
# larry
              

Cat Methods: Interacting with Our Cat

Methods allow interaction with cat instances, such as making them meow or jump.

class Cat:
  # ...
  def meow(self):
      return "Meow!"
  def jump(self, height):
      return f"Jumps {height} feet high!"

print(newCat.meow())
print(newCat.jump(3))
# Meow!
# Jumps 3 feet high!
    

Cat Auxiliary Methods: __str__ and __repr__

Explaining __str__ for user-friendly description and __repr__ for detailed developer view.

class Cat:
  # ...
  def __str__(self):
      return f"{self.name}, the {self.color} cat"
  def __repr__(self):
      return f"Cat('{self.name}', '{self.color}', {self.age})"

print(newCat)
print(repr(newCat))
# larry, the black cat
# Cat('larry', 'black', 3)

          

Cat Inheritance: Creating Different Breeds

Illustrating inheritance with different cat breeds. A base Cat class and derived classes like Siamese or Persian.

class Siamese(Cat):
  def purr(self):
      return "Loud purring"

class Persian(Cat):
  def fluff(self):
      return "Maximum fluffiness"

sia = Siamese('Whiskers', 'grey', 4)
print(sia.meow())
print(sia.purr())
# Meow!
# Loud purring
    

Persian Cat Siamese Cat

Destructors and Operator Overloading

The __del__ destructor method is called when an instance is about to be destroyed. Though not commonly used, it's helpful for clean-up activities.

Operator overloading, using methods like __add__, allows custom behavior for Python's built-in operators. For example, adding two cat instances could combine their attributes.

class Cat:
  # ...
  def __del__(self):
      print(f"{self.name} says goodbye!")

  def __add__(self, other):
      return Cat(f"{self.name} & {other.name}", self.color, max(self.age, other.age))

cat1 = Cat('Luna', 'grey', 2)
cat2 = Cat('Milo', 'brown', 4)
newCat = cat1 + cat2
print(newCat)
# Cat('Luna & Milo', 'grey', 4)
              

Cats Together

Procedural way to rotate a set of points

In [29]:
import numpy
  points = [ [1, 2], [4,-2] ]
  angle = numpy.pi/4 
  
  def rotate(points, angle):
      cos = numpy.cos(angle)
      sin = numpy.sin(angle)
      rotated = []
      for x,y in points:
          newx = cos * x - sin * y 
          newy = sin * x + cos * y 
          rotated.append( [newx, newy] )
      return rotated
  
  rotate(points, angle) 
  
Out[29]:
[[-0.7071067811865474, 2.121320343559643],
   [4.242640687119286, 1.4142135623730947]]

Point class definition

In [34]:
class Point():
        def __init__(self, x, y):
            self.x = x
            self.y = y
            
        def rotate_by(self, angle):
            cos = numpy.cos(angle)
            sin = numpy.sin(angle)
            newx = cos * self.x - sin * self.y 
            newy = sin * self.x + cos * self.y 
            self.x = newx
            self.y = newy
            
        def __str__(self):
            return "({},{})".format(self.x, self.y)
    
        def __repr__(self):
            return "Point({},{})".format(self.x, self.y)
        
        
    

Object oriented way

In [33]:
points = [ Point(1,2), Point(4,-2) ]
  angle = numpy.pi/4 
  
  for p in points:
      p.rotate_by(angle) 
      
  points
  
Out[33]:
[Point(-0.7071067811865474,2.121320343559643),
   Point(4.242640687119286,1.4142135623730947)]

Link click rates are falling.