#!/usr/bin/env python # coding: utf-8 # In[2]: import itertools as it import networkx as nx from collections import Counter import copy import matplotlib.pyplot as plt import random from fractions import Fraction import math def collapse(fraction): if fraction < 1: while fraction < 1: fraction *= Fraction(2, 1) elif fraction >= 2: while fraction >= 2: fraction *= Fraction(1, 2) return fraction def hsPointToFR(point): fraction = Fraction(1, 1) for dim in point: if dim > 0: fraction = fraction * dim else: fraction = fraction * 1/abs(dim) return fraction def pitches(iterable, r): for base in it.combinations_with_replacement(iterable, r - 1): split = tuple(list(g) for k, g in it.groupby(tuple(b for b in base if b != 1))) mults = list(it.product([-1, 1], repeat = len(split))) for mult in mults: yield tuple(it.chain(*[[val * mult[idx] for val in g] for idx, g in enumerate(split)])) def expandPitch(pitch): num = 1; den = 1; expandedPitch = list(pitch) for dim in pitch: if dim > 0: num *= dim else: den *= abs(dim) fraction = num/den if fraction < 1: while fraction < 1: fraction *= 2 expandedPitch = [2] + expandedPitch elif fraction >= 2: while fraction >= 2: fraction *= 1/2 expandedPitch = [-2] + expandedPitch return tuple(expandedPitch) def expandChord(chord): return tuple([expandPitch(p) for p in chord]) def transposePitch(pitch, trans): transposedPitch = list(pitch) for t in trans: if (t * -1) in transposedPitch: transposedPitch.remove(t * -1) else: transposedPitch.append(t) transposedPitch.sort(key=lambda val: abs(val)) return transposedPitch def transposeChord(chord, trans): transposedChord = list(chord) for pdx, pitch in enumerate(chord): transposedPitch = transposePitch(pitch, trans) transposedChord[pdx] = tuple(transposedPitch) return tuple(transposedChord) def chords(pitches, r): def is_connected(iterable): points = comparitors = list(iterable) connectedPoints = [] base = points[0] bIdxScroll = 0 while True: for comp in comparitors: comps = sorted([base, comp], key=len, reverse=True) if ((Counter(comps[0]) - Counter(comps[1])).total() == 1) and (len(comps[0]) - len(comps[1]) == 1): comparitors = connectedPoints = connectedPoints + comps points.remove(base) if comp in points: points.remove(comp) if(len(points) == 0): return True else: base = points[0] bIdxScroll = 0 break else: if bIdxScroll < (len(points) - 1): bIdxScroll += 1 base = points[bIdxScroll] else: return False def is_centered(iterable): return len(list(iterable)[0]) == 0 #return filter(is_connected, it.takewhile(is_centered, it.combinations(pitches, r))) return {c for c in it.takewhile(is_centered, it.combinations(pitches, r)) if is_connected(c)} def pitchDifference(frs): cents1 = (1200 * math.log(hsPointToFR(frs[0]), 2)) cents2 = (1200 * math.log(hsPointToFR(frs[1]), 2)) return abs(cents2 - cents1) def difference(p1, p2): return transposePitch(p1, [p * -1 for p in p2]) def edges(chords): def edgeDict(transposition, symDiff): dict = {} dict['melodic_movement'] = pitchDifference(symDiff) dict['transposition'] = transposition return dict def reverseDict(dict): revDict = copy.deepcopy(dict) if revDict['transposition'] != (): revDict['transposition'] = tuple(t * -1 for t in revDict['transposition']) return revDict def edgeData(iterable): [base, comp] = list(iterable) expandedBase = expandChord(base) expandedComp = expandChord(comp) transpositions = set([tuple(difference(pair[0], pair[1])) for pair in set(it.product(expandedBase, expandedComp))]) edges = [(expandedBase, expandedComp, edgeDict(t, symDiff)) for t in transpositions if len(symDiff := list(set(expandedBase) ^ set(tChord := transposeChord(expandedComp, t)))) == 2] edges = edges + [(e[1], e[0], reverseDict(e[2])) for e in edges] if edges != []: return edges else: return None return list(it.chain(*[e for c in it.combinations(chords, 2) if (e := edgeData(c)) is not None])) def graph(edges): G = nx.MultiDiGraph() G.add_edges_from(edges) return G def hamiltonian(G): F = [(G,[list(G.nodes())[0]])] n = G.number_of_nodes() while F: graph,path = F.pop() confs = [] neighbors = (node for node in graph.neighbors(path[-1]) if node != path[-1]) #exclude self loops for neighbor in neighbors: conf_p = path[:] conf_p.append(neighbor) conf_g = nx.Graph(graph) conf_g.remove_node(path[-1]) confs.append((conf_g,conf_p)) for g,p in confs: if len(p)==n: return p else: F.append((g,p)) return None def stochastic_hamiltonian(graph): check_graph = graph.copy() #next_node = random.choice(list(graph.nodes())) next_node = list(graph.nodes())[0] check_graph.remove_node(next_node) path = [next_node] while (nx.number_of_nodes(check_graph) > 0) and (len(path) < 5000): neighbors = graph[next_node] nd_list = list(graph.degree(list(neighbors))) neighbors, weights = zip(*[[n, 1/pow(d, 2) if n not in path else 0.0000001] for n, d in nd_list]) next_node = random.choices(neighbors, weights=weights)[0] path.append(next_node) if next_node in check_graph.nodes: check_graph.remove_node(next_node) return [path, check_graph] def stochastic_hamiltonian(graph): check_graph = graph.copy() #next_node = random.choice(list(graph.nodes())) next_node = list(graph.nodes())[0] check_graph.remove_node(next_node) path = [] while (nx.number_of_nodes(check_graph) > 0) and (len(path) < 5000): outEdges = list(graph.out_edges(next_node, data=True)) weights = [(1 if e[2]['melodic_movement'] < 200 else 0.001) * (1 if e[1] not in [pE[0] for pE in path] else 0.0000001) for e in outEdges] edge = random.choices(outEdges, weights=weights)[0] next_node = edge[1] path.append(edge) if next_node in check_graph.nodes: check_graph.remove_node(next_node) return path # In[3]: pSet = pitches([1, 3, 5], 4) #print(len(list(pSet))) cSet = chords(pSet, 4) #print(cSet) eSet = edges(cSet) #for e in eSet: # print(e) testGraph = graph(eSet) # In[4]: len(testGraph.nodes) # In[5]: len(testGraph.edges) # In[6]: sGraph = nx.Graph(testGraph) pos = nx.draw_spring(sGraph, node_size=5, width=0.1) # larger figure size plt.figure(1, figsize=(12,12)) nx.draw(sGraph, pos, node_size=5, width=0.1) #plt.show() plt.savefig('compact_sets.png', dpi=150) # In[7]: def reconcilePath(ham): def sortByOther(c1, c2, trans): indices = list(range(len(c1))) sortedChord = copy.deepcopy(c2) for pitch in c2: transposedPitch = tuple(transposePitch(pitch, trans)) if transposedPitch in c1: index = c1.index(transposedPitch) sortedChord[index] = pitch indices.remove(index) else: diff = pitch sortedChord[indices[0]] = diff return sortedChord rPath = [[[], [list(p) for p in ham[0][0]]]] for cdx in range(len(ham)-1): c1 = list(ham[cdx][0]) c2 = list(ham[cdx][1]) trans = list(ham[cdx][2]['transposition']) c2 = sortByOther(c1, c2, trans) ham[cdx+1][0] = c2 rPath.append([trans, [list(p) for p in c2]]) return rPath ham = stochastic_hamiltonian(testGraph) ham = [list(e) for e in ham] print(len(ham)) for e in ham: print(e) rPath = reconcilePath(ham) rPath # In[8]: def pathToChords(path): curRoot = Fraction(1, 1) chords = [] for trans, points in path: curRoot = curRoot * hsPointToFR(trans) chord = [float(curRoot * hsPointToFR(p)) for p in points] chords.append(chord) return chords fPath = pathToChords(rPath) len(set([tuple(p) for p in fPath])) fPath # In[284]: # Opening a file in write mode{ file = open("seq.txt", "w+") # Converting the array to a string and writing to the file content = str(fPath) file.write(content) # Closing the file file.close() # In[279]: for edge in list(testGraph.edges(data=True))[:1000]: print(edge) # In[161]: import networkx as nx from matplotlib import pyplot as plt import math G = nx.grid_graph(dim=(range(-3, 4), range(-3, 4))) def getLabel(x, y): num = 1 den = 1 if x >= 0: num *= math.pow(3, x) else: den *= math.pow(3, abs(x)) if y >= 0: num *= math.pow(2, y) else: den *= math.pow(2, abs(y)) return str(int(num)) + "/" + str(int(den)) plt.figure(figsize=(10 * math.log2(3), 10 * math.log2(2))) #plt.figure(figsize=(10, 10)) pos = {(x, y):(x * math.log2(3), y * math.log2(2)) for x,y in G.nodes()} labels = {(x, y):getLabel(x, y) for x,y in G.nodes()} nx.draw_networkx_labels(G, pos, labels=labels) nx.draw(G, pos=pos, node_color='white', with_labels=False, node_size=1000) # In[160]: import networkx as nx from matplotlib import pyplot as plt import math G = nx.grid_graph(dim=(range(-2, 3), range(-2, 3))) def collapseLabel(fraction): if fraction < 1: while fraction < 1: fraction *= Fraction(2, 1) elif fraction >= 2: while fraction >= 2: fraction *= Fraction(1, 2) return fraction def getLabel(x, y): num = 1 den = 1 if x >= 0: num *= math.pow(5, x) else: den *= math.pow(5, abs(x)) if y >= 0: num *= math.pow(3, y) else: den *= math.pow(3, abs(y)) fraction = collapse(Fraction(int(num), int(den))) num = fraction.numerator den = fraction.denominator return str(int(num)) + "/" + str(int(den)) plt.figure(figsize=(5 * math.log2(5), 5 * math.log2(3))) #plt.figure(figsize=(10, 10)) pos = {(x, y):(x, y) for x,y in G.nodes()} labels = {(x, y):getLabel(x, y) for x,y in G.nodes()} nx.draw_networkx_labels(G, pos, labels=labels) nx.draw(G, pos=pos, node_color='white', with_labels=False, node_size=2000) # In[44]: for node in list(testGraph.nodes)[2:3]: edges = list(testGraph.out_edges(node, data=True)) for edge in edges: if list(edge)[2]['transposition'] != (): print(edge) # In[251]: import networkx as nx import numpy as np import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # The graph to visualize G = nx.grid_graph(dim=(range(-1, 2), range(-1, 2), range(-1, 2))) # 3d spring layout #pos = nx.spring_layout(G, dim=3, seed=779) pos = {(x, y, z):(math.log2(2) * x, math.log2(3) * y, math.log2(5) * z) for x,y,z in G.nodes()} # Extract node and edge positions from the layout node_xyz = np.array([pos[v] for v in sorted(G)]) edge_xyz = np.array([(pos[u], pos[v]) for u, v in G.edges()]) # Create the 3D figure fig = plt.figure(figsize=(10, 10)) ax = fig.add_subplot(111, projection="3d") # Plot the nodes - alpha is scaled by "depth" automatically ax.scatter(*node_xyz.T, s=100, ec="w") ax.view_init(elev=30, azim=45, roll=15) ax.axis('equal') # Plot the edges for vizedge in edge_xyz: ax.plot(*vizedge.T, color="tab:gray") def _format_axes(ax): """Visualization options for the 3D axes.""" # Turn gridlines off ax.grid(False) # Suppress tick labels for dim in (ax.xaxis, ax.yaxis, ax.zaxis): dim.set_ticks([]) # Set axes labels ax.set_xlabel("x") ax.set_ylabel("y") ax.set_zlabel("z") _format_axes(ax) fig.tight_layout() plt.show() # In[31]: from tikzpy import TikzPicture def collapseLabel(fraction): if fraction < 1: while fraction < 1: fraction *= Fraction(2, 1) elif fraction >= 2: while fraction >= 2: fraction *= Fraction(1, 2) return fraction def getLabel(x, y, z, collapse = False): num = 1 den = 1 if x >= 0: num *= math.pow(3, x) else: den *= math.pow(3, abs(x)) if y >= 0: num *= math.pow(5, y) else: den *= math.pow(5, abs(y)) if z >= 0: num *= math.pow(2, z) else: den *= math.pow(2, abs(z)) if collapse: fraction = collapseLabel(Fraction(int(num), int(den))) else: fraction = Fraction(int(num), int(den)) num = fraction.numerator den = fraction.denominator return str(int(num)) + "/" + str(int(den)) def chord2Points(chord): points = [] for n in chord: counter = Counter(n) points.append(tuple([counter[d] - counter[-d] for d in [2, 3, 5]])) return tuple(points) def genLattice(chord = None, ranges = None, filename = "tikz", collapse = False, scale = 1): dx = math.log2(3) * scale dy = math.log2(5) * scale dz = math.log2(2) * scale if chord: set = chord2Points(chord) if ranges: rz,rx,ry = ranges else: rz,rx,ry = [[min(t), max(t) + 1] for t in list(zip(*set))] if collapse: rz = [0, 1] tikz = TikzPicture(center=True) tikz.set_tdplotsetmaincoords(30, -30) tikz.options = "tdplot_main_coords" for x in range(*rx): for y in range(*ry): for z in range(*rz): line = tikz.line((x * dx - dx / 2, y * dy, z * dz), (x * dx + dx / 2, y * dy, z * dz), options="thick, black, -") line = tikz.line((x * dx, y * dy - dy / 2, z * dz), (x * dx, y * dy + dy / 2, z * dz), options="thick, black, -") if not collapse: line = tikz.line((x * dx, y * dy, z * dz - dz / 2), (x * dx, y * dy, z * dz + dz / 2), options="thick, black, -") node = tikz.node((x * dx, y * dy, z * dz), options="draw, fill=white, scale=0.5", text=getLabel(x,y,z, collapse)) if chord: for e in set: z,x,y = e if collapse: z = 0 line = tikz.line((x * dx - dx / 2, y * dy, z * dz), (x * dx + dx / 2, y * dy, z * dz), options="thick, black, -") line = tikz.line((x * dx, y * dy - dy / 2, z * dz), (x * dx, y * dy + dy / 2, z * dz), options="thick, black, -") if not collapse: line = tikz.line((x * dx, y * dy, z * dz - dz / 2), (x * dx, y * dy, z * dz + dz / 2), options="thick, black, -") node = tikz.node((x * dx, y * dy, z * dz), options="draw, fill=yellow, scale=0.5", text=getLabel(x,y,z, collapse)) tikz.compile(filename + ".pdf", True) texFile = open(filename + ".tex", "w+") texFile.write(tikz.code()) texFile.close() # In[72]: edge = (((), (-2, 3), (2, 3, -5), (3, 3, -5)), ((), (2, 2, -3), (-2, 3), (-2, -2, 5)), {'melodic_movement': 813.6862861351653, 'transposition': (2, 3, -5)}) chord = transposeChord(edge[0], (-2, -3, 5)) #genLattice(chord, path="figure.pdf", collapse=False) genLattice(chord, ranges=[[-2, 2], [-2, 2], [-2, 2]], filename="compact_set_1_transposed_expanded_padded", collapse=False, scale=2) # In[79]: edge = (((), (-2, 3), (2, 3, -5), (3, 3, -5)), ((), (2, 2, -3), (-2, 3), (-2, -2, 5)), {'melodic_movement': 813.6862861351653, 'transposition': (2, 3, -5)}) chord = transposeChord(edge[0], (-2, -3, 5)) #genLattice(chord, path="figure.pdf", collapse=False) genLattice(chord, ranges=[[-2, 2], [-1, 3], [-1, 2]], filename="compact_set_1_transposed_expanded_padded", collapse=False, scale=2) # In[80]: edge = (((), (-2, 3), (2, 3, -5), (3, 3, -5)), ((), (2, 2, -3), (-2, 3), (-2, -2, 5)), {'melodic_movement': 813.6862861351653, 'transposition': (2, 3, -5)}) chord = edge[0] #genLattice(chord, path="figure.pdf", collapse=False) genLattice(chord, ranges=[[-2, 2], [-1, 3], [-1, 2]], filename="compact_set_1_expanded_padded", collapse=False, scale=2)