python concatenation in cycle partitioning - python

I understand that I can only concatenate things of similar types, but I'm really confused as to why the following are of different types.
this a part of my code:
import sys
import numpy as np
import networkx as nx
import matplotlib.pyplot as plt
import gurobipy as GRB
def solveOptCyclePartition(diGraph):
# Create the Optimization Model
try:
m = GRB.Model("ctrlModel")
linkWeights = {}
for (u,v) in sorted(diGraph.edges(data=False)):
linkWeights[(u,v)] = diGraph[u][v]['weight']
#Create variables
e = m.addVars(sorted(diGraph.edges(data=False)),lb=0.0,ub=1.0,
obj=linkWeights , name="e")
# Add the objective function
m.setObjective( (e.prod(linkWeights)), GRB.MAXIMIZE)
#Add Constraint: One output edge from each node
m.addConstrs( (e.sum(i,'*') == 1 for i in nx.nodes(diGraph)), "outDegree")
#Add Constraint: One input edge from each node
m.addConstrs( (e.sum('*',i) == 1 for i in nx.nodes(diGraph)), "inDegree")
# Compute optimal solution
m.optimize()
# Print solution
if m.status == GRB.Status.OPTIMAL:
objOptimalVal = m.getAttr('objVal')
solution = m.getAttr('x', e)
for (u,v) in sorted(diGraph.edges(data=False)):
if solution[u,v] > 0:
print('%s -> %s: %g' % (u, v, solution[u,v]))
return objOptimalVal , solution
except:
print('Error reported')
#-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
#def main():
# Network Adjacency Matrix
A = np.matrix([[0,0,0],
[1,0,0],
[1,0,1]])
G = nx.DiGraph()
G = nx.from_numpy_matrix(A)
# Check to see if connected
if nx.is_connected(G) == False:
print('The graph is not connected and has unaccessible nodes')
sys.exit(0)
# Nodes and Edge Lists
nodes = nx.nodes(G)
N = len(nodes)
edgeList = nx.to_edgelist(G)
# M Actuator nodes are selected randomly fron N nodes
M = 1;
augNodes = range(N,N+M)
actuatorNodes = sorted(list(np.random.permutation(np.arange(N))[:M]))
actuatorEdges = [(i,actuatorNodes[N-i],{'weight':1}) for i in augNodes ]
# Augmented edges lists and augmented graph
augEdgeList = (edgeList + actuatorEdges +
[(i,j,{'weight':0}) for i in nodes for j in augNodes] +
[(i,i,{'weight':0}) for i in nodes+augNodes if (i,i,{'weight':1}) not in edgeList])
Gprim = nx.DiGraph()
Gprim = nx.from_edgelist(augEdgeList,Gprim)
# Poljak Algorithm: Perform maximum weight cycle partitioning on
# augmented graph and return the optimal solution
objOptimalVal, solution = solveOptCyclePartition(Gprim)
plt.draw()
# Evaluate the solution
#if __name__ == '__main__':
# main()
I'm trying to solve cycle partitioning to find among all the partitions the one that encompasses the maximum number of edges with unit weight and satisfies the
following constraint. but i get this error:
*** TypeError: can only concatenate list (not "range") to list

Without the full stack trace it's hard to be sure exactly where this error is occurring, but I believe that it is happening on the following line:
[(i,i,{'weight':0}) for i in nodes+augNodes if (i,i,{'weight':1}) not in edgeList])
The particular section to pay attention to is nodes+augNodes.
The nodes variable contains the return value from calling networkx.nodes() which returns a list according to the documentation. augNodes on the other hand is being set to a range on the following line:
augNodes = range(N,N+M)
Since you can't append a range to a list, you're getting the TypeError that you see.

Related

How to calculate overall distances from lowest root(s) of a directed graph with networkx

If you look at this DAG (directed acyclic graph):
I want to create a dict which maps the distance from the lowest node(s) to all others nodes which is similar to the x position (height) of each node from the bottom in the rendered graph.
For that given graph it would be:
distance_nodes_map: {
0: {'base-zero', 'base-one'},
1: {'low-b', 'low-a', 'low-c'},
3: {'high-x', 'high-z', 'high-y'},
2: {'mid-r', 'mid-q', 'mid-p'},
4: {'super'}
}
I wrote an algorithm which worked for that graph above but then I've tested another graph and it didn't work anymore. I tried some algorithms and functions like shortest path or descendants_at_distance but I don't think they are really helpful as an input to calculate the distances.
My algorithm doesn't work for instance for this graph:
https://gist.github.com/timaschew/3b08a07243fa6f43773014ef5e705c96
Here is gist which contains:
a python script which reads a YAML file, the dependency/graph structure and generates a HTML with a rendered mermaid graph (I've removed my algorithm to calculate the distances in a wrong way)
both graphs which are shown here, as a YAML file
You are looking for an algorithm that draws a layered graph. There are many different algorithms, and you should choose the one that best fit your needs (for example, have a look at the following paper A Technique for Drawing Directed Graphs by Gansner et al.).
Many of those algorithms are already implemented in Graphviz (a very famous and powerful graph visualization software). Once you have installed it, it's pretty straightforward to compute the result you are looking for (G is your directed acyclic graph built using networkx.DiGraph):
from networkx.drawing.nx_agraph import graphviz_layout
def get_distance_nodes_map(G):
pos = graphviz_layout(G, prog='dot')
coor = sorted({y for k, (x, y) in pos.items()})
kmap = dict(zip(coor, range(len(coor))))
distance_nodes_map = {level: set() for level in kmap.values()}
for k, (x, y) in pos.items():
distance_nodes_map[kmap[y]].add(k)
return distance_nodes_map
Here are a couple of examples using data that you provided:
>>> from networkx import DiGraph
>>> from pprint import PrettyPrinter
>>> pp = PrettyPrinter()
>>> G1 = DiGraph()
>>> G1.add_edges_from([('super', 'high-x'), ('high-x', 'mid-p'),
... ('mid-p', 'low-b'), ('mid-p', 'low-c'),
... ('low-c', 'base-zero'), ('low-c', 'base-one'),
... ('high-y', 'mid-p'), ('high-y', 'base-zero'),
... ('high-z', 'base-one'), ('high-z', 'mid-r'),
... ('high-z', 'mid-q'), ('mid-q', 'low-a'),
... ('low-a', 'base-one')])
>>> pp.pprint(get_distance_nodes_map(G1))
{0: {'base-one', 'base-zero'},
1: {'low-a', 'low-b', 'low-c'},
2: {'mid-p', 'mid-r', 'mid-q'},
3: {'high-y', 'high-x', 'high-z'},
4: {'super'}}
>>> G2 = DiGraph()
>>> G2.add_edges_from([('n10', 'n11'), ('n11', 'n12'), ('n12', 'n13'),
... ('n13', 'n14'), ('n20', 'n14'), ('n20', 'n21'),
... ('n21', 'n22'), ('n22', 'n23'), ('n30', 'n23'),
... ('n30', 'n31'), ('n31', 'n32')])
>>> pp.pprint(get_distance_nodes_map(G2))
{0: {'n32'},
1: {'n31', 'n23'},
2: {'n30', 'n22'},
3: {'n21', 'n14'},
4: {'n13', 'n20'},
5: {'n12'},
6: {'n11'},
7: {'n10'}}
Untested pseudo-code because my lunch break is nearly over:
You have a multi-root tree, with one chosen principal root.
For each root, create a subgraph consisting of all reachable nodes for that root.
Starting with the principal root (root a), compute the distance/shortest path length to the root for all nodes in the corresponding subgraph A.
Find all subgraphs that share at least one node with the principal subgraph, and select the subgraph (subgraph B) that has the node (node x) with the smallest distance to the principal root.
Compute the distance to the root b for all nodes in subgraph B. Add the distance d(node x, root a). Subtract the distance d(node x, root b).
Create the union of subgraph A and B. Repeat steps 3-5 until no roots remain.
Subtract the maximum distance & reverse the sign such that principal root has the largest distance/order value.
Edit:
My pseudocode works (*). I blame user error. ;-)
#!/usr/bin/env python
"""
https://stackoverflow.com/q/66584661/
"""
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
def hierarchical_layout(graph):
longest_path = nx.algorithms.dag.dag_longest_path(graph)
principal_root = longest_path[0]
roots = [node for node, degree in list(graph.in_degree) if degree==0]
subgraphs = {root : create_subgraph(graph, root) for root in roots}
# Starting with the principal root (root a), compute the
# longest path length to the root for all nodes in the
# corresponding subgraph A.
node_to_level = single_source_longest_dag_path_length(subgraphs[principal_root], principal_root)
explored = subgraphs[principal_root]
del subgraphs[principal_root]
while len(explored) < len(graph):
# Find all subgraphs that share at least one node with the
# principal subgraph, and select the subgraph (subgraph B) that
# has the node (node x) with the smallest distance to the
# principal root.
minimum_cost = np.inf
minimum_cost_node = None
minimum_cost_root = None
for root, subgraph in subgraphs.items():
for node in subgraph.nodes:
if node in node_to_level:
if node_to_level[node] < minimum_cost:
minimum_cost = node_to_level[node]
minimum_cost_node = node
minimum_cost_root = root
assert minimum_cost_node, "Could not find a connected subgraph."
# Compute the distance to the root b for all nodes in subgraph
# B. Add the distance d(node x, root a). Subtract the distance
# d(node x, root b).
path_lengths = [len(path) for path in nx.all_simple_paths(subgraphs[minimum_cost_root], minimum_cost_root, minimum_cost_node)]
offset = np.max(path_lengths) - 1
for node, distance in single_source_longest_dag_path_length(subgraphs[minimum_cost_root], minimum_cost_root).items():
if not node in node_to_level:
node_to_level[node] = distance + minimum_cost - offset
# Create the union of subgraph A and B.
explored = nx.compose(explored, subgraphs[minimum_cost_root])
del subgraphs[minimum_cost_root]
return node_to_level
def create_subgraph(G, node):
# https://stackoverflow.com/a/45678930/2912349
nodes = nx.single_source_shortest_path(G,node).keys()
return G.subgraph(nodes)
def single_source_longest_dag_path_length(graph, s):
# from AlaskaJoslin's comment to https://stackoverflow.com/a/60978007/2912349
dist = dict.fromkeys(graph.nodes, -float('inf'))
dist[s] = 0
topo_order = nx.topological_sort(graph)
for n in topo_order:
for s in graph.successors(n):
if dist[s] < dist[n] + 1:
dist[s] = dist[n] + 1
return dist
if __name__ == '__main__':
# edge_list = [
# ("n10", "n11"),
# ("n11", "n12"),
# ("n12", "n13"),
# ("n13", "n14"),
# ("n20", "n21"),
# ("n20", "n14"),
# ("n21", "n22"),
# ("n22", "n23"),
# ("n30", "n23"),
# ("n30", "n31"),
# ("n31", "n32"),
# ]
edge_list = [
("low-a", "base-one"),
("low-c", "base-zero"),
("low-c", "base-one"),
("mid-p", "low-b"),
("mid-p", "low-c"),
("mid-q", "low-a"),
("high-x", "mid-p"),
("high-y", "mid-p"),
("high-y", "base-zero"),
("high-z", "mid-q"),
("high-z", "mid-r"),
("high-z", "base-one"),
("super", "high-x"),
]
graph = nx.DiGraph()
graph.add_edges_from(edge_list)
node_to_level = hierarchical_layout(graph)
# reverse output format
distance_nodes_map = dict()
max_distance = np.max(list(node_to_level.values()))
for node, distance in node_to_level.items():
reversed_distance = max_distance - distance
if reversed_distance in distance_nodes_map:
distance_nodes_map[reversed_distance].add(node)
else:
distance_nodes_map[reversed_distance] = set([node])
# print(distance_nodes_map)
for ii, nodes in sorted(distance_nodes_map.items())[::-1]:
print(f"{ii} : {nodes}")
Yields:
# 4 : {'super'}
# 3 : {'high-x', 'high-y', 'high-z'}
# 2 : {'mid-p', 'mid-r', 'mid-q'}
# 1 : {'low-a', 'low-b', 'low-c'}
# 0 : {'base-one', 'base-zero'}
(*) "subtract distance d(node x, root b)" naturally implied the longest path length between node x and root b. Obviously.

Duplicate a nurbs curve along a curve to perform a loft

I have put together a script that creates the following; A start and end curve shape and a linear curve between.
Now what I'm wanting to do is to duplicate and transform the starting curve shape along the path (as depicted by the image), and perform a loft (preferred as would prob give the cleanest result), or alternatively, loft between the two existing curve shapes, and then deform the loft geometry to the curve. For the latter I have tried;
pm.deformer((loftShape, path), type='curveWarp', name='curveWarp#')
without success. The locators are points calculated to generate the correct
bezier curve given different distances/ starting angles. I would have thought the hard work was done, but I'm having trouble with this seemingly simple last step.
Below is a method I put together to query curve info:
def getClosestCV(x, curves, tolerance=0.0):
'''Find the closest control vertex between the given vertices, CVs, or objects and each of the given curves.
:Parameters:
x (str)(obj)(list) = Polygon vertices, control vertices, objects, or points given as (x,y,z) tuples.
curves (str)(obj)(list) = The reference object in which to find the closest CV for each vertex in the list of given vertices.
tolerance (int)(float) = Maximum search distance. Default is 0.0, which turns off the tolerance flag.
:Return:
(dict) closest vertex/cv pairs (one pair for each given curve) ex. {<vertex from set1>:<vertex from set2>}.
ex. vertices = Init.getComponents(objects, 'vertices')
closestVerts = getClosestCV(curve0, curves)
'''
pm.undoInfo(openChunk=True)
x = pm.ls(x, flatten=1) #assure x arg is a list (if given as str or single object).
npcNode = pm.ls(pm.createNode('nearestPointOnCurve'))[0] #create a nearestPointOnCurve node.
result={}
for curve in pm.ls(curves):
pm.connectAttr(curve.worldSpace, npcNode.inputCurve, force=1) #Connect the curve's worldSpace geometry to the npc node.
for i in x:
if not isinstance(i, (tuple, list, set)):
pos = pm.pointPosition(i)
else:
pos = i
pm.setAttr(npcNode.inPosition, pos)
distance = Init.getDistanceBetweenTwoPoints(pos, pm.getAttr(npcNode.position))
p = pm.getAttr(npcNode.parameter)
if not tolerance:
result[i] = p
elif distance < tolerance:
result[i] = p
pm.delete(npcNode)
pm.undoInfo(closeChunk=True)
return result
def getCvInfo(c, returnType='cv', filter_=[]):
'''Get a dict containing CV's of the given curve(s) and their corresponding point positions (based on Maya's pointOnCurve command).
:Parameters:
- c (str)(obj)(list) = Curves or CVs to get CV info from.
- returnType (str) = The desired returned values. Default is 'cv'.
valid values are:
'cv' = Return a list of all CV's for the given curves.
'count' = Return an integer representing the total number of cvs for each of the curves given.
'parameter', 'position', 'index', 'localPosition', 'tangent', 'normalizedTangent', 'normal', 'normalizedNormal', 'curvatureRadius', 'curvatureCenter'
= Return a dict with CV's as keys and the returnType as their corresponding values.
ex. {NurbsCurveCV(u'polyToCurveShape7.cv[5]'): [-12.186520865542082, 15.260936896515751, -369.6159740743584]}
- filter_ (str)(obj)(list) = Value(s) to filter for in the returned results.
:Return:
(dict)(list)(int) dependant on returnType.
ex. cv_tan = getCvInfo(curve.cv[0:2],'tangent') #get CV tangents for cvs 0-2.
ex. cvParam = getCvInfo(curve, 'parameters') #get the curves CVs and their corresponding U parameter values.
ex. filtered = getCvInfo(<curve>, 'normal', <normal>) #filter results for those that match the given value.
'''
result={}
for curve in pm.ls(c):
if '.cv' in str(curve): #if CV given.
cvs = curve
curve = pm.listRelatives(cvs, parent=1)
else: #if curve(s) given
cvs = curve.cv
parameters = Init.getClosestCV(cvs, curve) #use getClosestCV to get the parameter location for each of the curves CVs.
for cv, p in parameters.items():
if returnType is 'position': # Get cv position
v = pm.pointOnCurve(curve, parameter=p, position=True)
elif returnType is 'localPosition':
v = pm.getAttr(cv) # local cv position
elif returnType is 'tangent': # Get cv tangent
v = pm.pointOnCurve(curve, parameter=p, tangent=True)
elif returnType is 'normalizedTangent':
v = pm.pointOnCurve(curve, parameter=p, normalizedTangent=True)
elif returnType is 'normal': # Get cv normal
v = pm.pointOnCurve(curve, parameter=p, normal=True)
elif returnType is 'normalizedNormal':
v = pm.pointOnCurve(curve, parameter=p, normalizedNormal=True) #Returns the (x,y,z) normalized normal of curve1 at parameter 0.5.
elif returnType is 'curvatureRadius': # Get cv curvature
v = pm.pointOnCurve(curve, parameter=p, curvatureRadius=True) #Returns the curvature radius of curve1 at parameter 0.5.
elif returnType is 'curvatureCenter':
v = pm.pointOnCurve(curve, parameter=p, curvatureCenter=True)
elif returnType is 'parameter': # Return the CVs parameter.
v = p
elif returnType is 'count': # total number of cv's for the curve.
result[curve] = len(Init.getCvInfo(curve))
break
elif returnType is 'index': # index of the cv
s = str(cv)
v = int(s[s.index('[')+1:s.index(']')])
else:
v = None
result[cv] = v
if returnType is 'cv':
result = result.keys()
if filter_:
if not isinstance(filter_, (tuple, set, list)):
filter_ = list(filter_)
try:
result = {k:v for k,v in result.items() if any((v in filter_, v==filter_))}
except AttributeError:
result = [i for i in result if any((i in filter_, i==filter_))]
if len(result) is 1:
try:
result = result.values()[0]
except AttributeError, TypeError:
result = result[0]
return result
I ultimately decided to use the built-in MASH plugin for this. Perhaps this will be of help to someone in the future.
def duplicateAlongCurve(path, start, count=6, geometry='Instancer'):
'''Duplicate objects along a given curve using MASH.
:Parameters:
path (obj) = The curve to use as a path.
start () = Starting object.
count (int) = The number of duplicated objects. (point count on the MASH network)
geometry (str) = Particle instancer or mesh instancer (Repro node). (valid: 'Mesh' (default), 'Instancer')
:Return:
(list) The duplicated objects in order of start to end.
'''
pm.undoInfo(openChunk=1)
#create a MASH network
import MASH.api as mapi
mashNW = mapi.Network()
mashNW.MTcreateNetwork(start, geometry=geometry, hideOnCreate=False) #MASH_tools module (derived from 'createNetwork')
curveNode = pm.ls(mashNW.addNode('MASH_Curve').name)[0]
pm.connectAttr(path.worldSpace[0], curveNode.inCurves[0], force=1)
pm.setAttr(curveNode.stopAtEnd, 1) #0=off, 1=on
pm.setAttr(curveNode.clipStart, 0)
pm.setAttr(curveNode.clipEnd, 1)
pm.setAttr(curveNode.timeStep, 1)
pm.setAttr(curveNode.curveLengthAffectsSpeed, 1)
distNode = pm.ls(mashNW.distribute)[0]
pm.setAttr(distNode.pointCount, count)
pm.setAttr(distNode.amplitudeX, 0)
instNode = pm.ls(mashNW.instancer)[0]
baked_curves = mashNW.MTbakeInstancer(instNode) #MASH_tools module (derived from 'MASHbakeInstancer')
result=[start]
for curve in reversed(baked_curves):
result.append(curve)
pm.delete(mashNW.waiter.name()) #delete the MASH network.
pm.undoInfo(closeChunk=1)
return result

Pycharm debugger skips breakpoints after specific line of code - why?

I'm new to pycharm and so far my impression of the debugger is that it's marverlous! However, it behaves weird in my code and I cannot figure out what is going wrong.
If I set a breakpoint to these lines of code and then press "step over" or "step into my code" it runs until the end ignoring all other upcoming breakpoints. Any idea what I do wrong? Breakpoints before that line work perfectly fine.
for ind, fit in zip(pop, fitnesses):
ind.fitness.values = fit
my code
You need to pip install deap, efel and brian2 for the code to run.
# DEAP
# https://github.com/DEAP/deap/tree/54b83e2cc7a73be7657cb64b01033a31584b891d
# import array
import matplotlib.pyplot as plt
import pandas as pd
from scipy.io import loadmat
import random, numpy, os, efel, scipy, math, time, array, json
from deap import algorithms, base, creator, tools, benchmarks
from deap.benchmarks.tools import diversity, convergence # , hypervolume
from machine_hh_model_v02 import brian_hh_model
# parallel processing
# from scoop import futures
parallel_processing = "no"
# Starting values
channels = {"ENa": 65,
"EK": -90,
"El": -70,
"ECa": 120,
"gNa": 0.05,
"gK": 0.005,
"gL": 1e-4,
"gM": 8e-5,
"gCa": 1e-5}
# Boundaries
bounds = {"ENa": [50, 70],
"EK": [-100, -80],
"El": [-50, -100],
"ECa": [100, 120],
"gNa": [0, 1],
"gK": [0, 1],
"gL": [0, 1],
"gM": [0, 1],
"gCa": [0, 1]}
low, up = [x[0] for x in bounds.values()], [x[1] for x in bounds.values()]
# Set parameters
ext = 2.5 # external current stimulation [nA]
num_gen = 2 # number of generations
num_parents = 40 # number of parents
num_params = len(channels) # number of parameters to optimize
prob_crossover = 0.9 # probability that crossover takes place
# How to generate individuals
def initIndividual(container, sigma):
return container(random.gauss(x, sigma) for x in channels.values())
# CREATOR
# http://deap.readthedocs.io/en/master/tutorials/basic/part1.html
# The create() function takes at least two arguments, a name for the newly created class and a base class. Any
# subsequent argument becomes an attribute of the class. Neg. weights relate to minizing, pos. weight to maximizing
# problems.
# -- define fitness problem (which params are min./max. problems with which weight)
creator.create("FitnessMulti", base.Fitness, weights=tuple(numpy.ones(num_params) * -1))
# Next we will create the class Individual, which will inherit the class list and contain our previously defined
# FitnessMulti class in its fitness attribute. Note that upon creation all our defined classes will be part of the
# creator container and can be called directly.
# -- associate fitness problem to individuals, that are going to be created
creator.create("Individual", list, fitness=creator.FitnessMulti)
# TOOLBOX
# http://deap.readthedocs.io/en/master/examples/ga_onemax.html
# http://deap.readthedocs.io/en/master/api/tools.html#module-deap.tools
# All the objects we will use on our way, an individual, the population, as well as all functions, operators, and
# arguments will be stored in a DEAP container called Toolbox. It contains two methods for adding and removing content,
# register() and unregister().
toolbox = base.Toolbox()
# The newly introduced register() method takes at least two arguments: an alias and a function. Toolbox.attr_bool(),
# when called, will draw a random integer between -100 and 100. Toolbox.attr_float(), when called, will draw a random
# floating point number.
# -- how to generate values for each individual
# toolbox.register("attr_float", random.uniform, -100, 100)
# toolbox.register("attr_float", lambda: [random.gauss(x, 5) for x in channels.values()])
# Our individuals will be generated using the function initRepeat(). Its first argument is a container class, the
# Individual one we defined in the previous section. This container will be filled using the method attr_float(),
# provided as second argument, and will contain 10 integers, as specified using the third argument. When called, the
# individual() method will thus return an individual initialized with what would be returned by calling the attr_float()
# method 100 times.
# -- how and how many individuals to create
# toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, num_params)
toolbox.register("individual", initIndividual, creator.Individual, sigma=1)
# Finally, the population() method uses the same paradigm.
# -- how and how many parents to create
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
# LOAD EXPERIMENTAL DATA
# set path
# mainpath = r'C:\OwnCloud\Masterarbeit' # mainpath
# pathfit = os.path.join(mainpath, r'fitness_params') # fitness files
# os.chdir(os.path.join(mainpath, pathfit)) # change directory
# load fitness file
# xl = pd.ExcelFile('fitness.xlsx') # load excel file
# xl_mean = xl.parse("median") # load sheet 'mean' containing mean/median values
# xl_var = xl.parse("quartile-to-median distance") # load sheet 'std containing std/quantiles values
xl_mean = pd.read_json("median")
xl_var = pd.read_json("distance")
########################
############ SOMETHING IS WRONG HERE
########################
# EFEL: median
def efel_stats(features):
# get latency of first spike
if features['peak_time'].any():
features['first_peak_time'] = features['peak_time'][0]
del features['peak_time']
# get median
for key, val in features.items():
if val is None or numpy.isnan(val) or not val:
features[key] = 9999
else:
features[key] = scipy.nanmedian(val)
# get median
# if features['Spikecount'] == 0:
# for key, val in f#eatures.items( ):
# features[key] = 9999
# else:
# for key, val in features.items():
# features[key] = scipy.nanmedian(val)
return features
# ERROR FUNCTION
# The returned value must be iterable and of a length equal to the number of objectives (weights).
def error_function(external_current, indi, xl_mean=xl_mean, xl_var=xl_var):
# output variable
allerrors = []
# BRIAN: run model
stim_start, stim_duration = 500, 1000
voltage, time = brian_hh_model(1, 0, stim_start, stim_duration,
ENa=indi[0], EK=indi[1], El=indi[2], ECa=indi[3], gNa=indi[4], gK=indi[5],
gL=indi[6], gM=indi[7], gCa=indi[8])
# EFEL: extract features and get median
feature_names = ['Spikecount', 'peak_voltage', 'min_AHP_values', 'AP_begin_voltage', 'spike_half_width',
'voltage_base', 'steady_state_voltage_stimend',
'AP_begin_time', 'peak_time']
trace = {'T': time, 'V': voltage, 'stim_start': [stim_start], 'stim_end': [stim_start + stim_duration]}
features = efel.getFeatureValues([trace], feature_names)[0]
features = efel_stats(features)
# # ERROR FUNCTION: get error value
for feature, value in features.items():
# median for one external current (experimental data)
experiment_vals = xl_mean.loc[xl_mean['stimulus'] == external_current, feature].values[0]
error = float(abs(value - experiment_vals))
# my model can produce the same, less or more #spikes, peakvoltage, ...
if value == experiment_vals:
error = 0.
elif value < experiment_vals:
error = error / float(xl_var.loc[xl_var['stimulus'] == external_current, feature].values[0][0])
elif value > experiment_vals:
error = error / float(xl_var.loc[xl_var['stimulus'] == external_current, feature].values[0][1])
# append error value of this feature
allerrors.append(error)
return allerrors
# GENETIC OPERATORS
# Within DEAP there are two ways of using operators. We can 1) simply call a function from the tools module or
# 2) register it with its arguments in a toolbox, as we have already seen for our initialization methods. The second
# option allows us to to easily switch between the operators if desired.
# see http://deap.readthedocs.io/en/master/api/tools.html#module-deap.tools
# Crossover
# Register the crossover function to the toolbox
# tools.cxOnePoint --> one point crossover
# tools.cxTwoPoint --> two-point crossover
toolbox.register("mate", tools.cxSimulatedBinary, eta=20.0)
# Mutation
# Register the mutation function to the toolbox
# tools.mutGaussian --> applies a gaussian mutation of mean mu and standard deviation sigma on the input individual. The indpb argument is the probability of each attribute to be mutated.
# tools.mutPolynomialBounded --> Polynomial mutation as implemented in original NSGA-II algorithm in C by Deb.
# toolbox.register("mutate", tools.mutPolynomialBounded, eta=20, low=low, up=up, indpb=1.0/num_params)
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=2, indpb=0.9)
# Selection
# tools.sortNondominated(individuals, k, first_front_only=False) --> Sort the first k individuals into different nondomination levels using the “Fast Nondominated Sorting Approach” proposed by Deb et al., see [Deb2002].
# tools.sortLogNondominated(individuals, k, first_front_only=False) --> Sort individuals in pareto non-dominated fronts using the Generalized Reduced Run-Time Complexity Non-Dominated Sorting Algorithm presented by Fortin et al. (2013).
toolbox.register("select", tools.selNSGA2)
# Evaluation
# Register error function in the toolbox.
# The evaluation will be performed by calling the alias "evaluate".
# toolbox.register("evaluate", error_function, ext, model_vals)
# ALGORITHM
# Now that everything is ready, we can start to write our own algorithm. It is usually done in a main function.
if parallel_processing=="yes":
toolbox.register("map", futures.map)
def main():
# register statistics to the toolbox to maintain stats of the evolution
stats = tools.Statistics(lambda ind: ind.fitness.values)
stats.register("avg", numpy.mean, axis=0)
stats.register("std", numpy.std, axis=0)
stats.register("min", numpy.min, axis=0)
stats.register("max", numpy.max, axis=0)
logbook = tools.Logbook()
logbook.header = "gen", "evals", "min"
###
### NSGA-II algorithm as in "Deb 2002: A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II"
### https://github.com/DEAP/deap/blob/master/examples/ga/nsga2.py
###
# create random parent population pop
pop = toolbox.population(n=num_parents)
# register error function
toolbox.register("evaluate", error_function, ext)
# evaluate parent population
invalid_ind = [ind for ind in pop if not ind.fitness.valid]
fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit
# assign crowding distance to the individuals, no actual selection is done
pop = toolbox.select(pop, len(pop))
# print logbook
record = stats.compile(pop)
logbook.record(gen=0, evals=len(invalid_ind), **record)
print(logbook.stream)
# print(record)
# Begin the generational process
for gen in range(1, num_gen):
# increase the variance in my population
offspring = tools.selTournamentDCD(pop, len(pop))
# I have no idea why
offspring = [toolbox.clone(ind) for ind in offspring]
# crossover
for ind1, ind2 in zip(offspring[::2], offspring[1::2]):
if random.random() <= prob_crossover:
toolbox.mate(ind1, ind2)
# mutation
toolbox.mutate(ind1)
toolbox.mutate(ind2)
del ind1.fitness.values, ind2.fitness.values
# Fitness
invalid_ind = [ind for ind in offspring if not ind.fitness.valid]
fitnesses = toolbox.map(toolbox.evaluate, invalid_ind)
for ind, fit in zip(invalid_ind, fitnesses):
ind.fitness.values = fit
# Select the next generation population
pop = toolbox.select(pop + offspring, num_parents)
record = stats.compile(pop)
logbook.record(gen=gen, evals=len(invalid_ind), **record)
print(logbook.stream)
# print(record)
# print("Final population hypervolume is %f" % hypervolume(pop, [11.0, 11.0]))
return pop, logbook
# create pareto front (all non-dominated individuals that ever lived)
# pareto = tools.ParetoFront()
if __name__ == "__main__":
pop, logbook = main()
print(logbook)
print("POPULATION", pop)
for indi in pop:
stim_start, stim_duration = 500, 1000
voltage, time = brian_hh_model(1, 1, stim_start, stim_duration,
ENa=indi[0], EK=indi[1], El=indi[2], ECa=indi[3], gNa=indi[4], gK=indi[5],
gL=indi[6], gM=indi[7], gCa=indi[8])
print("INDIVIDUAL ", indi)
machine_hh_model_v02
# brian user guide
# http://brian2.readthedocs.io/en/2.0rc/user/index.html
from brian2 import *
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_style()
def brian_hh_model(input_current, plotflag, stim_start, stim_duration, **parameter):
# C++ standalone mode
# At the beginning of the script, i.e. after the import statements, add:
# set_device('cpp_standalone', build_on_run=False)
# ********
# Handling units
# You can generate a physical quantity by multiplying a scalar or vector value with its physical unit:
# tau = 20*ms --> 20. ms
# rates = [10, 20, 30] * Hz --> [ 10. 20. 30.] Hz
# Most Brian functions will also complain about non-specified or incorrect units:
# G = NeuronGroup(10, 'dv/dt = -v/tau: volt', dt=0.5) --> "dt" has wrong dimensions, dimensions were (1) (s)
# Directly get the unitless value of a state variable by appending an underscore to the name
# print(rates_) --> [ 10. 20. 30.]
# ********
# Default parameters
# Set default parameters
default = {"ENa": 65, "EK": -90, "El": -70, "ECa": 120, "gNa": 0.05, "gK": 0.005, "gL": 1e-4, "gM": 8e-5,
"gCa": 1e-5}
# Use default parameter, if not defined as an input parameter
for key, val in default.items():
if key not in parameter:
parameter[key] = val
# Parameters
# Extract parameters that were given as an input (as a dictionary).
d = 96 * umetre # 79.8 umetre
area = d*d*3.141
Cm = 1*ufarad*cm**-2 * area # 1 ufarad
ENa = parameter["ENa"]*mV
EK = parameter["EK"]*mV
El = parameter["El"]*mV
ECa = parameter["ECa"]*mV
g_na = parameter["gNa"]*siemens*cm**-2 * area # 0.05 siemens
g_kd = parameter["gK"]*siemens*cm**-2 * area # 0.005 siemens
gl = parameter["gL"]*siemens*cm**-2 * area # 1e-4 siemens
gm = parameter["gM"]*siemens*cm**-2 * area # 8e-5 siemens
gCa = parameter["gCa"]*siemens*cm**-2 * area
tauMax = 4000 * ms
VT = -63*mV
# Equations
# Define both state variables and continuous-updates on these variables through differential equations. An Equation is
# a set of single lines in a string:
# 1. dx/dt = f : unit (differential equation)
# 2. x = f : unit (subexpression)
# 3. x : unit (parameter)
# There are three special units, "1" -> floating point number, "boolean" and "integer"
# Some special variables are defined: t, dt (time) and xi (white noise). Some other variable names (e.g. _pre) are
# forbidden. Flags -- dx/dt = f : unit (constant), parameter will not be changed during a run.
# The model
eqs = Equations('''
Im = gm * p * (v-EK) : amp
Ica = gCa * q*q * r * (v-ECa) : amp
dv/dt = (gl*(El-v) - g_na*(m*m*m)*h*(v-ENa) - g_kd*(n*n*n*n)*(v-EK) - Im - Ica + I)/Cm : volt
dm/dt = 0.32*(mV**-1)*(13.*mV-v+VT)/
(exp((13.*mV-v+VT)/(4.*mV))-1.)/ms*(1-m)-0.28*(mV**-1)*(v-VT-40.*mV)/
(exp((v-VT-40.*mV)/(5.*mV))-1.)/ms*m : 1
dn/dt = 0.032*(mV**-1)*(15.*mV-v+VT)/
(exp((15.*mV-v+VT)/(5.*mV))-1.)/ms*(1.-n)-.5*exp((10.*mV-v+VT)/(40.*mV))/ms*n : 1
dh/dt = 0.128*exp((17.*mV-v+VT)/(18.*mV))/ms*(1.-h)-4./(1+exp((40.*mV-v+VT)/(5.*mV)))/ms*h : 1
# K+ current
dp/dt = (1/(1+exp(-(v-VT+35.*mV)/(10.*mV))) - p) / (tauMax / (3.3 * exp((v - VT + 35.*mV)/(20.*mV) + exp(-(v - VT + 35.*mV)/(20.*mV))))) : 1
# Ca2+ current
dq/dt = 0.055*(mV**-1) * (-27.*mV - v) / (exp((-27.*mV - v) / (3.8*mV)) - 1.)/ms * (1.-q) - 0.94*exp((-75.*mV - v) / (17.*mV))/ms*q : 1
dr/dt = 0.000457 * exp((-13.*mV - v) / (50.*mV))/ms * (1.-r) - 0.0065 / (1. + exp((-15.*mV - v) / (28.*mV)))/ms*r : 1
I : amp
''')
# NeuronGroup
# The core of every simulation is a NeuronGroup, a group of neurons that share the same equations defining their
# properties. Minimum inputs are "number of neurons" and "model description in the form of equations". Threshold and
# refractoriness are only used for emiting spikes. To make a neuron non-excitable for a certain time period after a
# spike, the refractory keyword can be used.
# G = NeuronGroup(10, 'dv/dt = -v/tau : volt', threshold='v > -50*mV', reset='v = -70*mV', refractory=5*ms)
# Dictionary
# You can set multiple initial values at once using a dictionary and the Group.get_states() and Group.set_states()
# methods.
# initial_values = {'v': 1, 'tau': 10*ms}
# group.set_states(initial_values)
# group.v[:] --> 1)
# states = group.get_states()
# states['v'] --> 1)
group = NeuronGroup(1, eqs, method="exponential_euler")
group.v = El
group.I = 0*nA
# Recording
# Recording variables during a simulation is done with “monitor” objects. Specifically, spikes are recorded with
# SpikeMonitor, the time evolution of variables with StateMonitor and the firing rate of a population of neurons with
# PopulationRateMonitor. You can get all the stored values in a monitor with the Group.get_states().
# In this example, we record two variables v and u, and record from indices 0, 10 and 100 --> three neurons.
# G = NeuronGroup(...)
# M = StateMonitor(G, ('v', 'u'), record=[0, 10, 100])
# M.v[1] will return the values for the second recorded neuron which is the neuron with the index 10.
M = StateMonitor(group, 'v', record=0)
# Run the model
# The command run(100*ms) runs the simulation for 100 ms.
run(stim_start*ms)
group.I[0] = input_current*nA # current injection at one end
run(stim_duration*ms)
group.I = 0*nA
run(stim_start*ms)
# C++ standalone mode
# After the last run() call, call device.build() explicitly:
# device.build(directory='output', compile=True, run=True, debug=False)
# Timing
# profiling_summary(show=5) -- show the 5 objects that took the longest
# profiling_summary(show=2)
# Output
time = M.t/ms
voltage = M.v[0]/mV
# plot output
if plotflag:
plt.plot(time, voltage)
xlabel('Time [ms]')
ylabel('Membrane potential [mV]')
plt.show()
# For multiple calls
# device.reinit()
# device.activate()
return (voltage, time)
#brian_hh_model(1, 1, 500, 1000, ENa=65, EK=-90, El=-70, ECa=120, gNa=0.05, gK=0.005, gL=1e-4, gM=8e-5, gCa=1e-5)
Thanks a lot in advance!!

How to visualize the cluster result as a graph with different node color based on its cluster?

i have a geodesic distance of graph data in .csv format
i want to reduce it into 2D using Multidimensional Scaling (MDS) and cluster it using Kmedoids
This is my code:
# coding: utf-8
import numpy as np
import csv
from sklearn import manifold
from sklearn.metrics.pairwise import pairwise_distances
import kmedoidss
rawdata = csv.reader(open('data.csv', 'r').readlines()[1:])
# Process the data into a 2D array, omitting the header row
data, labels = [], []
for row in rawdata:
labels.append(row[1])
data.append([int(i) for i in row[1:]])
#print data
# Now run very basic MDS
# Documentation here: http://scikit-learn.org/dev/modules/generated/sklearn.manifold.MDS.html#sklearn.manifold.MDS
mds = manifold.MDS(n_components=2, dissimilarity="precomputed")
pos = mds.fit_transform(data)
# distance matrix
D = pairwise_distances(pos, metric='euclidean')
# split into c clusters
M, C = kmedoidss.kMedoids(D, 3)
print ('Data awal : ')
for index, point_idx in enumerate(pos, 1):
print(index, point_idx)
print ('\n medoids:' )
for point_idx in M:
print('{} index ke - {} '.format (pos[point_idx], point_idx+1))
print('')
print('clustering result:')
for label in C:
for point_idx in C[label]:
print('cluster- {}:{} index- {}'.format(label, pos[point_idx], point_idx+1))
kmedoidss.py
import numpy as np
import random
def kMedoids(D, k, tmax=100):
# determine dimensions of distance matrix D
m, n = D.shape
# randomly initialize an array of k medoid indices
M = np.sort(np.random.choice(n, k))
# create a copy of the array of medoid indices
Mnew = np.copy(M)
# initialize a dictionary to represent clusters
C = {}
for t in xrange(tmax):
# determine clusters, i. e. arrays of data indices
J = np.argmin(D[:,M], axis=1)
for kappa in range(k):
C[kappa] = np.where(J==kappa)[0]
# update cluster medoids
for kappa in range(k):
J = np.mean(D[np.ix_(C[kappa],C[kappa])],axis=1)
j = np.argmin(J)
Mnew[kappa] = C[kappa][j]
np.sort(Mnew)
# check for convergence
if np.array_equal(M, Mnew):
break
M = np.copy(Mnew)
else:
# final update of cluster memberships
J = np.argmin(D[:,M], axis=1)
for kappa in range(k):
C[kappa] = np.where(J==kappa)[0]
# return results
return M, C
how to visualize the cluster result as a graph with different node color based on its cluster?
You don't need MDS to run kMedoids - just run it on the original distance matrix (kMedoids can also be made to work on a similarity matrix by switching min for max).
Use MDS only for plotting.
The usual approach for visualization is to use a loop over clusters, and plot each cluster in a different color; or to use a color predicate. There are many examples in the scipy documentation.
http://scikit-learn.org/stable/auto_examples/cluster/plot_cluster_comparison.html
colors = np.array([x for x in 'bgrcmykbgrcmykbgrcmykbgrcmyk'])
colors = np.hstack([colors] * 20)
y_pred = labels.astype(np.int)
plt.scatter(X[:, 0], X[:, 1], color=colors[y_pred].tolist(), s=10)
where X is your pos variable (2d mds result) and labels are an integer cluster number for every point. Since you don't have your data in thid "labels" layout, consider using a loop instead:
for label, pts in C.items():
plt.scatter(pos[pts, 0], pos[pts, 1], color=colors[label])
plt.show()

percolation (network attack) by degree

I am trying to implement a function to remove some fraction of high degree nodes based on initial lcc (1% in every step) until the lcc will be destructed completely (size zero). to test my code I am using DBLP coauthor ship network which is already shown that the lcc of the this graph will be destructed after removing 1.5% of high degree nodes. However my code fail to that,.
one thing that I am not sure is that when we mask some nodes and then use GraphView(G,vfilt=mask), it does not take of isolated vertices after masking. So I had to check for them manually. thanks for any hint!
def _breake_lcc(self, G, remove_ratio):
'''
G:graph
remove_ratio = ratio for removing high degree nodes
'''
#lcc of complete graph
l = gt.label_largest_component(G)
lcc_graph = gt.GraphView(G,vfilt=l)
lcc_org_size = float(lcc_graph.num_vertices())
lcc_size = [lcc_org_size/lcc_org_size]
#print 'lcc_size_zero: ', lcc_graph.num_vertices()
#extract the vertices inside lcc
node_ind = np.where(l.a==1)[0]
#compute the degree of all nodes
degree = lcc_graph.degree_property_map('total')
#extract the degreee of nodes in the lcc
lcc_vertex_degree = degree.a[node_ind]
#sort the nodes based on degree
sorted_idx = np.argsort(lcc_vertex_degree)[::-1]
sorted_vertex = set(node_ind[sorted_idx])
#number of nodes
n = len(node_ind)
#number of top x% degree nodes to be removed -ratio to size
remove_size = int((n*remove_ratio)/100.)
print 'remove size',remove_size
step = 1
#extract top x% vertices to remove
nodes_to_be_removed = list(sorted_vertex)[:remove_size]
#removed the above vertices from the list of valid vertices
valid_vertices = list(sorted_vertex - set(nodes_to_be_removed))
#continue until there is no other node to remove
while len(valid_vertices)>0:
print step * remove_ratio
# print len(valid_vertices)
#filter out those nodes from lcc.
#THIS IS AUTOMATICALLY UPDATE LCC_GRAPH
l.a[np.array(nodes_to_be_removed)] = False
#I REALIZED GRAPHVIEW DOES NOT TAKE CARE OF ISOLATED NODE AUTOMATICALLY
#SO I CHECK FOR THEM MANUALLY
ind_remove = [int(i) for i in lcc_graph.vertices() if i.out_degree()==0]
if len(ind_remove)>0:
print len(ind_remove)
mask = lcc_graph.new_vertex_property("bool")
#mask.a[::] = True
mask.a = l.a.copy()
#l.a[np.array(ind_remove)] = False
mask.a[np.array(ind_remove)] = False
#mask.a[nodes_to_be_removed] = False
#print np.all(mask.a==l.a)
lcc_graph = gt.GraphView(lcc_graph, vfilt=mask)
#update the valid nodes
valid_vertices = list(set(valid_vertices) - set(ind_remove))
print len(valid_vertices), len(sorted_vertex),lcc_graph.num_vertices()
lcc_temp_size = float(lcc_graph.num_vertices())
#print 'valid', len(valid_vertices),lcc_temp_size
valid_vertices = list(sorted_vertex -set(nodes_to_be_removed))
nodes_to_be_removed = valid_vertices[:int(remove_size)]
print lcc_temp_size,lcc_org_size
rel_size = lcc_temp_size/lcc_org_size
lcc_size.append(rel_size)
print 'lcc size', rel_size
print '------------'
step+=1
return lcc_size

Categories