Skip to content

Commit

Permalink
Merge pull request #12 from AlainKadar/compute_api
Browse files Browse the repository at this point in the history
Compute API
  • Loading branch information
AlainKadar authored Feb 5, 2024
2 parents 5b14322 + 1fe78d1 commit c66683a
Show file tree
Hide file tree
Showing 68 changed files with 1,747 additions and 1,478 deletions.
9 changes: 4 additions & 5 deletions AverageNodalConnectivityCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,19 @@ AverageNodalConnectivityCast::~AverageNodalConnectivityCast () {}

void AverageNodalConnectivityCast::average_nodal_connectivity_compute () {
igraph_t* g = (igraph_t*)this->G_ptr;
printf("Completed\n");
int num_verts = igraph_vcount(g);
int den = 0;
igraph_integer_t nc;
float total_connectivity = 0;
for (igraph_integer_t i=0; i<num_verts; i++) {
for (igraph_integer_t j=i+1; j<num_verts; j++) {
igraph_st_vertex_connectivity(g, &nc, i, j, (igraph_vconn_nei_t)IGRAPH_VCONN_NEI_NEGATIVE);
if (nc == -1) { continue; }
total_connectivity += nc;
den++;
}
}
printf("Completed\n");

anc = 2 * total_connectivity / float(num_verts) / (float(num_verts)-1);

anc = total_connectivity / float(den);
igraph_destroy(g);
}

Expand Down
Binary file added Example/ANF/slice0000.tiff
Binary file not shown.
24 changes: 24 additions & 0 deletions Example/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from StructuralGT import networks
import numpy as np
from StructuralGT.electronic import Electronic
from StructuralGT.structural import Structural

options = {"Thresh_method":0, "gamma": 3, "md_filter": 0, "g_blur": 1,
"autolvl": 0,"fg_color":0,"laplacian": 0, "scharr": 0, "sobel":0 ,
"lowpass": 1, "asize":7, "bsize":3, "wsize":5, "thresh": 103}

ANF = networks.Network('ANF')
ANF.binarize(options_dict=options)
ANF.img_to_skel(crop=[200,300,200,300], rotate=45, merge_nodes=5, remove_objects=10)
ANF.set_graph(weight_type=['FixedWidthConductance'], rho_dim=1, R_j=12, sub=True)

S = Structural()
S.compute(ANF)
print(S.diameter)

E = Electronic()
E.compute(ANF, 0, 0, [[0,50],[350,400]])
print(E.effective_resistance)


print(ANF.graph.transitivity_undirected())
14 changes: 9 additions & 5 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ The following minimal example shows how the package can be used to calculate the

.. code:: python
from StructuralGT import physical_networks
from StructuralGT.structural import Structural
from StructuralGT.networks import Network
Nanofibre3DNetwork = physical_networks.StructuralNetwork('Nanofibre_Image_Stack')
Nanofibre3DNetwork = Network('Nanofibre_Image_Stack')
Nanofibre3DNetwork.binarize()
Nanofibre3DNetwork.stack_to_gsd(crop=[0,500,0,500,0,500])
Nanofibre3DNetwork.node_calc(betweenness=True)
Nanofibre3DNetwork.node_labelling(Nanofibre3DNetwork.betweenness)
Nanofibre3DNetwork.img_to_skel(crop=[0,500,0,500,0,500])
Nanofibre3DNetwork.set_graph(weight_type=['Length'])
S = Structural()
S.compute(ANF)
print(S.diameter)
89 changes: 35 additions & 54 deletions RandomBetweennessCast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ void RandomBetweennessCast::random_betweenness_compute () {
igraph_real_t* weights_arr = (double *)weights_ptr;
igraph_vector_init_array(&weights_vec, weights_arr, num_edges);

//std::cout << num_verts << "\n";
/*Prepare Laplacian as Eigen Array*/
igraph_sparsemat_t A, compA;
igraph_sparsemat_t L, compL;
Expand All @@ -36,80 +35,62 @@ void RandomBetweennessCast::random_betweenness_compute () {
&weights_vec, IGRAPH_NO_LOOPS);
igraph_sparsemat_compress(&L, &compL);
igraph_sparsemat_compress(&A, &compA);
//igraph_sparsemat_print(&L, stdout);
igraph_sparsemat_iterator_t mit;
Eigen::MatrixXf eigL = Eigen::MatrixXf::Zero(num_verts, num_verts);
Eigen::MatrixXf L_reduced = Eigen::MatrixXf::Zero(num_verts-1, num_verts-1);

int row, col;
float val;
igraph_sparsemat_iterator_init(&mit, &compL);
for (int i=0; i<(num_edges*2+num_verts); i++) {

row = igraph_sparsemat_iterator_row(&mit);
col = igraph_sparsemat_iterator_col(&mit);
if ((row == 0) || (col == 0)) {
igraph_sparsemat_iterator_next(&mit);
continue;
}

val = igraph_sparsemat_iterator_get(&mit);

eigL(row, col) = val;
// if (igraph_sparsemat_iterator_end(&mit)) { break; }
// printf("Setting %i,%i equal to %f\n", row,col,val);
L_reduced(row-1, col-1) = val;
igraph_sparsemat_iterator_next(&mit);
}

//igraph_integer_t* sources_arr = (long long *)sources_ptr;
//igraph_integer_t* targets_arr = (long long *)targets_ptr;

/*Invert Laplacian*/
//eigL.completeOrthogonalDecomposition().pseudoInverse();
Eigen::MatrixXf pinv(num_verts, num_verts);
pinv = eigL.completeOrthogonalDecomposition().pseudoInverse();
Eigen::MatrixXf L_red_inv(num_verts-1, num_verts-1);
L_red_inv = L_reduced.inverse();

//std::cout << pinv << "\n";
/*Get matrix C*/
Eigen::MatrixXf C(num_verts, num_verts);
// Set the first column and row to zeros
C.col(0).setZero();
C.row(0).setZero();
C.block(1, 1, num_verts-1, num_verts-1) = L_red_inv;

/*Here, from/to refer to the edge endpoints; not the source/targets used
* to calculate the betweenness subset.
*/
//printf("%i\n", sources_len);
betweennesses.resize(num_edges);
/*Get incidence matrix, B*/
igraph_integer_t from, to;
Eigen::MatrixXf B = Eigen::MatrixXf::Zero(num_edges, num_verts);
for (int i=0; i<num_edges; i++) {
for (int s=0; s<sources_len; s++) {
for (int t=0; t<targets_len; t++) {
igraph_edge(g, i, &from, &to);
if (from>=to) { continue; }
betweennesses[i] += abs(pinv(int(from),sources[s])-pinv(int(from),targets[t])
-pinv(int(to),sources[s])+pinv(int(to),targets[t]))*igraph_sparsemat_get(
&A, from, to);
}
}
igraph_edge(g, i, &from, &to);
B(i, from) = float(VECTOR(weights_vec)[i]);
B(i, to) = float(-VECTOR(weights_vec)[i]);
}

/*
for (int i=0; i<num_edges; i++) {
std::cout << betweennesses[i] << "\n";
/*Get flow matrix, F*/
Eigen::MatrixXf F(num_edges, num_verts);
F = B * C;

Eigen::VectorXf b = Eigen::VectorXf::Zero(num_verts);
Eigen::VectorXf RW = Eigen::VectorXf::Zero(num_edges);
for (int u=0; u<num_verts; u++) {
for (int v=u+1; v<num_verts; v++) {
b[u] = 1;
b[v] = -1;
RW += (F*b).cwiseAbs();
b[u] = 0;
b[v] = 0;
}
}
*/

/*
igraph_vector_int_t sources_vec, targets_vec;
igraph_vector_t res, weights_vec;
igraph_vector_init(&res, num_edges);
igraph_vs_t sources, targets;
igraph_integer_t* sources_arr = (long long *)sources_ptr;
igraph_integer_t* targets_arr = (long long *)targets_ptr;
igraph_vector_int_init_array(&sources_vec, sources_arr, sources_len);
igraph_vector_int_init_array(&targets_vec, targets_arr, targets_len);
igraph_vs_vector(&sources, &sources_vec);
igraph_vs_vector(&targets, &targets_vec);
igraph_real_t* weights_arr = (double *)weights_ptr;
igraph_vector_init_array(&weights_vec, weights_arr, num_edges);
*/

betweennesses <<= RW;
igraph_destroy(g);
}



}
Binary file modified StructuralGT/.DS_Store
Binary file not shown.
17 changes: 12 additions & 5 deletions StructuralGT/GetWeights_3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ def unitvector(u,v):

vec = u-v # find the vector between u and v

# returns the unit vector in the direction from v to u
return vec/np.linalg.norm(vec)
if np.linalg.norm(vec)==0:
return np.array([0,]*len(u), dtype=np.float16)
else:
return vec/np.linalg.norm(vec)

def halflength(u,v):
# Inputs:
Expand All @@ -45,8 +47,8 @@ def findorthogonal(u,v):
# u, v: two coordinates (x, y) or (x, y, z)

n = unitvector(u,v) # make n a unit vector along u,v
if (np.isnan(n[0]) or np.isnan(n[1])):
n[0] , n[1] = float(0) , float(0)
#if (np.isnan(n[0]) or np.isnan(n[1])):
# n[0] , n[1] = float(0) , float(0)
hl = halflength(u,v) # find the half-length of the vector u,v
orth = np.random.randn(len(u)) # take a random vector
orth -= orth.dot(n) * n # make it orthogonal to vector u,v
Expand Down Expand Up @@ -151,12 +153,12 @@ def assignweights(ge, img_bin, weight_type=None, R_j=0, rho_dim=1):
midpt, orth = findorthogonal(pt1, pt2)
m.astype(int)
pix_width = int(lengthtoedge(m, orth, img_bin))
length = len(ge)
if(weight_type==None):
wt = pix_width/10
elif(weight_type=='VariableWidthConductance'):
#This is electrical conductance; not graph conductance.
#This conductance is based on both width and length of edge, as measured from the raw data. rho_dim is resistivity (i.e. ohm pixels)
length = len(ge)
if pix_width == 0 or length == 0:
wt = 1 #Arbitrary. Smallest possible value for a lattice graph. Using zero may cause 0 elements on the weighted Laplacian diagonal, rendering flow problems underdetermined
else:
Expand All @@ -180,6 +182,11 @@ def assignweights(ge, img_bin, weight_type=None, R_j=0, rho_dim=1):
wt = 1 #Arbitrary. Smallest possible value for a lattice graph. Using zero may cause 0 elements on the weighted Laplacian diagonal, rendering flow problems underdetermined
else:
wt = pix_width**2
elif(weight_type=='Width'):
if pix_width == 0 or length == 0:
wt = 1 #Arbitrary. Smallest possible value for a lattice graph. Using zero may cause 0 elements on the weighted Laplacian diagonal, rendering flow problems underdetermined
else:
wt = pix_width
elif(weight_type=='Length'):
wt = len(ge)
elif(weight_type=='InverseLength'):
Expand Down
36 changes: 36 additions & 0 deletions StructuralGT/average_nodal_connectivity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from StructuralGT.util import _Compute
import numpy as np
import copy
from StructuralGT import _average_nodal_connectivity_cast

class AverageNodalConnectivity(_Compute):
"""A module solely for calculating the average nodal connectivity.
Written separately because it is computationally very expensive, yet has
been shown to correlate well with material properties.REF
"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def compute(self, network):
"""Computes the average nodal connectivity."""
_copy = copy.deepcopy(network.graph)

cast = _average_nodal_connectivity_cast.PyCast(_copy._raw_pointer())

cast.average_nodal_connectivity_compute()

self._average_nodal_connectivity = cast.average_nodal_connectivity

@_Compute._computed_property
def average_nodal_connectivity(self):
r"""The nodal connectivity $\kappa(i,j)$, is the minimum number of edges
that would need to be removed to disconnect nodes $i$ and $j$. The
average nodal connectivity is the connectivity value averaged over all
pairs of nodes:
.. math::
\bar{\kappa} = 2\frac{\sum_{i \neq j}\kappa(i,j)}{n(n-1)}
"""
return self._average_nodal_connectivity
Loading

0 comments on commit c66683a

Please sign in to comment.