Mining & Steiner Trees

384 days ago by exp.ipi

%html <h1>Mine Optimisation & Steiner Trees</h1> <p>The following is program to compute approximate steiner trees for a gradient restricted network, such as a mine</p> <p><small>Note: the last cell ("Helper functions") needs to be run first</small></p> 
       

Mine Optimisation & Steiner Trees

The following is program to compute approximate steiner trees for a gradient restricted network, such as a mine

Note: the last cell ("Helper functions") needs to be run first

Mine Optimisation & Steiner Trees

The following is program to compute approximate steiner trees for a gradient restricted network, such as a mine

Note: the last cell ("Helper functions") needs to be run first

%html <p>Record any changes you make to the end of this list (except if you just change the stuff in the "data" box), so that others can work out what's happened:</p> <ul> </ul> 
       

Record any changes you make to the end of this list (except if you just change the stuff in the "data" box), so that others can work out what's happened:

Record any changes you make to the end of this list (except if you just change the stuff in the "data" box), so that others can work out what's happened:

#### data #### ## The terminals of the steiner tree (e.g. ore bodies, surface outlets) nodes = [(0,0,-0.2),(0,0.5,-0.25),(-0.1,0.4,-0.2),(-0.1,0.1,-0.34),(-0.2,-0.2,-0.27)] #nodes = [(0,0,0),(0,0.5,-0.15),(-0.1,0.4,-0.1),(-0.1,0.1,-0.24),(-0.2,-0.2,-0.17)] #nodes = [(0,0,-0.09),(0,0.1,-0.15),(-0.1,0.08,-0.12),(0.08,-0.1,-0.14),(-0.05,-0.1,-0.17)] #nodes = [(0,0,0),(0,0.1,-0.2),(-0.1,0.08,-0.5),(0.08,-0.1,-0.3),(-0.05,-0.1,-0.4)] #nodes = [(0,0,0),(0,0.1,-0.2),(-0.1,0.08,-0.38),(0.08,-0.1,-0.28),(-0.05,-0.1,-0.33)] ## The restriction on the gradient of edges of the steiner tree gradient_restriction = 1/7.0 
       
## In this cell we compute the candidates for the steiner points ## and then only keep the useful/valid ones m = gradient_restriction # make sure all the nodes are floating point (not symbolic) for efficiency fnodes = [ [ float(x) for x in p ] for p in nodes ] # Get a few candidates for steiner points new_nodes = get_all_3_steiner_points(fnodes)+get_all_4_steiner_points(fnodes) # compute the (normal) convex hull of the points (the field=RDF # is to stop the nodes being rounded to integral points) convex_hull = Polyhedron(fnodes,field=RDF) # filter out the steiner points outside this hull valid_nodes = [] invalid_nodes = [] for n in new_nodes: if convex_hull.contains(n): if n not in valid_nodes: valid_nodes.append(n) else: invalid_nodes.append(n) print len(valid_nodes),"of the",len(new_nodes),"new nodes are valid" # Make a plot of the interesting bits G = Graphics() if len(invalid_nodes) > 0: G += points(invalid_nodes,color='orange',size=3) if len(valid_nodes) > 0: G += points(valid_nodes) if len(fnodes) > 0: G += points(fnodes,color='red',size=10) G += convex_hull.show(opacity=0.1) print print "====================================================" print " Graph of the candidates for steiner points" G.show(aspect_ratio=1) 
       
11 of the 15 new nodes are valid

====================================================
     Graph of the candidates for steiner points
11 of the 15 new nodes are valid

====================================================
     Graph of the candidates for steiner points
## Now we construct the complete graph on these vertex, with the appropriate weights all_nodes = fnodes+valid_nodes n_o_nodes = len(all_nodes) # a dictionary of the node numbers => euclidean position node_dict = {} for i,node in enumerate(all_nodes): node_dict[i] = node # the dictionary required to make the graph, i.e: # each node number => (dictionary of neighbour number => weight of edge) graph_dict = {} for start in range(n_o_nodes): start_point = node_dict[start] ends_dict = {} for end in range(start+1,n_o_nodes): ends_dict[end] = metric(m,start_point,node_dict[end]) graph_dict[start] = ends_dict complete_graph=Graph(graph_dict,weighted=True) print "The complete graph (with steiner points) on the nodes given above" print "(note: red edges are 'bent', red vertices are the terminals)" graph_plot(complete_graph,node_dict,range(len(nodes)),m).show(aspect_ratio=1) 
       
The complete graph (with steiner points) on the nodes given above
(note: red edges are 'bent', red vertices are the terminals)
The complete graph (with steiner points) on the nodes given above
(note: red edges are 'bent', red vertices are the terminals)
## compute the steiner tree # using that \/ function is dodgy (as in it fails for more than 3 or 4 nodes), # so we need to write our own (possible the approximating one) smt_of_complete = complete_graph.steiner_tree(range(len(nodes)),weighted=True) 
       
## print it out print "The steiner tree on the nodes given above" print "(note: red edges are 'bent', red vertices are the terminals)" graph_plot(smt_of_complete,node_dict,range(len(nodes)),m).show(aspect_ratio=1,frame=True) 
       
The steiner tree on the nodes given above
(note: red edges are 'bent', red vertices are the terminals)
Traceback (click to the left of this block for traceback)
...
NameError: name 'graph_plot' is not defined
The steiner tree on the nodes given above
(note: red edges are 'bent', red vertices are the terminals)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "_sage_input_2.py", line 10, in <module>
    exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("IyMgcHJpbnQgaXQgb3V0CnByaW50ICJUaGUgc3RlaW5lciB0cmVlIG9uIHRoZSBub2RlcyBnaXZlbiBhYm92ZSIKcHJpbnQgIihub3RlOiByZWQgZWRnZXMgYXJlICdiZW50JywgcmVkIHZlcnRpY2VzIGFyZSB0aGUgdGVybWluYWxzKSIKZ3JhcGhfcGxvdChzbXRfb2ZfY29tcGxldGUsbm9kZV9kaWN0LHJhbmdlKGxlbihub2RlcykpLG0pLnNob3coYXNwZWN0X3JhdGlvPTEsZnJhbWU9VHJ1ZSk="),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))
  File "", line 1, in <module>
    
  File "/tmp/tmpw900Lc/___code___.py", line 5, in <module>
    exec compile(u'graph_plot(smt_of_complete,node_dict,range(len(nodes)),m).show(aspect_ratio=_sage_const_1 ,frame=True)
  File "", line 1, in <module>
    
NameError: name 'graph_plot' is not defined
## helper functions # return a list of all the k subsets of a given list def k_subsets(list,k): if len(list) < k or k <= 0: return [] elif k == 1: # treating this separately makes things simpler return [[x] for x in list] else: subsets = [] # select a first element, and then find all the k-1 subsets # from the later elements, and prepend your chosen first element for i,elem in enumerate(list): for smaller_subset in k_subsets(list[i+1:], k - 1): subsets.append([elem] + smaller_subset) return subsets # compute the given metric for two points (in any dimension > 1) def metric(m,p1,p2,label_too=False): if len(p1) != len(p2): return 0 # square of the horizontal distance horiz = sum( [ (a-b)^2 for a,b in zip(p1,p2)[:-1] ] ) # the vertical distance vert = abs(p1[-1] - p2[-1]) # rephrasing of the condition for distinguishing the types of edges test = vert^2 - m^2 * horiz if test > 0: if label_too: return 'b', sqrt(1+1/m^2)*vert else: return sqrt(1+1/m^2)*vert elif label_too: if test == 0: return 'm', sqrt(horiz + vert^2) else: return 'f', sqrt(horiz + vert^2) else: return sqrt(horiz + vert^2) ### plots the graph nicely ## this needs to be rewritten def graph_plot(g,pos,special,m,edges={'b':{'color':'red'},'m':{'color':'orange'},'f':{'color':'green'}},special_vertex={'color':'red','size':8},normal_vertex={'size':6}): e = g.edges() default = {} if 'all' in edges: default = edges['all'] if 'b' not in edges: edges['b'] = default if 'm' not in edges: edges['m'] = default if 'f' not in edges: edges['f'] = default g = Graphics() points = [] for i,j,w in e: label,d = metric(m,pos[i],pos[j],label_too=True) g += line3d([pos[i],pos[j]],**edges[label]) if i not in points: points.append(i) if i in special: g += point3d(pos[i],**special_vertex) else: g += point3d(pos[i],**normal_vertex) if j not in points: points.append(j) if j in special: g += point3d(pos[j],**special_vertex) else: g += point3d(pos[j],**normal_vertex) return g def average(list): return sum(list)/len(list) # can add an arbitrary number of points # does it by grouping the coefficients by index # and then summing them. def add_points(*points): return [sum(row) for row in zip(*points)] def scale_point(s,p1): return [s*x for x in p1] # computes the total distance of one point to any number of others def total_distance(m,p,*other_points): return sum([ metric(m,p,other_point,label_too=False) for other_point in other_points ]) # find/approximate a steiner point of some vertices returning (True, p) # iff p is the steiner point and is different to all of them def find_steiner_point(m,*ps): if len(ps) == 0: return False, None l = len(ps[0]) if not all([len(x) == l for x in ps]): return False,None # Finds an alright guess, i.e. the geometric centriod of the points, # and then optimises that guess by adding a small amount here and there to try to improve it. centroid = scale_point(1/len(ps),add_points(*ps)) centroid_dist = total_distance(m,centroid,*ps) # initial conditions guess = centroid dist = centroid_dist step = dist / 10 # 10 is just for fun while step > 5e-4 * centroid_dist: best = guess best_dist = dist # this is our permuting stage, so it adds a small amount in each direction (6 of them) # and checks to see if these new points are better for twiddler in [(step,0,0),(0,step,0),(0,0,step)]: neg_twiddler = scale_point(-1,twiddler) guess1 = add_points(guess, twiddler) guess2 = add_points(guess, neg_twiddler) dist1 = total_distance(m,guess1,*ps) dist2 = total_distance(m,guess2,*ps) # do the checks if dist1 < best_dist: best = guess1 best_dist = dist1 if dist2 < best_dist: best = guess2 best_dist = dist2 # we've found our new winners, so remember them for next time guess = best dist = best_dist step /= 2 # now check that the steiner point isn't too close to one of the terminals # if it is, we will assume that the terminal is the steiner point threshold = 1e-2 * centroid_dist for p in ps: if metric(m,p,guess) < threshold: return False,p # if i'm here, then guess passed all the tests, so return it return True,guess def get_all_3_steiner_points(ps): v = [] # do the 3 points for set_of_points in k_subsets(ps,3): is_point,real_point = find_steiner_point(m, *set_of_points) if is_point: v.append(real_point) return v def get_all_4_steiner_points(ps): v = [] for set_of_points in k_subsets(ps,4): is_point,real_point = find_steiner_point(m, *set_of_points) if is_point: v.append(real_point) return v