4. Introduction to Classes
First I wanted to take a moment to apologize for my sudden absence here. I had a job transition and then I got married.
This post will introduce the concept of classes and why they are useful. We will use shapes as an example because of their familiarity and natural hierarchical structure (ie. a square is a type of rectangle). Then we will give an example of how this will be useful for our Ising Model.
What are classes:
A class is a collection of data and functions. These data are called members or properties, and the functions are called methods. An instance of class is an object (you might have heard the term object-oriented programming). Classes can inherit from other classes. When one class inherits from another it copies the data and functions. The class that is doing the inheriting is the child or derived class. The class that is being inherited is called the parent or base class.
For example, if we can define a Rectangle class that has length and width as its data and functions that calculate its area, and perimeter, and can draw itself in a terminal. We will initialize an instance of a Rectangle by providing a height and a width. We could then define a class Square that inherits from Rectangle. Here Square is the child or derived class and Rectangle is the parent or base class. We will initialize an instance of Square by providing a single number: the side length. Square will then call the initializer of Rectangle with the side length for the height and the width. Then without writing any more code Square will be able to use the area, perimeter, and draw methods provided by Rectangle. The code for Rectangle and Square in Python is below.
Why use classes:
Classes are a way of making your code modular. This makes it easier to reuse code you write and makes it easier to test code you have written.
Classes are a way of organizing your code into “objects” with well defined interfaces. An interface is a collection of publicly accessible functions that the class provides.
When two objects have the same interface they can then be treated similarly even if the implementation of those interfaces is different. For example, you might have a square grid and a triangular grid. Both of these grids could have the same interface. That is, you might construct them in the same manner (Giving a Nx and a Ny), and each could provide a list of nearest neighbors to a given site. These different types of grids will have different answers when asked for nearest neighbors of a site, but the way they are asked for a neighbor list and the format of the answer will be the same. This allows us to separate out the logic that governs the grid type from the rest of the algorithm. This makes the code more modular and allows us to swap out the grid type. This has the added benefit of making it easy to add new grid types to the code, as once we have defined the behavior specific to the new type of grid it is ready to be used by the rest of the algorithm. This concept in programming is called polymorphism.
This strategy of breaking up logical units of the code into classes that can be used in other classes is called Composition, as we have created objects that are composed of other objects. The use of both Inheritance and Composition make object oriented programming a powerful tool for expressing logic in a reusable and readable way.
Application:
Now with a basic understanding of how classes work, let's talk about how they could be useful in our Ising model example. We’ll start by considering two lattices: a square lattice and a triangular lattice. The relevant difference between these lattices for our model is which spins are considered nearest neighbors. If we have two classes SquareLattice and TriangularLattice, they can both inherit from a base Lattice class. SquareLattice and TriangularLattice would implement the nearest neighbor code differently and the base class would contain all the code that both lattices need to use. Notably, if we have code to detect clusters on our lattice it could live in the base Lattice class and utilize a nearestNeighbors() function implemented by its derived classes. Using classes in this manner allows us to write a generic cluster algorithm where we leave the specifics of lattice to the specific lattice class. Likewise when we write our function to calculate the energy of a spin we can write it in a way that is lattice agnostic because our lattices share an interface that we can use to obtain a nearest neighbor list.
Vocab
This post introduces a lot of new vocab. I have summarized it again here.
Class - A collection of data and functions
Object - An instance of a class. I.E not just the class square but a specific square.
Members - The data of a class
Methods - The functions or behavior of a class
Class Interface - The public methods of a class (as opposed to private functions not intended for use outside the class). How other part of the code interact with a class
Inheritance - How one class copies data and behaviors from another class
Child (Derived) Class - A class that inherits behavior and data
Parent (Base) Class - A class that has been inherited from
Polymorphism - Classes with the same interface can be interchanged
Composition - Classes using other classes to perform their function