How to reshape a networkx graph in Python?

后端 未结 1 448
萌比男神i
萌比男神i 2021-02-01 04:38

So I created a really naive (probably inefficient) way of generating hasse diagrams.

Question:

I have 4 dimensions...

1条回答
  •  一向
    一向 (楼主)
    2021-02-01 05:09

    This is a pragmatic, rather than purely mathematical answer.

    I think you have two issues - one with layout, the other with your network.

    1. Network

    You have too many edges in your network for it to represent the unit tesseract. Caveat I'm not an expert on the maths here - just came to this from the plotting angle (matplotlib tag). Please explain if I'm wrong.

    Your desired projection and, for instance, the wolfram mathworld page for a Hasse diagram for n=4 has only 4 edges connected all nodes, whereas you have 6 edges to the 2 and 7 edges to the 3 bit nodes. Your graph fully connects each "level", i.e. 4-D vectors with 0 1 values connect to all vectors with 1 1 value, which then connect to all vectors with 2 1 values and so on. This is most obvious in the projection based on the Wikipedia answer (2nd image below)

    2. Projection

    I couldn't find a pre-written algorithm or library to automatically project the 4D tesseract onto a 2D plane, but I did find a couple of examples, e.g. Wikipedia. From this, you can work out a co-ordinate set that would suit you and pass that into the nx.draw() call.

    Here is an example - I've included two co-ordinate sets, one that looks like the projection you show above, one that matches this one from wikipedia.

    import networkx as nx
    import matplotlib.pyplot as plt
    import itertools
    
    H = nx.DiGraph()
    
    axis_labels = ['p','q','r','s']
    
    D_len_node = {}
    
    #Iterate through axis labels
    for i in xrange(0,len(axis_labels)+1):
        #Create edge from empty set
        if i == 0:
            for ax in axis_labels:
                H.add_edge('O',ax)
        else:
            #Create all non-overlapping combinations
            combinations = [c for c in itertools.combinations(axis_labels,i)]
            D_len_node[i] = combinations
        #Create edge from len(i-1) to len(i) #eg. pq >>> pqr, pq >>> pqs
        if i > 1:
            for node in D_len_node[i]:
                for p_node in D_len_node[i-1]:
                    if set.intersection(set(p_node),set(node)):
                        H.add_edge(''.join(p_node),''.join(node))
    
    #This is manual two options to project tesseract onto 2D plane 
    # - many projections are available!!
    wikipedia_projection_coords = [(0.5,0),(0.85,0.25),(0.625,0.25),(0.375,0.25),
                                    (0.15,0.25),(1,0.5),(0.8,0.5),(0.6,0.5),
                                    (0.4,0.5),(0.2,0.5),(0,0.5),(0.85,0.75),
                                    (0.625,0.75),(0.375,0.75),(0.15,0.75),(0.5,1)]
    
    #Build the "two cubes" type example projection co-ordinates
    half_coords = [(0,0.15),(0,0.6),(0.3,0.15),(0.15,0),
                   (0.55,0.6),(0.3,0.6),(0.15,0.4),(0.55,1)]
    #make the coords symmetric
    example_projection_coords = half_coords + [(1-x,1-y) for (x,y) in half_coords][::-1]
    
    print example_projection_coords
    
    
    def powerset(s):
        ch = itertools.chain.from_iterable(itertools.combinations(s, r) for r in range(len(s)+1))
        return [''.join(t) for t in ch]
    
    pos={}
    for i,label in enumerate(powerset(axis_labels)):
        if label == '':
           label = 'O'
        pos[label]= example_projection_coords[i]
    
    #Show Plot
    nx.draw(H,pos,with_labels = True,node_shape = 'o')
    plt.show() 
    

    Note - unless you change what I've mentioned in 1. above, they still have your edge structure, so won't look exactly the same as the examples from the web. Here is what it looks like with your existing network generation code - you can see the extra edges if you compare it to your example (e.g. I don't this pr should be connected to pqs:

    'Two cube' projection

    Projection of tesseract generated by code

    Wikimedia example projection

    Alternative projection of tesseract generated by code provided


    Note

    If you want to get into the maths of doing your own projections (and building up pos mathematically), you might look at this research paper.


    EDIT:

    Curiosity got the better of me and I had to search for a mathematical way to do this. I found this blog - the main result of which being the projection matrix:

    4D to 2D projection matrix

    This led me to develop this function for projecting each label, taking the label containing 'p' to mean the point has value 1 on the 'p' axis, i.e. we are dealing with the unit tesseract. Thus:

    def construct_projection(label):
        r1 = r2 = 0.5
        theta = math.pi / 6
        phi = math.pi / 3
        x = int( 'p' in label) + r1 * math.cos(theta) * int('r' in label) - r2 * math.cos(phi) * int('s' in label)
        y = int( 'q' in label) + r1 * math.sin(theta) * int('r' in label) + r2 * math.sin(phi) * int('s' in label)
        return (x,y)
    

    Gives a nice projection into a regular 2D octagon with all points distinct.

    This will run in the above program, just replace

     pos[label] = example_projection_coords[i]
    

    with

    pos[label] = construct_projection(label)
    

    This gives the result:

    projection onto an octagon

    play with r1,r2,theta and phi to your heart's content :)

    0 讨论(0)
提交回复
热议问题