There's a special class of sparse graphs called expander graphs. These graphs, while being sparse, still have a number of strong connectivity properties.

The goal of this problem is to create a function which constructs an example of an expander graph. There are a few strategies used to do this; we'll follow an "algebraic" approach due to Margulis, Gabber and Galil. The construction goes like this:

We'll construct a graph on $n^2$ nodes, where the nodes are labeled $(x, y)$ and correspond to points in $Z_n \times Z_n$. Each node $(x, y)$ in $Z_n \times Z_n$ will have eight neighbors given by:

\begin{align*} &(x \pm 2y,\; y)\\ &(x \pm (2y + 1),\; y)\\ &(x,\; y \pm 2x)\\ &(x,\; y \pm (2x + 1)) \end{align*}Use this description to implement a function which constructs an expander graph on $n^2$ nodes.

*Notes:*

- Make sure that you do the neighbor computations mod n.
- Use a
`MultiGraph`

object since there are self-loops and parallel edges. - Drawing the graph using NetworkX is not very useful since it does not draw self-loops and parallel edges.
- For further exploration you could compute the second eigenvalue of the graph Laplacian of this graph as a function of $n$ and see what it does.

Suppose that the company "All-Star Sporting Goods" opens a new store. Shortly after opening, the store has hired 50 employees. After some time, management decides to check on how things are going and wants to ensure that "team spirit" is alive amoung the employees.

In particular, they want to get a sense of how many employees know each other and whether or not anyone is feeling isolated.

After doing a careful survey of the employees, management has made a log of the employees "employees.txt" and a log "friends.txt" of people who are friends with each other.

Using this data, construct a graph with nodes representing the employees and the edges representing friends. (You can assume that the employees are "good, honest people" and that there are no one-sided friendships.)

Now, the company wants to do some analysis on the connections between people. For each employee, use the shortest_path_length function to compute their connection length to everyone else. Then, find how many people are at most distance 3 away. (That is, a friend, a friend of a friend, or a friend of a friend of a friend.)

Suppose a hosptial has a list of willing kidney donors and people who are need of transplants. Due to genetic constraints, not every donor is compatible with every receiver. In particular, suppose a specific attribute, not unlike blood type, has been singled out to determine compatibility. This attribute comes in 4 types with the following property:

Donor type A can give to receiver type A, B, C, or D.

Donor type B can give to receiver type B or D.

Donor type C can give to receiver type C or D

Donor type D can only give to receiver type D.

A list of people marked donor or receiver along with their type is provided in "donor-receiver.txt". Use this data to construct a graph of donors and receivers. Notice that this is a bipartite graph!

Now, we'd like to compute the maximum number of donor-receiver pairs which exist. In more formal terms, we'd like to compute a maximum bipartite matching.

In order to do this, we'll provide an interesting solution via max-cut as follows:

- Create a bipartite graph between donors and receivers such that all edges have capacity 1.
- Add a special source node connected to all donors by an edge of capacity 1.
- Add a special sink node connected to all receivers by an edge of capacity 1
- Compute the maximum flow from the added source to the added sink.

The result from the last step turns out to provide a maximum bipartite matching.