Matrix + Graph in Ruby
Tags: ruby, programming
Published: 2023-01-10 07:24:58

## Introduction to Matrices in Ruby

A matrix is a two-dimensional array of numbers. It is often used to represent and manipulate linear transformations in mathematics and computer science. In ruby, we can represent a matrix as an array of arrays, with each inner array representing a row of the matrix.

Here is an example of how to create a matrix in ruby:


```ruby
# Create a 3x3 matrix with all zeros
matrix = Array.new(3) { Array.new(3, 0) }

# Create a 2x2 matrix with specific values
matrix = [[1, 2], [3, 4]]

# Create a 3x3 identity matrix
matrix = Array.new(3) { |i| Array.new(3) { |j| i == j ? 1 : 0 } }
```

We can access the elements of a matrix using the [] operator. For example, to get the element at the second row and third column of the matrix above, we can do the following:

```ruby
matrix[1][2]
```

To perform operations on matrices, we can use the Matrix class from the matrix library in ruby. This class provides methods for matrix addition, subtraction, multiplication, and other operations.

```ruby
require 'matrix'

# Create two matrices
matrix_a = Matrix[[1, 2], [3, 4]]
matrix_b = Matrix[[5, 6], [7, 8]]

# Perform matrix addition
matrix_c = matrix_a + matrix_b

# Perform matrix multiplication
matrix_d = matrix_a * matrix_b
```

## Graphs and Matrices

A graph is a collection of interconnected nodes or vertices, represented by points on a plane. These nodes can be connected by edges, which represent the relationships between the nodes.

In computer science, graphs are often used to represent networks, such as social networks or communication networks. They can also be used to represent data structures, such as trees and lists.

There are two common ways to represent graphs in computer algorithms: adjacency lists and adjacency matrices.

An adjacency list represents a graph as an array of linked lists. Each element in the array represents a node in the graph, and the linked list for that element contains the nodes that are connected to it by an edge.

An adjacency matrix is a two-dimensional matrix that represents a graph. The rows and columns of the matrix represent the nodes in the graph, and the elements of the matrix represent the edges between the nodes.

Here is an example of how to create an adjacency matrix for a simple graph in ruby:

```ruby
# Create an empty matrix with the same number of rows and columns as the number of nodes in the graph
matrix = Array.new(num_nodes) { Array.new(num_nodes, 0) }

# Set the elements of the matrix to 1 to represent the edges between the nodes
matrix[0][1] = 1
matrix[0][2] = 1
matrix[1][2] = 1
```

We can use the adjacency matrix to represent the graph and perform operations on it. For example, we can use it to determine the degree of a node, which is the number of edges connected to it. To do this, we can sum the elements in the row or column of the matrix corresponding to the node.

```ruby
# Find the degree of node 0
degree = matrix[0].sum
```

We can also use the adjacency matrix to determine whether there is an edge between two nodes. If the element at the intersection of the rows and columns corresponding to the nodes is 1, then there is an edge. If it is 0, then there is no edge.

```ruby
# Check if there is an edge between node 0 and node 1
if matrix[0][1] == 1
  puts "There is an edge between node 0 and node 1"
else
  puts "There is no edge between node 0 and node 1"
end
```

Another operation we can perform on a graph using its adjacency matrix is finding the shortest path between two nodes. This can be done using algorithms such as Dijkstra's algorithm or the Floyd-Warshall algorithm.

```ruby
# Find the shortest path between node 0 and node 2 using Dijkstra's algorithm
require 'set'

def dijkstra(matrix, source, target)
  # Initialize distances and previous nodes
  distances = Array.new(matrix.size, Float::INFINITY)
  prev_nodes = Array.new(matrix.size, nil)
  distances[source] = 0

  # Create a set of unvisited nodes
  unvisited_nodes = Set.new((0...matrix.size).to_a)

  # Iterate until there are no unvisited nodes
  while !unvisited_nodes.empty?
    # Find the node with the minimum distance
    curr_node = unvisited_nodes.min_by { |node| distances[node] }

    # Break if we have reached the target node
    break if curr_node == target

    # Remove the current node from the set of unvisited nodes
    unvisited_nodes.delete(curr_node)

    # Update the distances of the neighbors
    (0...matrix.size).each do |neighbor|
      # Skip if there is no edge between the current node and the neighbor
      next if matrix[curr_node][neighbor] == 0

      # Calculate the distance to the neighbor
      alt = distances[curr_node] + matrix[curr_node][neighbor]

      # Update the distance and previous node if necessary
      if alt < distances[neighbor]
        distances[neighbor] = alt
        prev_nodes[neighbor] = curr_node
      end
    end
  end

  # Return the shortest path
  path = []
  curr_node = target
  while curr_node
    path.unshift(curr_node)
    curr_node = prev_nodes[curr_node]
  end
  path
end

shortest_path = dijkstra(matrix, 0, 2)
```

## Incidence Matrices

In addition to adjacency matrices, another way to represent a graph using a matrix is through an incidence matrix. An incidence matrix is a matrix with a row for each node and a column for each edge, and the elements of the matrix represent whether a node is connected to an edge.

Here is an example of how to create an incidence matrix for a simple graph in ruby:

```ruby
# Create an empty matrix with the same number of rows as the number of nodes and the same number of columns as the number of edges
matrix = Array.new(num_nodes) { Array.new(num_edges, 0) }

# Set the elements of the matrix to 1 to represent the connections between the nodes and edges
matrix[0][0] = 1
matrix[1][0] = 1
matrix[1][1] = 1
matrix[2][1] = 1
```

We can use the incidence matrix to perform various operations on the graph, such as finding the degree of a node or determining the endpoints of an edge.

```ruby
# Find the degree of node 0
degree = matrix[0].sum

# Find the endpoints of edge 1
endpoints = (0...num_nodes).select { |node| matrix[node][1] == 1 }
```

## NetworkX

The [networkx](https://github.com/SciRuby/networkx.rb) library is a powerful tool for working with graphs in ruby. It provides classes for representing graphs, as well as algorithms for analyzing and manipulating them.

Here is an example of how to use networkx to create and manipulate a graph:

```ruby
require 'networkx'

# Create an empty graph
g = NetworkX::Graph.new

# Add nodes to the graph
g.add_node(0)
g.add_node(1)
g.add_node(2)

# Add edges to the graph
g.add_edge(0, 1)
g.add_edge(0, 2)
g.add_edge(1, 2)

# Find the degree of node 0
degree = g.degree(0)

# Find the shortest path between node 0 and node 2
shortest_path = NetworkX.shortest_path(g, 0, 2)
```

## Conclusion

Matrices are a powerful tool for representing and manipulating graphs in computer algorithms. Whether you use adjacency matrices, incidence matrices, or a library like networkx, there are many ways to work with graphs in ruby. I hope this article has been helpful in understanding some of the basics and giving you some ideas for further exploration.