Create matrix with same in and out degree for all nodes - python

I've stated this question in graph theory terms, but that conceptualization isn't necessary.
What I'm trying to do, using Python, is produce a matrix of zeros and ones, where every row has the same number of ones and every column has the same number of ones. The number for rows will not be the same as the number for columns when the number of rows (sending nodes) does not equal the number of columns (receiving nodes) -- which is something I'm allowing.
It makes sense to me to do this in numpy, but there may be other packages (like networkx?) that would help.
Here's the function I'm looking to write with the desired inputs and outputs:
n_pre = 4 # number of nodes available to send a connection
n_post = 4 # number of nodes available to receive a connection
p = 0.5 # proportion of all possible connections that exist
mat = generate_mat(n_pre, n_post, p)
print mat
The output would be, for example:
[[0, 1, 0, 1],
[1, 0, 1, 0],
[1, 1, 0, 0],
[0, 0, 1, 1]]
Notice, every column and every row has two ones in it. Aside from this constraint, the positions of the ones should be random (and vary from call to call of this function).
In graph theory terms, this means every node has an in-degree of 2 and an out-degree of 2 (50% of all possible connections, as specified with p = 0.5).

For a square matrix, what you describe is the adjacency matrix of a random k-regular directed graph, and there are known algorithms to generate such graphs. igraph implements one:
# I think this is how you call it - it's an instance method for some reason.
igraph.Graph().K_Regular(n, k, directed=True)
networkx has a function for random k-regular undirected graphs:
networkx.random_regular_graph(k, n)
For a non-square matrix, what you describe is isomorphic to a random biregular graph. I have found no convenient existing implementation for random biregular graphs, but the term should be a good starting point for searching for known algorithms.

First, do the pre-work so that we have available the size of the square matrix and the population pop of each row and column. Now, initialize a matrix with pop ones on the diagonal. For n = 6 and pop = 3, you'd have
[[1, 1, 1, 0, 0, 0]
[0, 1, 1, 1, 0, 0]
[0, 0, 1, 1, 1, 0]
[0, 0, 0, 1, 1, 1]
[1, 0, 0, 0, 1, 1]
[1, 1, 0, 0, 0, 1]]
Now, apply your friendly neighborhood random shuffle operation to the columns, then the rows (or in the other order). There's your matrix. A shuffle of rows-only or columns-only does not change the population on either axis.

Related

numpy vectorization of cellular automata

Trying to optimize my current implementation of a program that generates cellular automata using Wolfram Numbering. I am having trouble applying the rule to the board after calculating the neighbors for each cell. The current example uses 2 states and is the same as Conway's Game of Life, but my program can do any number of states. The decimal 224 corresponds to the ruleset for CGOL in the following way:
[0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0]
Basically, there are 18 positions, or nine possible neighborhood sums for each state (0-8).
If the current cell is 1, you index into the rule in the following way:
>>> [0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0][1::2]
[0, 0, 1, 1, 0, 0, 0, 0, 0]
1 being the value of the cell, and 2 being the number of states. As you can see, if the state is 1, then if there are 2 or 3 neighbors the cell survives, else dies. From there you index w/ the neighborhood sum for that cell to get the actual update value of the cell. So to update a cell in each generation you do: Rule[state_value::total_states][sum of neighbors].
E.g.,
>>> [0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0][1::2][2]
1
Currently then, I have the grid of all cells, called world, of an arbitrary shape, another equally shaped numpy array that has the sum of all the neighbors for each of those cells calculated using convolve from scipy - call it nbrs -, and the previously mentioned list for the rule - is it possible to update the value of each cell in world while avoiding a for loop?
For instance:
world = rule[cell_value::total_states][sum_of_neighbors_for_given_cell_in_nbrs]
You haven't given us a lot of code to work with, so here is a minimal idea of how it could work.
First, create an array that you can index with the current state to get the rule:
rule = [0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0]
rule_map = np.stack((rule[0::2], rule[1::2]), axis=0)
Now you can use your current states to get the rule for each cell:
world = np.random.randint(2, size=(5, 6))
cur_cell_rules = rule_map[world] # shape: (5, 6, 9)
To get the new world state, we can use index interators. Here, I use an array containing all world indices to first get the (flattened) current cell neighborhood sums, and then use those to get the (flattened) new states. In the assignment, I unflatten it again to the world shape. (There probably is an easier way to do this...)
cur_cell_neighborhood_sum = ... # shape: (5, 6)
world_ind = np.asarray([*np.ndindex(world.shape)])
# update world
world[world_ind[:, 0], world_ind[:, 1]] = cur_cell_rules[world_ind[:, 0], world_ind[:, 1], cur_cell_neighborhood_sum[world_ind[:, 0], world_ind[:, 1]]]
Edit:
To avoid the large cur_cell_rules array, you can go the other way, too:
world_potential = rule_map[:, cur_cell_neighborhood_sum] # shape: (2, 5, 6)
# update world, this time in smaller steps
world_flat = world[world_ind[:, 0], world_ind[:, 1]]
world_new_flat = world_potential[world_flat, world_ind[:, 0], world_ind[:, 1]]
world[world_ind[:, 0], world_ind[:, 1]] = world_new_flat

The neighbours() Function

I am working on a new question. How am I supposed to develop the a solution for the following:
The neighbors() Function
In a 2D array, each element has up to eight neighbors - the cells immediately to the north, east, west, south, northeast, northwest, southeast, and southwest of it. Of course, elements on the boundary of the array have fewer neighbors (entries at the four corners only have three neighbors). Your task is to write a function called neighbors() that takes a 2D array named input, a row index, and a column index as input and returns the number of neighbors of array[row][column] that are 1's.
For example, if the 2D input array is
array = [ [0, 0, 0, 0], [1, 1, 0, 1], [0, 0, 0, 1] ]
representing the array
0__0__0__0
1__1__0__1
0__0__0__1
then here are some examples of the function being used:
>>> neighbors(array, 1, 1)
1
>>> neighbors(array, 2, 2)
3

gmsh, how to generate serendipity elements

For Finite-Element simulations I need higher order meshes.
For sake of efficiency I want to use serendipity elements, i.e. elements without interior nodes.
The setOrder() function of gmsh was easy to find, it generates Lagrangian elements.
How can I set another element type, or somehow remove the interior nodes?
In the following example of a square in 2D the generated quadrilaterals of second order have 9 nodes each, I would like to have only 8 nodes per element.
Interestingly, gmsh seems to know these element types, as they are mentioned in the documentation of the file format elm-type=10 and elm-type=16, respectively.
import numpy
import gmsh
gmsh.initialize()
gmsh.model.add("mini")
dim1=1
dim2=2
gmsh.model.geo.addPoint(0, 0, 0, 0.5, 1)
gmsh.model.geo.addPoint(1, 0, 0, 0.5, 2)
gmsh.model.geo.addPoint(1, 1, 0, 0.5, 3)
gmsh.model.geo.addPoint(0, 1, 0, 0.5, 4)
gmsh.model.geo.addLine(1, 2, 1)
gmsh.model.geo.addLine(2, 3, 2)
gmsh.model.geo.addLine(3, 4, 3)
gmsh.model.geo.addLine(4, 1, 4)
gmsh.model.geo.addCurveLoop([1, 2, 3, 4], 1)
gmsh.model.geo.addPlaneSurface([1], 1)
Square = gmsh.model.addPhysicalGroup(dim2, [1])
gmsh.model.setPhysicalName(dim2, Square, "Unit_Square")
gmsh.model.geo.mesh.setRecombine(dim2, 1)
gmsh.model.geo.synchronize()
gmsh.model.mesh.generate(dim2)
gmsh.model.mesh.setOrder(2) # generates Laplacian elements, but I want Serendipity
gmsh.write("mesh_order2.msh")
gmsh.finalize()
Christophe Geuzaine answered my question, the keyword is "incomplete elements"
https://gitlab.onelab.info/gmsh/gmsh/-/issues/1272
meaning for my example
...
gmsh.model.mesh.generate(dim2)
gmsh.option.setNumber('Mesh.SecondOrderIncomplete', 1) # <-- that's it!
gmsh.model.mesh.setOrder(2)
...

How to make adjacency matrix for sub-graphs detected by leidenalg

I need to make adjacency matrix for each community(sub-graph) detected by leidenalg; but the problem is the output of find_partition() is just showing the nodes in each sub-graph. Is there any way to convert the output to something like np.array with edge information of each sub-graph??
import leidenalg
import igraph as ig
G = ig.Graph.Erdos_Renyi(10, 0.1);
partitions = leidenalg.find_partition(G, leidenalg.ModularityVertexPartition)
print(partitions)
output:
Clustering with 10 elements and 3 clusters
[0] 2, 5, 8, 9
[1] 3, 4, 6
[2] 0, 1, 7
You can do this by simply constructing the subgraph and the computing the adjacency matrix. Your example is not quite reproducible because ig.Graph.Erdos_Renyi uses the random number generator. Therefore, I added a little code to set the random seed and generate a graph like yours, except reproducible. I simply get the adjacency matrix for the first partition, but of course, you can just loop through the partitions and get all of the matrices.
import igraph as ig
import leidenalg
import random
random.seed(a=321)
G = ig.Graph.Erdos_Renyi(10, 0.28);
partitions = leidenalg.find_partition(G, leidenalg.ModularityVertexPartition)
print(partitions)
P0 = G.subgraph(partitions[0])
P0.get_adjacency()
Out[15]: Matrix([[0, 1, 1, 0], [1, 0, 1, 1], [1, 1, 0, 1], [0, 1, 1, 0]])

Trying to extract a patch given 8 points from NumPy array

I have an array which looks like this
boxes = [268,885,426,865,406,707,248,727]
It's a collection of (x,y) points. If I plot this using this function:
def draw_boxes_on_image_mod(image, boxes):
image_copy = image.copy()
image_copy = np.array(image_copy)
cv2.line(image_copy, (boxes[0],boxes[1]),(boxes[2],boxes[3]),(0,255,255),2)
cv2.line(image_copy, (boxes[4], boxes[5]),(boxes[6],boxes[7]),(0,255,255),2)
cv2.line(image_copy, (boxes[0],boxes[1]),(boxes[6],boxes[7]),(0,255,255),2)
cv2.line(image_copy, (boxes[4], boxes[5]),(boxes[2],boxes[3]),(0,255,255),2)
scipy. misc.imsave('/home/ryan/TEST.png', image_copy)
return image_copy
I get an image with a rectangle drawn on the part of the image I'm interested in, But what I want is to extract that portion and convert it into an image.
I was thinking of using NumPy indexing to achieve this but
image = image[268:426]
I am finding it difficult to understand how to index the (x,y) values together.
Any suggestions would be really helpful.Thanks in advance.
When you just call A[1:3] all you are asking for are the rows 1 and 2, the rows including 1 and stopping before 3, so you must take into account columns as well to get the exact subsection you need.
You can do this in numpy by stating the range of the rows and columns, the subsection of the array you want will start at a row and end at row + m as well as starting at a column and ending at column + n
For example take
A = np.array([[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]])
We want just the values in the middle set to 1, so we select them with
Asub = A[1:3,1:3]
To get
[[1 1]
[1 1]]

Categories