Clean up unused code
- Remove unused methods: generate_connected_sets_with_edges, build_graph_lattice_method, _compute_edge_data_fast, _wrap_pitch, _toCHS - Remove unused import: product from itertools - Remove old notebook files: compact_sets_optimized_2.py, compact_sets_optimized_2.ipynb - Keep session-ses_328e.md for reference compact_sets.py: 1450 -> 1067 lines
This commit is contained in:
parent
c44dd60e83
commit
fa19b7877f
385
compact_sets.py
385
compact_sets.py
|
|
@ -13,7 +13,7 @@ Mathematical foundations:
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
from fractions import Fraction
|
from fractions import Fraction
|
||||||
from itertools import combinations, permutations, product
|
from itertools import combinations, permutations
|
||||||
from math import prod, log
|
from math import prod, log
|
||||||
from operator import add
|
from operator import add
|
||||||
from random import choice, choices, seed
|
from random import choice, choices, seed
|
||||||
|
|
@ -372,389 +372,6 @@ class HarmonicSpace:
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
def generate_connected_sets_with_edges(
|
|
||||||
self, min_size: int, max_size: int, symdiff_range: tuple[int, int]
|
|
||||||
) -> tuple[set[Chord], list[tuple[Chord, Chord, dict]]]:
|
|
||||||
"""
|
|
||||||
Generate chords and find edges using sibling grouping.
|
|
||||||
|
|
||||||
For symdiff=2: group chords by parent (chord with one fewer pitch)
|
|
||||||
All siblings (same parent) have symdiff=2 with each other after transposition.
|
|
||||||
|
|
||||||
This version finds ALL parents for each chord to ensure complete coverage.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
min_size: Minimum number of pitches in a chord
|
|
||||||
max_size: Maximum number of pitches in a chord
|
|
||||||
symdiff_range: (min, max) symmetric difference for valid edges
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Tuple of (chords set, list of edges with data)
|
|
||||||
"""
|
|
||||||
# Generate all chords first
|
|
||||||
chords_set = self.generate_connected_sets(min_size, max_size)
|
|
||||||
|
|
||||||
# Find ALL parents for each chord
|
|
||||||
# A parent is any size-(k-1) connected subset that can grow to this chord
|
|
||||||
chord_to_parents: dict[Chord, list[Chord]] = {}
|
|
||||||
|
|
||||||
for chord in chords_set:
|
|
||||||
if len(chord) <= min_size:
|
|
||||||
chord_to_parents[chord] = []
|
|
||||||
continue
|
|
||||||
|
|
||||||
parents = []
|
|
||||||
pitches_list = list(chord.pitches)
|
|
||||||
|
|
||||||
# Try removing each pitch to find possible parents
|
|
||||||
for i in range(len(pitches_list)):
|
|
||||||
candidate_pitches = pitches_list[:i] + pitches_list[i + 1 :]
|
|
||||||
if len(candidate_pitches) < min_size:
|
|
||||||
continue
|
|
||||||
candidate = Chord(tuple(candidate_pitches), self.dims)
|
|
||||||
if candidate.is_connected():
|
|
||||||
parents.append(candidate)
|
|
||||||
|
|
||||||
chord_to_parents[chord] = parents
|
|
||||||
|
|
||||||
# Group chords by parent - a chord may appear in multiple parent groups
|
|
||||||
from collections import defaultdict
|
|
||||||
|
|
||||||
parent_to_children: dict[tuple, list[Chord]] = defaultdict(list)
|
|
||||||
for chord, parents in chord_to_parents.items():
|
|
||||||
for parent in parents:
|
|
||||||
# Use sorted pitches as key
|
|
||||||
parent_key = tuple(sorted(p.hs_array for p in parent.pitches))
|
|
||||||
parent_to_children[parent_key].append(chord)
|
|
||||||
|
|
||||||
# Find edges between siblings
|
|
||||||
edges = []
|
|
||||||
seen_edges = set() # Deduplicate
|
|
||||||
from itertools import combinations
|
|
||||||
|
|
||||||
for parent_key, children in parent_to_children.items():
|
|
||||||
if len(children) < 2:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# For each pair of siblings
|
|
||||||
for c1, c2 in combinations(children, 2):
|
|
||||||
edge_data = self._find_valid_edges(c1, c2, symdiff_range)
|
|
||||||
for (
|
|
||||||
trans,
|
|
||||||
weight,
|
|
||||||
movements,
|
|
||||||
cent_diffs,
|
|
||||||
voice_crossing,
|
|
||||||
is_dt,
|
|
||||||
) in edge_data:
|
|
||||||
# Create edge key for deduplication (smaller chord first)
|
|
||||||
c1_key = tuple(sorted(p.hs_array for p in c1.pitches))
|
|
||||||
c2_key = tuple(sorted(p.hs_array for p in c2.pitches))
|
|
||||||
edge_key = (
|
|
||||||
(c1_key, c2_key, tuple(sorted(movements.items()))),
|
|
||||||
trans.hs_array,
|
|
||||||
)
|
|
||||||
|
|
||||||
if edge_key not in seen_edges:
|
|
||||||
seen_edges.add(edge_key)
|
|
||||||
edges.append(
|
|
||||||
(
|
|
||||||
c1,
|
|
||||||
c2,
|
|
||||||
{
|
|
||||||
"transposition": trans,
|
|
||||||
"weight": weight,
|
|
||||||
"movements": movements,
|
|
||||||
"cent_diffs": cent_diffs,
|
|
||||||
"voice_crossing": voice_crossing,
|
|
||||||
"is_directly_tunable": is_dt,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
inv_trans = self._invert_transposition(trans)
|
|
||||||
# Reverse edge
|
|
||||||
rev_edge_key = (
|
|
||||||
(
|
|
||||||
c2_key,
|
|
||||||
c1_key,
|
|
||||||
tuple(sorted(self._reverse_movements(movements).items())),
|
|
||||||
),
|
|
||||||
inv_trans.hs_array,
|
|
||||||
)
|
|
||||||
|
|
||||||
if rev_edge_key not in seen_edges:
|
|
||||||
seen_edges.add(rev_edge_key)
|
|
||||||
edges.append(
|
|
||||||
(
|
|
||||||
c2,
|
|
||||||
c1,
|
|
||||||
{
|
|
||||||
"transposition": inv_trans,
|
|
||||||
"weight": weight,
|
|
||||||
"movements": self._reverse_movements(movements),
|
|
||||||
"cent_diffs": list(reversed(cent_diffs)),
|
|
||||||
"voice_crossing": voice_crossing,
|
|
||||||
"is_directly_tunable": is_dt,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return chords_set, edges
|
|
||||||
|
|
||||||
def _is_terminating(self, pitch: Pitch, chord: Chord) -> bool:
|
|
||||||
"""Check if removing this pitch leaves the remaining pitches connected."""
|
|
||||||
remaining = tuple(p for p in chord.pitches if p != pitch)
|
|
||||||
if len(remaining) <= 1:
|
|
||||||
return True
|
|
||||||
remaining_chord = Chord(remaining, self.dims)
|
|
||||||
return remaining_chord.is_connected()
|
|
||||||
|
|
||||||
def build_graph_lattice_method(
|
|
||||||
self,
|
|
||||||
chords: set[Chord],
|
|
||||||
symdiff_min: int = 2,
|
|
||||||
symdiff_max: int = 2,
|
|
||||||
) -> nx.MultiDiGraph:
|
|
||||||
"""
|
|
||||||
Build voice leading graph using lattice neighbor traversal.
|
|
||||||
|
|
||||||
Algorithm:
|
|
||||||
1. For each chord C in our set
|
|
||||||
2. For each terminating pitch p in C (removing keeps remaining connected)
|
|
||||||
3. For each remaining pitch q in C \\ p:
|
|
||||||
For each adjacent pitch r to q (in full harmonic space):
|
|
||||||
Form C' = (C \\ p) ∪ {r}
|
|
||||||
If C' contains root -> add edge C -> C' (automatically valid)
|
|
||||||
If C' doesn't contain root -> transpose by each pitch -> add edges
|
|
||||||
|
|
||||||
No connectivity checks needed - guaranteed by construction.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
chords: Set of Chord objects
|
|
||||||
symdiff_min: Minimum symmetric difference (typically 2)
|
|
||||||
symdiff_max: Maximum symmetric difference (typically 2)
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
NetworkX MultiDiGraph
|
|
||||||
"""
|
|
||||||
graph = nx.MultiDiGraph()
|
|
||||||
|
|
||||||
for chord in chords:
|
|
||||||
graph.add_node(chord)
|
|
||||||
|
|
||||||
chord_index = {}
|
|
||||||
for chord in chords:
|
|
||||||
sig = tuple(sorted(p.hs_array for p in chord.pitches))
|
|
||||||
chord_index[sig] = chord
|
|
||||||
|
|
||||||
edges = []
|
|
||||||
edge_set = set()
|
|
||||||
|
|
||||||
root = self.pitch(tuple(0 for _ in self.dims))
|
|
||||||
|
|
||||||
for chord in chords:
|
|
||||||
chord_pitches = list(chord.pitches)
|
|
||||||
k = len(chord_pitches)
|
|
||||||
|
|
||||||
for p in chord_pitches:
|
|
||||||
if not self._is_terminating(p, chord):
|
|
||||||
continue
|
|
||||||
|
|
||||||
remaining = [x for x in chord_pitches if x != p]
|
|
||||||
|
|
||||||
for q in remaining:
|
|
||||||
# Generate adjacent pitches in CHS (skipping dim 0)
|
|
||||||
for d in range(1, len(self.dims)):
|
|
||||||
for delta in (-1, 1):
|
|
||||||
arr = list(q.hs_array)
|
|
||||||
arr[d] += delta
|
|
||||||
r = Pitch(tuple(arr), self.dims)
|
|
||||||
|
|
||||||
if r in chord_pitches:
|
|
||||||
continue
|
|
||||||
|
|
||||||
new_pitches = remaining + [r]
|
|
||||||
new_chord = Chord(tuple(new_pitches), self.dims)
|
|
||||||
|
|
||||||
contains_root = root in new_chord.pitches
|
|
||||||
|
|
||||||
if contains_root:
|
|
||||||
target_sig = tuple(
|
|
||||||
sorted(p.hs_array for p in new_chord.pitches)
|
|
||||||
)
|
|
||||||
target = chord_index.get(target_sig)
|
|
||||||
|
|
||||||
if target and target != chord:
|
|
||||||
edge_key = (chord, target)
|
|
||||||
if edge_key not in edge_set:
|
|
||||||
edge_set.add(edge_key)
|
|
||||||
|
|
||||||
movements, cent_diffs, voice_crossing = (
|
|
||||||
self._compute_edge_data_fast(chord, target)
|
|
||||||
)
|
|
||||||
if movements is not None:
|
|
||||||
is_dt = self._is_directly_tunable(
|
|
||||||
chord.pitches, target.pitches, movements
|
|
||||||
)
|
|
||||||
edges.append(
|
|
||||||
(
|
|
||||||
chord,
|
|
||||||
target,
|
|
||||||
{
|
|
||||||
"transposition": root.pitch_difference(
|
|
||||||
root
|
|
||||||
),
|
|
||||||
"weight": 1.0,
|
|
||||||
"movements": movements,
|
|
||||||
"cent_diffs": cent_diffs,
|
|
||||||
"voice_crossing": voice_crossing,
|
|
||||||
"is_directly_tunable": is_dt,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
for p_trans in new_chord.pitches:
|
|
||||||
trans = root.pitch_difference(p_trans)
|
|
||||||
transposed = new_chord.transpose(trans)
|
|
||||||
|
|
||||||
if root in transposed.pitches:
|
|
||||||
target_sig = tuple(
|
|
||||||
sorted(
|
|
||||||
p.hs_array for p in transposed.pitches
|
|
||||||
)
|
|
||||||
)
|
|
||||||
target = chord_index.get(target_sig)
|
|
||||||
|
|
||||||
if target and target != chord:
|
|
||||||
edge_key = (chord, target)
|
|
||||||
if edge_key not in edge_set:
|
|
||||||
edge_set.add(edge_key)
|
|
||||||
|
|
||||||
(
|
|
||||||
movements,
|
|
||||||
cent_diffs,
|
|
||||||
voice_crossing,
|
|
||||||
) = self._compute_edge_data_fast(
|
|
||||||
chord, target
|
|
||||||
)
|
|
||||||
if movements is not None:
|
|
||||||
is_dt = self._is_directly_tunable(
|
|
||||||
chord.pitches,
|
|
||||||
target.pitches,
|
|
||||||
movements,
|
|
||||||
)
|
|
||||||
edges.append(
|
|
||||||
(
|
|
||||||
chord,
|
|
||||||
target,
|
|
||||||
{
|
|
||||||
"transposition": trans,
|
|
||||||
"weight": 1.0,
|
|
||||||
"movements": movements,
|
|
||||||
"cent_diffs": cent_diffs,
|
|
||||||
"voice_crossing": voice_crossing,
|
|
||||||
"is_directly_tunable": is_dt,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
for u, v, data in edges:
|
|
||||||
graph.add_edge(u, v, **data)
|
|
||||||
|
|
||||||
inv_trans = self._invert_transposition(data["transposition"])
|
|
||||||
inv_movements = self._reverse_movements(data["movements"])
|
|
||||||
inv_cent_diffs = list(reversed(data["cent_diffs"]))
|
|
||||||
graph.add_edge(
|
|
||||||
v,
|
|
||||||
u,
|
|
||||||
transposition=inv_trans,
|
|
||||||
weight=1.0,
|
|
||||||
movements=inv_movements,
|
|
||||||
cent_diffs=inv_cent_diffs,
|
|
||||||
voice_crossing=data["voice_crossing"],
|
|
||||||
is_directly_tunable=data["is_directly_tunable"],
|
|
||||||
)
|
|
||||||
|
|
||||||
return graph
|
|
||||||
|
|
||||||
def _compute_edge_data_fast(self, c1: Chord, c2: Chord):
|
|
||||||
"""Compute edge data directly from two chords without transposition."""
|
|
||||||
c1_pitches = c1.pitches
|
|
||||||
c2_pitches = c2.pitches
|
|
||||||
k = len(c1_pitches)
|
|
||||||
|
|
||||||
c1_collapsed = [p.collapse() for p in c1_pitches]
|
|
||||||
c2_collapsed = [p.collapse() for p in c2_pitches]
|
|
||||||
|
|
||||||
common_c1 = []
|
|
||||||
common_c2 = []
|
|
||||||
for i, pc1 in enumerate(c1_collapsed):
|
|
||||||
for j, pc2 in enumerate(c2_collapsed):
|
|
||||||
if pc1 == pc2:
|
|
||||||
common_c1.append(i)
|
|
||||||
common_c2.append(j)
|
|
||||||
break
|
|
||||||
|
|
||||||
movements = {}
|
|
||||||
for src_idx, dest_idx in zip(common_c1, common_c2):
|
|
||||||
movements[src_idx] = dest_idx
|
|
||||||
|
|
||||||
changing_c1 = [i for i in range(k) if i not in common_c1]
|
|
||||||
changing_c2 = [j for j in range(k) if j not in common_c2]
|
|
||||||
|
|
||||||
if len(changing_c1) != len(changing_c2):
|
|
||||||
return None, None, None
|
|
||||||
|
|
||||||
if changing_c1:
|
|
||||||
valid = True
|
|
||||||
for src_i, dest_j in zip(changing_c1, changing_c2):
|
|
||||||
p1 = c1_pitches[src_i]
|
|
||||||
p2 = c2_pitches[dest_j]
|
|
||||||
if not self._is_adjacent_pitches(p1, p2):
|
|
||||||
valid = False
|
|
||||||
break
|
|
||||||
movements[src_i] = dest_j
|
|
||||||
|
|
||||||
if not valid:
|
|
||||||
return None, None, None
|
|
||||||
|
|
||||||
cent_diffs = []
|
|
||||||
for src_idx, dest_idx in movements.items():
|
|
||||||
src_pitch = c1_pitches[src_idx]
|
|
||||||
dst_pitch = c2_pitches[dest_idx]
|
|
||||||
cents = abs(src_pitch.to_cents() - dst_pitch.to_cents())
|
|
||||||
cent_diffs.append(cents)
|
|
||||||
|
|
||||||
voice_crossing = not all(movements.get(i, i) == i for i in range(k))
|
|
||||||
|
|
||||||
return movements, cent_diffs, voice_crossing
|
|
||||||
|
|
||||||
def _wrap_pitch(self, hs_array: tuple[int, ...]) -> tuple[int, ...]:
|
|
||||||
"""Wrap a pitch so its frequency ratio is in [1, 2)."""
|
|
||||||
p = self.pitch(hs_array)
|
|
||||||
return p.collapse().hs_array
|
|
||||||
|
|
||||||
def _toCHS(self, hs_array: tuple[int, ...]) -> tuple[int, ...]:
|
|
||||||
"""
|
|
||||||
Convert a pitch to Collapsed Harmonic Space (CHS).
|
|
||||||
|
|
||||||
In CHS, all pitches have dimension 0 = 0.
|
|
||||||
This is different from collapse() which only ensures frequency in [1, 2).
|
|
||||||
|
|
||||||
Steps:
|
|
||||||
1. First collapse to [1,2) to get pitch class
|
|
||||||
2. Then set dimension 0 = 0
|
|
||||||
"""
|
|
||||||
# First collapse to [1,2)
|
|
||||||
p = self.pitch(hs_array)
|
|
||||||
collapsed = p.collapse().hs_array
|
|
||||||
|
|
||||||
# Then set dim 0 = 0
|
|
||||||
result = list(collapsed)
|
|
||||||
result[0] = 0
|
|
||||||
return tuple(result)
|
|
||||||
|
|
||||||
def build_voice_leading_graph(
|
def build_voice_leading_graph(
|
||||||
self,
|
self,
|
||||||
chords: set[Chord],
|
chords: set[Chord],
|
||||||
|
|
|
||||||
|
|
@ -1,690 +0,0 @@
|
||||||
{
|
|
||||||
"cells": [
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 1,
|
|
||||||
"id": "b5d4c6c9-16d5-433c-bc2b-47a23642123c",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"from itertools import chain, combinations, permutations, product, pairwise\n",
|
|
||||||
"from math import prod, log\n",
|
|
||||||
"from copy import deepcopy\n",
|
|
||||||
"import networkx as nx\n",
|
|
||||||
"from fractions import Fraction\n",
|
|
||||||
"import json\n",
|
|
||||||
"from operator import add\n",
|
|
||||||
"\n",
|
|
||||||
"def hs_array_to_fr(hs_array):\n",
|
|
||||||
" return prod([pow(dims[d], hs_array[d]) for d in range(len(dims))])\n",
|
|
||||||
"\n",
|
|
||||||
"def hs_array_to_cents(hs_array):\n",
|
|
||||||
" return (1200 * log(hs_array_to_fr(hs_array), 2))\n",
|
|
||||||
"\n",
|
|
||||||
"def expand_pitch(hs_array):\n",
|
|
||||||
" expanded_pitch = list(hs_array)\n",
|
|
||||||
" frequency_ratio = hs_array_to_fr(hs_array)\n",
|
|
||||||
" if frequency_ratio < 1:\n",
|
|
||||||
" while frequency_ratio < 1:\n",
|
|
||||||
" frequency_ratio *= 2\n",
|
|
||||||
" expanded_pitch[0] += 1\n",
|
|
||||||
" elif frequency_ratio >= 2:\n",
|
|
||||||
" while frequency_ratio >= 2:\n",
|
|
||||||
" frequency_ratio *= 1/2\n",
|
|
||||||
" expanded_pitch[0] += -1\n",
|
|
||||||
" return tuple(expanded_pitch)\n",
|
|
||||||
"\n",
|
|
||||||
"def expand_chord(chord):\n",
|
|
||||||
" return tuple(expand_pitch(p) for p in chord)\n",
|
|
||||||
"\n",
|
|
||||||
"def collapse_pitch(hs_array):\n",
|
|
||||||
" collapsed_pitch = list(hs_array)\n",
|
|
||||||
" collapsed_pitch[0] = 0\n",
|
|
||||||
" return tuple(collapsed_pitch)\n",
|
|
||||||
"\n",
|
|
||||||
"def collapse_chord(chord):\n",
|
|
||||||
" return tuple(collapse_pitch(p) for p in chord)\n",
|
|
||||||
"\n",
|
|
||||||
"def transpose_pitch(pitch, trans):\n",
|
|
||||||
" return tuple(map(add, pitch, trans))\n",
|
|
||||||
"\n",
|
|
||||||
"def transpose_chord(chord, trans):\n",
|
|
||||||
" return tuple(transpose_pitch(p, trans) for p in chord)\n",
|
|
||||||
"\n",
|
|
||||||
"def cent_difference(hs_array1, hs_array2):\n",
|
|
||||||
" return hs_array_to_cents(hs_array2) - hs_array_to_cents(hs_array1)\n",
|
|
||||||
"\n",
|
|
||||||
"def pitch_difference(hs_array1, hs_array2):\n",
|
|
||||||
" return transpose_pitch(hs_array1, [p * -1 for p in hs_array2])\n",
|
|
||||||
"\n",
|
|
||||||
"def edges(chords, min_symdiff, max_symdiff, max_chord_size): \n",
|
|
||||||
"\n",
|
|
||||||
" def reverse_movements(movements):\n",
|
|
||||||
" return {value['destination']:{'destination':key, 'cent_difference':value['cent_difference'] * -1} for key, value in movements.items()}\n",
|
|
||||||
"\n",
|
|
||||||
" def is_directly_tunable(intersection, diff):\n",
|
|
||||||
" # this only works for now when intersection if one element - need to fix that\n",
|
|
||||||
" return max([sum(abs(p) for p in collapse_pitch(pitch_difference(d, list(intersection)[0]))) for d in diff]) == 1\n",
|
|
||||||
"\n",
|
|
||||||
" for combination in combinations(chords, 2):\n",
|
|
||||||
" [expanded_base, expanded_comp] = [expand_chord(chord) for chord in combination]\n",
|
|
||||||
" edges = []\n",
|
|
||||||
" transpositions = set(pitch_difference(pair[0], pair[1]) for pair in set(product(expanded_base, expanded_comp)))\n",
|
|
||||||
" for trans in transpositions:\n",
|
|
||||||
" expanded_comp_transposed = transpose_chord(expanded_comp, trans)\n",
|
|
||||||
" intersection = set(expanded_base) & set(expanded_comp_transposed)\n",
|
|
||||||
" symdiff_len = sum([len(chord) - len(intersection) for chord in [expanded_base, expanded_comp_transposed]])\n",
|
|
||||||
" if (min_symdiff <= symdiff_len <= max_symdiff):\n",
|
|
||||||
" rev_trans = tuple(t * -1 for t in trans)\n",
|
|
||||||
" [diff1, diff2] = [list(set(chord) - intersection) for chord in [expanded_base, expanded_comp_transposed]]\n",
|
|
||||||
" base_map = {val: {'destination':transpose_pitch(val, rev_trans), 'cent_difference': 0} for val in intersection}\n",
|
|
||||||
" base_map_rev = reverse_movements(base_map)\n",
|
|
||||||
" maps = []\n",
|
|
||||||
" diff1 += [None] * (max_chord_size - len(diff1) - len(intersection))\n",
|
|
||||||
" perms = [list(perm) + [None] * (max_chord_size - len(perm) - len(intersection)) for perm in set(permutations(diff2))]\n",
|
|
||||||
" for p in perms:\n",
|
|
||||||
" appended_map = {\n",
|
|
||||||
" diff1[index]:\n",
|
|
||||||
" {\n",
|
|
||||||
" 'destination': transpose_pitch(val, rev_trans) if val != None else None, \n",
|
|
||||||
" 'cent_difference': cent_difference(diff1[index], val) if None not in [diff1[index], val] else None\n",
|
|
||||||
" } for index, val in enumerate(p)}\n",
|
|
||||||
" yield (tuple(expanded_base), tuple(expanded_comp), {\n",
|
|
||||||
" 'transposition': trans,\n",
|
|
||||||
" 'symmetric_difference': symdiff_len, \n",
|
|
||||||
" 'is_directly_tunable': is_directly_tunable(intersection, diff2),\n",
|
|
||||||
" 'movements': base_map | appended_map\n",
|
|
||||||
" },)\n",
|
|
||||||
" yield (tuple(expanded_comp), tuple(expanded_base), {\n",
|
|
||||||
" 'transposition': rev_trans,\n",
|
|
||||||
" 'symmetric_difference': symdiff_len, \n",
|
|
||||||
" 'is_directly_tunable': is_directly_tunable(intersection, diff1),\n",
|
|
||||||
" 'movements': base_map_rev | reverse_movements(appended_map)\n",
|
|
||||||
" },)\n",
|
|
||||||
"\n",
|
|
||||||
"def graph_from_edges(edges):\n",
|
|
||||||
" g = nx.MultiDiGraph()\n",
|
|
||||||
" g.add_edges_from(edges)\n",
|
|
||||||
" return g\n",
|
|
||||||
"\n",
|
|
||||||
"def generate_graph(chord_set, min_symdiff, max_symdiff, max_chord_size):\n",
|
|
||||||
" #chord_set = chords(pitch_set, min_chord_size, max_chord_size)\n",
|
|
||||||
" edge_set = edges(chord_set, min_symdiff, max_symdiff, max_chord_size)\n",
|
|
||||||
" res_graph = graph_from_edges(edge_set)\n",
|
|
||||||
" return res_graph\n",
|
|
||||||
" \n",
|
|
||||||
"def compact_sets(root, m1, m2):\n",
|
|
||||||
" \n",
|
|
||||||
" def branch(r):\n",
|
|
||||||
" b = set()\n",
|
|
||||||
" for d in range(1, len(root)):\n",
|
|
||||||
" for a in [-1, 1]:\n",
|
|
||||||
" b.add((*r[:d], r[d] + a, *r[(d + 1):]))\n",
|
|
||||||
" return b\n",
|
|
||||||
" \n",
|
|
||||||
" def grow(c, p, e):\n",
|
|
||||||
" l = len(c)\n",
|
|
||||||
" if l >= m1 and l <= m2:\n",
|
|
||||||
" #yield tuple(sorted(c, key=hs_array_to_fr))\n",
|
|
||||||
" yield c\n",
|
|
||||||
" if l < m2:\n",
|
|
||||||
" e = set(e)\n",
|
|
||||||
" for b in p:\n",
|
|
||||||
" if b not in e:\n",
|
|
||||||
" e.add(b)\n",
|
|
||||||
" yield from grow((*c, b), p | branch(b), e)\n",
|
|
||||||
" yield from grow((root,), branch(root), set((root,)))\n",
|
|
||||||
"\n",
|
|
||||||
"def display_graph(graph):\n",
|
|
||||||
" show_graph = nx.Graph(graph)\n",
|
|
||||||
" pos = nx.draw_spring(show_graph, node_size=5, width=0.1)\n",
|
|
||||||
" plt.figure(1, figsize=(12,12)) \n",
|
|
||||||
" nx.draw(show_graph, pos, node_size=5, width=0.1)\n",
|
|
||||||
" plt.show()\n",
|
|
||||||
" #plt.savefig('compact_sets.png', dpi=150)\n",
|
|
||||||
"\n",
|
|
||||||
"def path_to_chords(path, start_root):\n",
|
|
||||||
" current_root = start_root\n",
|
|
||||||
" start_chord = tuple(sorted(path[0][0], key=hs_array_to_fr))\n",
|
|
||||||
" chords = ((start_chord, start_chord,),)\n",
|
|
||||||
" for edge in path:\n",
|
|
||||||
" trans = edge[2]['transposition']\n",
|
|
||||||
" movements = edge[2]['movements']\n",
|
|
||||||
" current_root = transpose_pitch(current_root, trans)\n",
|
|
||||||
" current_ref_chord = chords[-1][0]\n",
|
|
||||||
" next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)\n",
|
|
||||||
" next_transposed_chord = tuple(transpose_pitch(pitch, current_root) for pitch in next_ref_chord)\n",
|
|
||||||
" chords += ((next_ref_chord, next_transposed_chord,),)\n",
|
|
||||||
" return tuple(chord[1] for chord in chords)\n",
|
|
||||||
"\n",
|
|
||||||
"def write_chord_sequence(seq, path):\n",
|
|
||||||
" file = open(path, \"w+\")\n",
|
|
||||||
" content = json.dumps(seq)\n",
|
|
||||||
" content = content.replace(\"[[[\", \"[\\n\\t[[\")\n",
|
|
||||||
" content = content.replace(\", [[\", \",\\n\\t[[\")\n",
|
|
||||||
" content = content.replace(\"]]]\", \"]]\\n]\")\n",
|
|
||||||
" file.write(content)\n",
|
|
||||||
" file.close()"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 3,
|
|
||||||
"id": "5b3f30fe-02b2-4a6c-8cb2-100c7d4d0670",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"from random import choice, choices, seed\n",
|
|
||||||
"\n",
|
|
||||||
"# This is for the static version\n",
|
|
||||||
"def stochastic_hamiltonian(graph, start_root):\n",
|
|
||||||
" \n",
|
|
||||||
" def movement_size_weights(edges):\n",
|
|
||||||
" \n",
|
|
||||||
" def max_cent_diff(edge):\n",
|
|
||||||
" res = max([abs(v) for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None])\n",
|
|
||||||
" return res\n",
|
|
||||||
" \n",
|
|
||||||
" def min_cent_diff(edge):\n",
|
|
||||||
" res = [abs(v) for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None]\n",
|
|
||||||
" res.remove(0)\n",
|
|
||||||
" return min(res)\n",
|
|
||||||
" \n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 1000 if ((max_cent_diff(e) < 200) and (min_cent_diff(e)) > 1) else 0\n",
|
|
||||||
"\n",
|
|
||||||
" def hamiltonian_weights(edges):\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if e[1] not in [path_edge[0] for path_edge in path] else 1 / graph.nodes[e[1]]['count']\n",
|
|
||||||
" \n",
|
|
||||||
" def contrary_motion_weights(edges):\n",
|
|
||||||
"\n",
|
|
||||||
" def is_contrary(edge):\n",
|
|
||||||
" cent_diffs = [v for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None]\n",
|
|
||||||
" cent_diffs.sort()\n",
|
|
||||||
" return (cent_diffs[0] < 0) and (cent_diffs[1] == 0) and (cent_diffs[2] > 0)\n",
|
|
||||||
"\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if is_contrary(e) else 1\n",
|
|
||||||
" \n",
|
|
||||||
" def is_directly_tunable_weights(edges):\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if e[2]['is_directly_tunable'] else 0\n",
|
|
||||||
"\n",
|
|
||||||
"\n",
|
|
||||||
" def is_connected_to(edges, chordrefs):\n",
|
|
||||||
" \n",
|
|
||||||
" def is_connected(edge, chordrefs):\n",
|
|
||||||
" trans = edge[2]['transposition']\n",
|
|
||||||
" movements = edge[2]['movements']\n",
|
|
||||||
" tmp_root = transpose_pitch(current_root, trans)\n",
|
|
||||||
" current_ref_chord = chords[-1][0]\n",
|
|
||||||
" next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)\n",
|
|
||||||
" next_transposed_chord = tuple(transpose_pitch(pitch, tmp_root) for pitch in next_ref_chord)\n",
|
|
||||||
" #return min([min([sum(abs(d) for d in collapse_pitch(pitch_difference(c, p))) for p in next_transposed_chord]) for c in chordrefs]) == 0\n",
|
|
||||||
" return min([min([sum(abs(d) for d in pitch_difference(c, p)) for p in next_transposed_chord]) for c in chordrefs]) == 0\n",
|
|
||||||
"\n",
|
|
||||||
" \n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if is_connected(e, chordrefs) else 0\n",
|
|
||||||
"\n",
|
|
||||||
" def voice_crossing_weights(edges):\n",
|
|
||||||
" \n",
|
|
||||||
" def has_voice_crossing(edge):\n",
|
|
||||||
" source = list(edge[0])\n",
|
|
||||||
" ordered_source = sorted(source, key=hs_array_to_fr) \n",
|
|
||||||
" source_order = [ordered_source.index(p) for p in source]\n",
|
|
||||||
" destination = [transpose_pitch(edge[2]['movements'][p]['destination'], edge[2]['transposition']) for p in source]\n",
|
|
||||||
" ordered_destination = sorted(destination, key=hs_array_to_fr)\n",
|
|
||||||
" destination_order = [ordered_destination.index(p) for p in destination]\n",
|
|
||||||
" return source_order != destination_order\n",
|
|
||||||
"\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if not has_voice_crossing(e) else 0\n",
|
|
||||||
"\n",
|
|
||||||
" def is_bass_rooted(chord):\n",
|
|
||||||
" return max([sum(abs(p) for p in collapse_pitch(pitch_difference(chord[0], p))) for p in chord[1:]]) == 1\n",
|
|
||||||
" \n",
|
|
||||||
" current_root = start_root\n",
|
|
||||||
" check_graph = graph.copy()\n",
|
|
||||||
" next_node = choice([node for node in graph.nodes() if is_bass_rooted(node)])\n",
|
|
||||||
" check_graph.remove_node(next_node)\n",
|
|
||||||
" start_chord = tuple(sorted(next_node, key=hs_array_to_fr))\n",
|
|
||||||
" chords = ((start_chord, start_chord,),)\n",
|
|
||||||
" for node in graph.nodes(data=True):\n",
|
|
||||||
" node[1]['count'] = 1\n",
|
|
||||||
" path = []\n",
|
|
||||||
" index = 0\n",
|
|
||||||
" pathRefChords = ((0, 0, 0, 0, 0, 0, 0, 0), (-1, 1, 0, 0, 0, 0, 0, 0), (-2, 0, 1, 0, 0, 0, 0, 0), (-2, 0, 0, 1, 0, 0, 0, 0), (-3, 0, 0, 0, 1, 0, 0, 0), (-3, 0, 0, 0, 0, 1, 0, 0))\n",
|
|
||||||
" while (nx.number_of_nodes(check_graph) > 0) and (len(path) < 50):\n",
|
|
||||||
" out_edges = list(graph.out_edges(next_node, data=True))\n",
|
|
||||||
" factors = [\n",
|
|
||||||
" movement_size_weights(out_edges), \n",
|
|
||||||
" hamiltonian_weights(out_edges), \n",
|
|
||||||
" contrary_motion_weights(out_edges), \n",
|
|
||||||
" is_directly_tunable_weights(out_edges),\n",
|
|
||||||
" voice_crossing_weights(out_edges),\n",
|
|
||||||
" #is_sustained_voice_alt(out_edges, 1, current_root)\n",
|
|
||||||
" is_connected_to(out_edges, (pathRefChords[(len(path) + index) % 6], pathRefChords[(len(path) + index + 1) % 6], pathRefChords[(len(path) + index + 2) % 6]))\n",
|
|
||||||
" #is_connected_to(out_edges, pathRefChords)\n",
|
|
||||||
" ]\n",
|
|
||||||
" index += 1\n",
|
|
||||||
" weights = [prod(a) for a in zip(*factors)]\n",
|
|
||||||
" edge = choices(out_edges, weights=weights)[0]\n",
|
|
||||||
" #edge = random.choice(out_edges)\n",
|
|
||||||
"\n",
|
|
||||||
" trans = edge[2]['transposition']\n",
|
|
||||||
" movements = edge[2]['movements']\n",
|
|
||||||
" current_root = transpose_pitch(current_root, trans)\n",
|
|
||||||
" current_ref_chord = chords[-1][0]\n",
|
|
||||||
" next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)\n",
|
|
||||||
" next_transposed_chord = tuple(transpose_pitch(pitch, current_root) for pitch in next_ref_chord)\n",
|
|
||||||
" chords += ((next_ref_chord, next_transposed_chord,),)\n",
|
|
||||||
" \n",
|
|
||||||
" next_node = edge[1]\n",
|
|
||||||
" node[1]['count'] += 1\n",
|
|
||||||
" path.append(edge)\n",
|
|
||||||
" if next_node in check_graph.nodes:\n",
|
|
||||||
" check_graph.remove_node(next_node)\n",
|
|
||||||
" return tuple(chord[1] for chord in chords)"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 4,
|
|
||||||
"id": "96b6ba04-08fe-4ac2-a0cc-1ac9abe47b41",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"dims = (2, 3, 5, 7, 11, 13, 17, 19)\n",
|
|
||||||
"root = (0, 0, 0, 0, 0, 0, 0, 0)\n",
|
|
||||||
"chord = (root,)\n",
|
|
||||||
"chord_set = compact_sets(root, 3, 3)\n",
|
|
||||||
"#print(len(list(chord_set)))\n",
|
|
||||||
"graph = generate_graph(chord_set, 4, 4, 3)\n",
|
|
||||||
"#len(list(chord_set))"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 5,
|
|
||||||
"id": "7c90b52a-ebc0-4823-bfb2-ad0a696a4bb8",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"ename": "ValueError",
|
|
||||||
"evalue": "Total of weights must be greater than zero",
|
|
||||||
"output_type": "error",
|
|
||||||
"traceback": [
|
|
||||||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
|
||||||
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
|
|
||||||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[5]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m seed(\u001b[32m8729743\u001b[39m) \n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m path = \u001b[43mstochastic_hamiltonian\u001b[49m\u001b[43m(\u001b[49m\u001b[43mgraph\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mroot\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 3\u001b[39m \u001b[38;5;66;03m#for edge in path:\u001b[39;00m\n\u001b[32m 4\u001b[39m \u001b[38;5;66;03m# print(edge)\u001b[39;00m\n\u001b[32m 5\u001b[39m write_chord_sequence(path, \u001b[33m\"\u001b[39m\u001b[33msirens.txt\u001b[39m\u001b[33m\"\u001b[39m)\n",
|
|
||||||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 97\u001b[39m, in \u001b[36mstochastic_hamiltonian\u001b[39m\u001b[34m(graph, start_root)\u001b[39m\n\u001b[32m 95\u001b[39m index += \u001b[32m1\u001b[39m\n\u001b[32m 96\u001b[39m weights = [prod(a) \u001b[38;5;28;01mfor\u001b[39;00m a \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mzip\u001b[39m(*factors)]\n\u001b[32m---> \u001b[39m\u001b[32m97\u001b[39m edge = \u001b[43mchoices\u001b[49m\u001b[43m(\u001b[49m\u001b[43mout_edges\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mweights\u001b[49m\u001b[43m=\u001b[49m\u001b[43mweights\u001b[49m\u001b[43m)\u001b[49m[\u001b[32m0\u001b[39m]\n\u001b[32m 98\u001b[39m \u001b[38;5;66;03m#edge = random.choice(out_edges)\u001b[39;00m\n\u001b[32m 100\u001b[39m trans = edge[\u001b[32m2\u001b[39m][\u001b[33m'\u001b[39m\u001b[33mtransposition\u001b[39m\u001b[33m'\u001b[39m]\n",
|
|
||||||
"\u001b[36mFile \u001b[39m\u001b[32m/usr/lib/python3.13/random.py:487\u001b[39m, in \u001b[36mRandom.choices\u001b[39m\u001b[34m(self, population, weights, cum_weights, k)\u001b[39m\n\u001b[32m 485\u001b[39m total = cum_weights[-\u001b[32m1\u001b[39m] + \u001b[32m0.0\u001b[39m \u001b[38;5;66;03m# convert to float\u001b[39;00m\n\u001b[32m 486\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m total <= \u001b[32m0.0\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m487\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m'\u001b[39m\u001b[33mTotal of weights must be greater than zero\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m 488\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m _isfinite(total):\n\u001b[32m 489\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[33m'\u001b[39m\u001b[33mTotal of weights must be finite\u001b[39m\u001b[33m'\u001b[39m)\n",
|
|
||||||
"\u001b[31mValueError\u001b[39m: Total of weights must be greater than zero"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"seed(8729743) \n",
|
|
||||||
"path = stochastic_hamiltonian(graph, root)\n",
|
|
||||||
"#for edge in path:\n",
|
|
||||||
"# print(edge)\n",
|
|
||||||
"write_chord_sequence(path, \"sirens.txt\")"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 58,
|
|
||||||
"id": "215266dd-643d-4c2b-af7d-a8f8d6875c10",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"data": {
|
|
||||||
"text/plain": [
|
|
||||||
"0"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"execution_count": 58,
|
|
||||||
"metadata": {},
|
|
||||||
"output_type": "execute_result"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"len(list(chord_set))"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 10,
|
|
||||||
"id": "29877cb1-c9ae-4e3c-b492-b7e8646fed2d",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [],
|
|
||||||
"source": [
|
|
||||||
"from random import choice, choices, seed\n",
|
|
||||||
"\n",
|
|
||||||
"def stochastic_hamiltonian(chord_set, start_root, min_symdiff, max_symdiff, max_chord_size):\n",
|
|
||||||
" \n",
|
|
||||||
" def movement_size_weights(edges):\n",
|
|
||||||
" \n",
|
|
||||||
" def max_cent_diff(edge):\n",
|
|
||||||
" res = max([abs(v) for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None])\n",
|
|
||||||
" return res\n",
|
|
||||||
" \n",
|
|
||||||
" def min_cent_diff(edge):\n",
|
|
||||||
" res = [abs(v) for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None]\n",
|
|
||||||
" res.remove(0)\n",
|
|
||||||
" return min(res)\n",
|
|
||||||
" \n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" if ((max_cent_diff(e) < 100) and (min_cent_diff(e)) >= 0):\n",
|
|
||||||
" yield 1000\n",
|
|
||||||
" elif ((max_cent_diff(e) < 200) and (min_cent_diff(e)) >= 0):\n",
|
|
||||||
" yield 10\n",
|
|
||||||
" else:\n",
|
|
||||||
" yield 0\n",
|
|
||||||
"\n",
|
|
||||||
" def hamiltonian_weights(edges):\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if e[1] not in [path_edge[0] for path_edge in path] else 1 #/ graph.nodes[e[1]]['count']\n",
|
|
||||||
" \n",
|
|
||||||
" def contrary_motion_weights(edges):\n",
|
|
||||||
"\n",
|
|
||||||
" def is_contrary(edge):\n",
|
|
||||||
" cent_diffs = [v for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None]\n",
|
|
||||||
" cent_diffs.sort()\n",
|
|
||||||
" return (cent_diffs[0] < 0) and (cent_diffs[1] == 0) and (cent_diffs[2] > 0)\n",
|
|
||||||
" #return (cent_diffs[0] < 0) and (cent_diffs[1] == 0) and (cent_diffs[2] == 0) and (cent_diffs[3] > 0)\n",
|
|
||||||
"\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 100 if is_contrary(e) else 0\n",
|
|
||||||
" \n",
|
|
||||||
" def is_directly_tunable_weights(edges):\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if e[2]['is_directly_tunable'] else 0\n",
|
|
||||||
"\n",
|
|
||||||
" def transposition_weight(edges):\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 1000 if 0 <= hs_array_to_cents(e[2]['transposition']) < 100 else 0\n",
|
|
||||||
"\n",
|
|
||||||
" def is_sustained_voice(edges, voice):\n",
|
|
||||||
" \n",
|
|
||||||
" def is_sustained(edge):\n",
|
|
||||||
" source = list(edge[0])\n",
|
|
||||||
" ordered_source = sorted(source, key=hs_array_to_fr) \n",
|
|
||||||
" destination = [transpose_pitch(edge[2]['movements'][p]['destination'], edge[2]['transposition']) for p in source]\n",
|
|
||||||
" ordered_destination = sorted(destination, key=hs_array_to_fr)\n",
|
|
||||||
" return ordered_source[voice] == ordered_destination[voice]\n",
|
|
||||||
"\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if is_sustained(e) else 0\n",
|
|
||||||
"\n",
|
|
||||||
" def is_sustained_voice_alt(edges, voice, current_root):\n",
|
|
||||||
" \n",
|
|
||||||
" def is_sustained(edge):\n",
|
|
||||||
"\n",
|
|
||||||
" trans = edge[2]['transposition']\n",
|
|
||||||
" movements = edge[2]['movements']\n",
|
|
||||||
" tmp_root = transpose_pitch(current_root, trans)\n",
|
|
||||||
" current_ref_chord = chords[-1][0]\n",
|
|
||||||
" next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)\n",
|
|
||||||
" next_transposed_chord = tuple(transpose_pitch(pitch, tmp_root) for pitch in next_ref_chord)\n",
|
|
||||||
" return chords[-1][1][voice] == next_transposed_chord[voice]\n",
|
|
||||||
"\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if is_sustained(e) else 1\n",
|
|
||||||
"\n",
|
|
||||||
" def is_connected_to(edges, chordrefs):\n",
|
|
||||||
" \n",
|
|
||||||
" def is_connected(edge, chordrefs):\n",
|
|
||||||
" trans = edge[2]['transposition']\n",
|
|
||||||
" movements = edge[2]['movements']\n",
|
|
||||||
" tmp_root = transpose_pitch(current_root, trans)\n",
|
|
||||||
" current_ref_chord = chords[-1][0]\n",
|
|
||||||
" next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)\n",
|
|
||||||
" next_transposed_chord = tuple(transpose_pitch(pitch, tmp_root) for pitch in next_ref_chord)\n",
|
|
||||||
" #return min([min([sum(abs(d) for d in collapse_pitch(pitch_difference(c, p))) for p in next_transposed_chord]) for c in chordrefs]) == 0\n",
|
|
||||||
" return min([min([sum(abs(d) for d in pitch_difference(c, p)) for p in next_transposed_chord]) for c in chordrefs]) == 0\n",
|
|
||||||
"\n",
|
|
||||||
" \n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if is_connected(e, chordrefs) else 0\n",
|
|
||||||
"\n",
|
|
||||||
" def voice_crossing_weights(edges):\n",
|
|
||||||
" \n",
|
|
||||||
" def has_voice_crossing(edge):\n",
|
|
||||||
" source = list(edge[0])\n",
|
|
||||||
" ordered_source = sorted(source, key=hs_array_to_fr) \n",
|
|
||||||
" source_order = [ordered_source.index(p) for p in source]\n",
|
|
||||||
" destination = [transpose_pitch(edge[2]['movements'][p]['destination'], edge[2]['transposition']) for p in source]\n",
|
|
||||||
" ordered_destination = sorted(destination, key=hs_array_to_fr)\n",
|
|
||||||
" destination_order = [ordered_destination.index(p) for p in destination]\n",
|
|
||||||
" return source_order != destination_order\n",
|
|
||||||
"\n",
|
|
||||||
" for e in edges:\n",
|
|
||||||
" yield 10 if not has_voice_crossing(e) else 0\n",
|
|
||||||
"\n",
|
|
||||||
" def dca_weight(edges, last_chords):\n",
|
|
||||||
" for edge in edges:\n",
|
|
||||||
" source = list(edge[0])\n",
|
|
||||||
" ordered_source = sorted(source, key=hs_array_to_fr) \n",
|
|
||||||
" source_order = [ordered_source.index(p) for p in source]\n",
|
|
||||||
" destination = [transpose_pitch(edge[2]['movements'][p]['destination'], edge[2]['transposition']) for p in source]\n",
|
|
||||||
" ordered_destination = tuple(sorted(destination, key=hs_array_to_fr))\n",
|
|
||||||
" test_sequence = tuple(zip(*(last_chords + (ordered_destination, ))))\n",
|
|
||||||
" #print('here')\n",
|
|
||||||
" #print(test_sequence)\n",
|
|
||||||
" if len(test_sequence[0]) < 4:\n",
|
|
||||||
" yield 10\n",
|
|
||||||
" else:\n",
|
|
||||||
" if len(set(test_sequence[0][-2:])) == 1 or len(set(test_sequence[1][-2:])) == 1 or len(set(test_sequence[2][-2:])) == 1:\n",
|
|
||||||
" yield 0\n",
|
|
||||||
" else:\n",
|
|
||||||
" yield 10\n",
|
|
||||||
"\n",
|
|
||||||
" def is_bass_rooted(chord):\n",
|
|
||||||
" return max([sum(abs(p) for p in collapse_pitch(pitch_difference(chord[0], p))) for p in chord[1:]]) == 1\n",
|
|
||||||
"\n",
|
|
||||||
" def is_directly_tunable(intersection, diff):\n",
|
|
||||||
" # this only works for now when intersection if one element - need to fix that\n",
|
|
||||||
" return max([sum(abs(p) for p in collapse_pitch(pitch_difference(d, list(intersection)[0]))) for d in diff]) == 1\n",
|
|
||||||
"\n",
|
|
||||||
" def gen_edges(source, candidates, min_symdiff, max_symdiff, max_chord_size, ostinato_ref):\n",
|
|
||||||
" for target in candidates:\n",
|
|
||||||
" [expanded_source, expanded_target] = [expand_chord(chord) for chord in [source, target]]\n",
|
|
||||||
" edges = []\n",
|
|
||||||
" expanded_source_with_ostinato_ref = expanded_source + ostinato_ref\n",
|
|
||||||
" #print(expanded_source + ostinato_ref)\n",
|
|
||||||
" transpositions = set(pitch_difference(pair[0], pair[1]) for pair in set(product(expanded_source, expanded_target)))\n",
|
|
||||||
" #print(transpositions)\n",
|
|
||||||
" for trans in transpositions:\n",
|
|
||||||
" expanded_target_transposed = transpose_chord(expanded_target, trans)\n",
|
|
||||||
" intersection = set(expanded_source) & set(expanded_target_transposed)\n",
|
|
||||||
" symdiff_len = sum([len(chord) - len(intersection) for chord in [expanded_source, expanded_target_transposed]])\n",
|
|
||||||
" if (min_symdiff <= symdiff_len <= max_symdiff):\n",
|
|
||||||
" rev_trans = tuple(t * -1 for t in trans)\n",
|
|
||||||
" [diff1, diff2] = [list(set(chord) - intersection) for chord in [expanded_source, expanded_target_transposed]]\n",
|
|
||||||
" base_map = {val: {'destination':transpose_pitch(val, rev_trans), 'cent_difference': 0} for val in intersection}\n",
|
|
||||||
" #base_map_rev = reverse_movements(base_map)\n",
|
|
||||||
" maps = []\n",
|
|
||||||
" diff1 += [None] * (max_chord_size - len(diff1) - len(intersection))\n",
|
|
||||||
" perms = [list(perm) + [None] * (max_chord_size - len(perm) - len(intersection)) for perm in set(permutations(diff2))]\n",
|
|
||||||
" for p in perms:\n",
|
|
||||||
" appended_map = {\n",
|
|
||||||
" diff1[index]:\n",
|
|
||||||
" {\n",
|
|
||||||
" 'destination': transpose_pitch(val, rev_trans) if val != None else None, \n",
|
|
||||||
" 'cent_difference': cent_difference(diff1[index], val) if None not in [diff1[index], val] else None\n",
|
|
||||||
" } for index, val in enumerate(p)}\n",
|
|
||||||
" yield (tuple(expanded_source), tuple(expanded_target), {\n",
|
|
||||||
" 'transposition': trans,\n",
|
|
||||||
" 'symmetric_difference': symdiff_len, \n",
|
|
||||||
" 'is_directly_tunable': is_directly_tunable(intersection, diff2),\n",
|
|
||||||
" 'movements': base_map | appended_map\n",
|
|
||||||
" },)\n",
|
|
||||||
"\n",
|
|
||||||
" current_root = start_root\n",
|
|
||||||
" #weighted_chord_set = {\n",
|
|
||||||
" # chord:\n",
|
|
||||||
" # {\n",
|
|
||||||
" # 'weight': 10\n",
|
|
||||||
" # } for index, chord in enumerate(chord_set)}\n",
|
|
||||||
" next_chord = tuple(sorted(expand_chord(choice(chord_set)), key=hs_array_to_fr))\n",
|
|
||||||
" #tuple(sorted(next_node, key=hs_array_to_fr))\n",
|
|
||||||
" print(next_chord)\n",
|
|
||||||
" #weighted_chord_set[next_chord]['weight'] = 1;\n",
|
|
||||||
" chords = ((next_chord, next_chord,),)\n",
|
|
||||||
" last_chords = (next_chord,)\n",
|
|
||||||
" path = []\n",
|
|
||||||
" index = 0\n",
|
|
||||||
" pathRefChords = ((0, 0, 0, 0, 0, 0, 0), (-2, 0, 1, 0, 0, 0, 0), (-3, 0, 0, 0, 1, 0, 0), (-1, 1, 0, 0, 0, 0, 0), (-3, 0, 0, 0, 0, 1, 0), (-2, 0, 0, 1, 0, 0, 0))\n",
|
|
||||||
" #pathRefChords = ((0, 0, 0, 0, 0, 0), (-1, 1, 0, 0, 0, 0), (-2, 0, 1, 0, 0, 0), (-2, 0, 0, 1, 0, 0), (-3, 0, 0, 0, 1, 0), (-3, 0, 0, 0, 0, 1))\n",
|
|
||||||
" while (len(path) < 100):\n",
|
|
||||||
" ostinato_ref = (pathRefChords[len(path)%6],)\n",
|
|
||||||
" edges = list(gen_edges(next_chord, chord_set, min_symdiff, max_symdiff, max_chord_size, ostinato_ref))\n",
|
|
||||||
" #print(edges)\n",
|
|
||||||
" factors = [\n",
|
|
||||||
" movement_size_weights(edges), \n",
|
|
||||||
" hamiltonian_weights(edges), \n",
|
|
||||||
" #contrary_motion_weights(edges), \n",
|
|
||||||
" is_directly_tunable_weights(edges),\n",
|
|
||||||
" voice_crossing_weights(edges),\n",
|
|
||||||
" #dca_weight(edges, last_chords),\n",
|
|
||||||
" is_sustained_voice_alt(edges, choice([0, 1, 2]), current_root),\n",
|
|
||||||
" #is_connected_to(edges, (pathRefChords[len(path)%6],))\n",
|
|
||||||
" #is_connected_to(edges, (pathRefChords[(len(path) + index) % 6], pathRefChords[(len(path) + index + 1) % 6], pathRefChords[(len(path) + index + 2) % 6]))\n",
|
|
||||||
" #is_connected_to(edges, pathRefChords)\n",
|
|
||||||
" ]\n",
|
|
||||||
" index += 1\n",
|
|
||||||
" weights = [prod(a) for a in zip(*factors)]\n",
|
|
||||||
" edge = choices(edges, weights=weights)[0]\n",
|
|
||||||
" #edge = random.choice(out_edges)\n",
|
|
||||||
" #print(edge)\n",
|
|
||||||
"\n",
|
|
||||||
" trans = edge[2]['transposition']\n",
|
|
||||||
" movements = edge[2]['movements']\n",
|
|
||||||
" current_root = transpose_pitch(current_root, trans)\n",
|
|
||||||
" current_ref_chord = chords[-1][0]\n",
|
|
||||||
" next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)\n",
|
|
||||||
" next_transposed_chord = tuple(transpose_pitch(pitch, current_root) for pitch in next_ref_chord)\n",
|
|
||||||
" chords += ((next_ref_chord, next_transposed_chord,),)\n",
|
|
||||||
" \n",
|
|
||||||
" next_chord = edge[1]\n",
|
|
||||||
" #node[1]['count'] += 1\n",
|
|
||||||
"\n",
|
|
||||||
" last_chords = last_chords + (next_chord,)\n",
|
|
||||||
" if len(last_chords) > 2:\n",
|
|
||||||
" last_chords = last_chords[-2:]\n",
|
|
||||||
" #print(last_chords)\n",
|
|
||||||
" \n",
|
|
||||||
" path.append(edge)\n",
|
|
||||||
" #if next_node in check_graph.nodes:\n",
|
|
||||||
" # check_graph.remove_node(next_node)\n",
|
|
||||||
" return tuple(chord[1] for chord in chords)"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 7,
|
|
||||||
"id": "84095118-af45-49c8-8b5c-6b51d9432edc",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"((0, 0, 0, 0, 0, 0, 0), (-2, 0, 1, 0, 0, 0, 0), (-3, 1, 1, 0, 0, 0, 0))\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"seed(872984353450043) \n",
|
|
||||||
"dims = (2, 3, 5, 7, 11, 13, 17)\n",
|
|
||||||
"root = (0, 0, 0, 0, 0, 0, 0)\n",
|
|
||||||
"chord = (root,)\n",
|
|
||||||
"chord_set = compact_sets(root, 3, 3)\n",
|
|
||||||
"path = stochastic_hamiltonian(list(chord_set), root, 4, 4, 3)\n",
|
|
||||||
"#for edge in path:\n",
|
|
||||||
"# print(edge)\n",
|
|
||||||
"write_chord_sequence(path, \"sirens.txt\")"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"cell_type": "code",
|
|
||||||
"execution_count": 18,
|
|
||||||
"id": "1123dc10-17a4-449f-bb97-d959b1a2ee0c",
|
|
||||||
"metadata": {},
|
|
||||||
"outputs": [
|
|
||||||
{
|
|
||||||
"name": "stdout",
|
|
||||||
"output_type": "stream",
|
|
||||||
"text": [
|
|
||||||
"((0, 0, 0, 0), (4, -1, -1, 0), (7, -1, -1, -1), (6, -2, -1, 0), (3, 0, -1, 0))\n"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source": [
|
|
||||||
"seed(872984353450043) \n",
|
|
||||||
"dims = (2, 3, 5, 7)\n",
|
|
||||||
"root = (0, 0, 0, 0)\n",
|
|
||||||
"chord = (root,)\n",
|
|
||||||
"chord_set = compact_sets(root, 5, 5)\n",
|
|
||||||
"path = stochastic_hamiltonian(list(chord_set), root, 4, 4, 5)\n",
|
|
||||||
"#for edge in path:\n",
|
|
||||||
"# print(edge)\n",
|
|
||||||
"write_chord_sequence(path, \"sirens.txt\")"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"attachments": {},
|
|
||||||
"cell_type": "markdown",
|
|
||||||
"id": "e16fc2f3-d8f4-4d70-9f33-403f9639f4df",
|
|
||||||
"metadata": {},
|
|
||||||
"source": [
|
|
||||||
"dims = (2, 3, 5, 7, 11, 13, 17)\n",
|
|
||||||
"root = (0, 0, 0, 0, 0, 0, 0)\n",
|
|
||||||
"chord = (root,)\n",
|
|
||||||
"chord_set = compact_sets(root, 3, 3)\n",
|
|
||||||
"#print(len(list(chord_set)))\n",
|
|
||||||
"reduced_chord_set = tuple()\n",
|
|
||||||
"for chord in chord_set:\n",
|
|
||||||
" c_flag = False\n",
|
|
||||||
" for p1, p2 in combinations(collapse_chord(chord), 2):\n",
|
|
||||||
" diff = pitch_difference(p1, p2)\n",
|
|
||||||
" print(diff)\n",
|
|
||||||
" if diff in ((0, 1, 0, 0, 0, 0, 0), (0, 0, 1, 0, 0, 0, 0), (0, 0, 0, 1, 0, 0, 0), (0, -1, 0, 0, 0, 0, 0), (0, 0, -1, 0, 0, 0, 0), (0, 0, 0, -1, 0, 0, 0)) and not c_flag:\n",
|
|
||||||
" #if (abs(p1[1] - p2[1]) == 1 or abs(p1[2] - p2[2]) == 1 or abs(p1[3] - p2[3]) == 1) and not c_flag:\n",
|
|
||||||
" c_flag = True\n",
|
|
||||||
" #break\n",
|
|
||||||
" if c_flag:\n",
|
|
||||||
" reduced_chord_set += (chord,)\n",
|
|
||||||
" \n",
|
|
||||||
"\n",
|
|
||||||
"len(reduced_chord_set)\n",
|
|
||||||
"\n",
|
|
||||||
"pitch_difference("
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"metadata": {
|
|
||||||
"kernelspec": {
|
|
||||||
"display_name": "Python 3 (ipykernel)",
|
|
||||||
"language": "python",
|
|
||||||
"name": "python3"
|
|
||||||
},
|
|
||||||
"language_info": {
|
|
||||||
"codemirror_mode": {
|
|
||||||
"name": "ipython",
|
|
||||||
"version": 3
|
|
||||||
},
|
|
||||||
"file_extension": ".py",
|
|
||||||
"mimetype": "text/x-python",
|
|
||||||
"name": "python",
|
|
||||||
"nbconvert_exporter": "python",
|
|
||||||
"pygments_lexer": "ipython3",
|
|
||||||
"version": "3.13.11"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nbformat": 4,
|
|
||||||
"nbformat_minor": 5
|
|
||||||
}
|
|
||||||
|
|
@ -1,587 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
# coding: utf-8
|
|
||||||
|
|
||||||
# In[1]:
|
|
||||||
|
|
||||||
|
|
||||||
from itertools import chain, combinations, permutations, product, pairwise
|
|
||||||
from math import prod, log
|
|
||||||
from copy import deepcopy
|
|
||||||
import networkx as nx
|
|
||||||
from fractions import Fraction
|
|
||||||
import json
|
|
||||||
from operator import add
|
|
||||||
|
|
||||||
def hs_array_to_fr(hs_array):
|
|
||||||
return prod([pow(dims[d], hs_array[d]) for d in range(len(dims))])
|
|
||||||
|
|
||||||
def hs_array_to_cents(hs_array):
|
|
||||||
return (1200 * log(hs_array_to_fr(hs_array), 2))
|
|
||||||
|
|
||||||
def expand_pitch(hs_array):
|
|
||||||
expanded_pitch = list(hs_array)
|
|
||||||
frequency_ratio = hs_array_to_fr(hs_array)
|
|
||||||
if frequency_ratio < 1:
|
|
||||||
while frequency_ratio < 1:
|
|
||||||
frequency_ratio *= 2
|
|
||||||
expanded_pitch[0] += 1
|
|
||||||
elif frequency_ratio >= 2:
|
|
||||||
while frequency_ratio >= 2:
|
|
||||||
frequency_ratio *= 1/2
|
|
||||||
expanded_pitch[0] += -1
|
|
||||||
return tuple(expanded_pitch)
|
|
||||||
|
|
||||||
def expand_chord(chord):
|
|
||||||
return tuple(expand_pitch(p) for p in chord)
|
|
||||||
|
|
||||||
def collapse_pitch(hs_array):
|
|
||||||
collapsed_pitch = list(hs_array)
|
|
||||||
collapsed_pitch[0] = 0
|
|
||||||
return tuple(collapsed_pitch)
|
|
||||||
|
|
||||||
def collapse_chord(chord):
|
|
||||||
return tuple(collapse_pitch(p) for p in chord)
|
|
||||||
|
|
||||||
def transpose_pitch(pitch, trans):
|
|
||||||
return tuple(map(add, pitch, trans))
|
|
||||||
|
|
||||||
def transpose_chord(chord, trans):
|
|
||||||
return tuple(transpose_pitch(p, trans) for p in chord)
|
|
||||||
|
|
||||||
def cent_difference(hs_array1, hs_array2):
|
|
||||||
return hs_array_to_cents(hs_array2) - hs_array_to_cents(hs_array1)
|
|
||||||
|
|
||||||
def pitch_difference(hs_array1, hs_array2):
|
|
||||||
return transpose_pitch(hs_array1, [p * -1 for p in hs_array2])
|
|
||||||
|
|
||||||
def edges(chords, min_symdiff, max_symdiff, max_chord_size):
|
|
||||||
|
|
||||||
def reverse_movements(movements):
|
|
||||||
return {value['destination']:{'destination':key, 'cent_difference':value['cent_difference'] * -1} for key, value in movements.items()}
|
|
||||||
|
|
||||||
def is_directly_tunable(intersection, diff):
|
|
||||||
# this only works for now when intersection if one element - need to fix that
|
|
||||||
return max([sum(abs(p) for p in collapse_pitch(pitch_difference(d, list(intersection)[0]))) for d in diff]) == 1
|
|
||||||
|
|
||||||
for combination in combinations(chords, 2):
|
|
||||||
[expanded_base, expanded_comp] = [expand_chord(chord) for chord in combination]
|
|
||||||
edges = []
|
|
||||||
transpositions = set(pitch_difference(pair[0], pair[1]) for pair in set(product(expanded_base, expanded_comp)))
|
|
||||||
for trans in transpositions:
|
|
||||||
expanded_comp_transposed = transpose_chord(expanded_comp, trans)
|
|
||||||
intersection = set(expanded_base) & set(expanded_comp_transposed)
|
|
||||||
symdiff_len = sum([len(chord) - len(intersection) for chord in [expanded_base, expanded_comp_transposed]])
|
|
||||||
if (min_symdiff <= symdiff_len <= max_symdiff):
|
|
||||||
rev_trans = tuple(t * -1 for t in trans)
|
|
||||||
[diff1, diff2] = [list(set(chord) - intersection) for chord in [expanded_base, expanded_comp_transposed]]
|
|
||||||
base_map = {val: {'destination':transpose_pitch(val, rev_trans), 'cent_difference': 0} for val in intersection}
|
|
||||||
base_map_rev = reverse_movements(base_map)
|
|
||||||
maps = []
|
|
||||||
diff1 += [None] * (max_chord_size - len(diff1) - len(intersection))
|
|
||||||
perms = [list(perm) + [None] * (max_chord_size - len(perm) - len(intersection)) for perm in set(permutations(diff2))]
|
|
||||||
for p in perms:
|
|
||||||
appended_map = {
|
|
||||||
diff1[index]:
|
|
||||||
{
|
|
||||||
'destination': transpose_pitch(val, rev_trans) if val != None else None,
|
|
||||||
'cent_difference': cent_difference(diff1[index], val) if None not in [diff1[index], val] else None
|
|
||||||
} for index, val in enumerate(p)}
|
|
||||||
yield (tuple(expanded_base), tuple(expanded_comp), {
|
|
||||||
'transposition': trans,
|
|
||||||
'symmetric_difference': symdiff_len,
|
|
||||||
'is_directly_tunable': is_directly_tunable(intersection, diff2),
|
|
||||||
'movements': base_map | appended_map
|
|
||||||
},)
|
|
||||||
yield (tuple(expanded_comp), tuple(expanded_base), {
|
|
||||||
'transposition': rev_trans,
|
|
||||||
'symmetric_difference': symdiff_len,
|
|
||||||
'is_directly_tunable': is_directly_tunable(intersection, diff1),
|
|
||||||
'movements': base_map_rev | reverse_movements(appended_map)
|
|
||||||
},)
|
|
||||||
|
|
||||||
def graph_from_edges(edges):
|
|
||||||
g = nx.MultiDiGraph()
|
|
||||||
g.add_edges_from(edges)
|
|
||||||
return g
|
|
||||||
|
|
||||||
def generate_graph(chord_set, min_symdiff, max_symdiff, max_chord_size):
|
|
||||||
#chord_set = chords(pitch_set, min_chord_size, max_chord_size)
|
|
||||||
edge_set = edges(chord_set, min_symdiff, max_symdiff, max_chord_size)
|
|
||||||
res_graph = graph_from_edges(edge_set)
|
|
||||||
return res_graph
|
|
||||||
|
|
||||||
def compact_sets(root, m1, m2):
|
|
||||||
|
|
||||||
def branch(r):
|
|
||||||
b = set()
|
|
||||||
for d in range(1, len(root)):
|
|
||||||
for a in [-1, 1]:
|
|
||||||
b.add((*r[:d], r[d] + a, *r[(d + 1):]))
|
|
||||||
return b
|
|
||||||
|
|
||||||
def grow(c, p, e):
|
|
||||||
l = len(c)
|
|
||||||
if l >= m1 and l <= m2:
|
|
||||||
#yield tuple(sorted(c, key=hs_array_to_fr))
|
|
||||||
yield c
|
|
||||||
if l < m2:
|
|
||||||
e = set(e)
|
|
||||||
for b in p:
|
|
||||||
if b not in e:
|
|
||||||
e.add(b)
|
|
||||||
yield from grow((*c, b), p | branch(b), e)
|
|
||||||
yield from grow((root,), branch(root), set((root,)))
|
|
||||||
|
|
||||||
def display_graph(graph):
|
|
||||||
show_graph = nx.Graph(graph)
|
|
||||||
pos = nx.draw_spring(show_graph, node_size=5, width=0.1)
|
|
||||||
plt.figure(1, figsize=(12,12))
|
|
||||||
nx.draw(show_graph, pos, node_size=5, width=0.1)
|
|
||||||
plt.show()
|
|
||||||
#plt.savefig('compact_sets.png', dpi=150)
|
|
||||||
|
|
||||||
def path_to_chords(path, start_root):
|
|
||||||
current_root = start_root
|
|
||||||
start_chord = tuple(sorted(path[0][0], key=hs_array_to_fr))
|
|
||||||
chords = ((start_chord, start_chord,),)
|
|
||||||
for edge in path:
|
|
||||||
trans = edge[2]['transposition']
|
|
||||||
movements = edge[2]['movements']
|
|
||||||
current_root = transpose_pitch(current_root, trans)
|
|
||||||
current_ref_chord = chords[-1][0]
|
|
||||||
next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)
|
|
||||||
next_transposed_chord = tuple(transpose_pitch(pitch, current_root) for pitch in next_ref_chord)
|
|
||||||
chords += ((next_ref_chord, next_transposed_chord,),)
|
|
||||||
return tuple(chord[1] for chord in chords)
|
|
||||||
|
|
||||||
def write_chord_sequence(seq, path):
|
|
||||||
file = open(path, "w+")
|
|
||||||
content = json.dumps(seq)
|
|
||||||
content = content.replace("[[[", "[\n\t[[")
|
|
||||||
content = content.replace(", [[", ",\n\t[[")
|
|
||||||
content = content.replace("]]]", "]]\n]")
|
|
||||||
file.write(content)
|
|
||||||
file.close()
|
|
||||||
|
|
||||||
|
|
||||||
# In[3]:
|
|
||||||
|
|
||||||
|
|
||||||
from random import choice, choices, seed
|
|
||||||
|
|
||||||
# This is for the static version
|
|
||||||
def stochastic_hamiltonian(graph, start_root):
|
|
||||||
|
|
||||||
def movement_size_weights(edges):
|
|
||||||
|
|
||||||
def max_cent_diff(edge):
|
|
||||||
res = max([abs(v) for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None])
|
|
||||||
return res
|
|
||||||
|
|
||||||
def min_cent_diff(edge):
|
|
||||||
res = [abs(v) for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None]
|
|
||||||
res.remove(0)
|
|
||||||
return min(res)
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 1000 if ((max_cent_diff(e) < 200) and (min_cent_diff(e)) > 1) else 0
|
|
||||||
|
|
||||||
def hamiltonian_weights(edges):
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if e[1] not in [path_edge[0] for path_edge in path] else 1 / graph.nodes[e[1]]['count']
|
|
||||||
|
|
||||||
def contrary_motion_weights(edges):
|
|
||||||
|
|
||||||
def is_contrary(edge):
|
|
||||||
cent_diffs = [v for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None]
|
|
||||||
cent_diffs.sort()
|
|
||||||
return (cent_diffs[0] < 0) and (cent_diffs[1] == 0) and (cent_diffs[2] > 0)
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if is_contrary(e) else 1
|
|
||||||
|
|
||||||
def is_directly_tunable_weights(edges):
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if e[2]['is_directly_tunable'] else 0
|
|
||||||
|
|
||||||
|
|
||||||
def is_connected_to(edges, chordrefs):
|
|
||||||
|
|
||||||
def is_connected(edge, chordrefs):
|
|
||||||
trans = edge[2]['transposition']
|
|
||||||
movements = edge[2]['movements']
|
|
||||||
tmp_root = transpose_pitch(current_root, trans)
|
|
||||||
current_ref_chord = chords[-1][0]
|
|
||||||
next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)
|
|
||||||
next_transposed_chord = tuple(transpose_pitch(pitch, tmp_root) for pitch in next_ref_chord)
|
|
||||||
#return min([min([sum(abs(d) for d in collapse_pitch(pitch_difference(c, p))) for p in next_transposed_chord]) for c in chordrefs]) == 0
|
|
||||||
return min([min([sum(abs(d) for d in pitch_difference(c, p)) for p in next_transposed_chord]) for c in chordrefs]) == 0
|
|
||||||
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if is_connected(e, chordrefs) else 0
|
|
||||||
|
|
||||||
def voice_crossing_weights(edges):
|
|
||||||
|
|
||||||
def has_voice_crossing(edge):
|
|
||||||
source = list(edge[0])
|
|
||||||
ordered_source = sorted(source, key=hs_array_to_fr)
|
|
||||||
source_order = [ordered_source.index(p) for p in source]
|
|
||||||
destination = [transpose_pitch(edge[2]['movements'][p]['destination'], edge[2]['transposition']) for p in source]
|
|
||||||
ordered_destination = sorted(destination, key=hs_array_to_fr)
|
|
||||||
destination_order = [ordered_destination.index(p) for p in destination]
|
|
||||||
return source_order != destination_order
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if not has_voice_crossing(e) else 0
|
|
||||||
|
|
||||||
def is_bass_rooted(chord):
|
|
||||||
return max([sum(abs(p) for p in collapse_pitch(pitch_difference(chord[0], p))) for p in chord[1:]]) == 1
|
|
||||||
|
|
||||||
current_root = start_root
|
|
||||||
check_graph = graph.copy()
|
|
||||||
next_node = choice([node for node in graph.nodes() if is_bass_rooted(node)])
|
|
||||||
check_graph.remove_node(next_node)
|
|
||||||
start_chord = tuple(sorted(next_node, key=hs_array_to_fr))
|
|
||||||
chords = ((start_chord, start_chord,),)
|
|
||||||
for node in graph.nodes(data=True):
|
|
||||||
node[1]['count'] = 1
|
|
||||||
path = []
|
|
||||||
index = 0
|
|
||||||
pathRefChords = ((0, 0, 0, 0, 0, 0, 0, 0), (-1, 1, 0, 0, 0, 0, 0, 0), (-2, 0, 1, 0, 0, 0, 0, 0), (-2, 0, 0, 1, 0, 0, 0, 0), (-3, 0, 0, 0, 1, 0, 0, 0), (-3, 0, 0, 0, 0, 1, 0, 0))
|
|
||||||
while (nx.number_of_nodes(check_graph) > 0) and (len(path) < 50):
|
|
||||||
out_edges = list(graph.out_edges(next_node, data=True))
|
|
||||||
factors = [
|
|
||||||
movement_size_weights(out_edges),
|
|
||||||
hamiltonian_weights(out_edges),
|
|
||||||
contrary_motion_weights(out_edges),
|
|
||||||
is_directly_tunable_weights(out_edges),
|
|
||||||
voice_crossing_weights(out_edges),
|
|
||||||
#is_sustained_voice_alt(out_edges, 1, current_root)
|
|
||||||
is_connected_to(out_edges, (pathRefChords[(len(path) + index) % 6], pathRefChords[(len(path) + index + 1) % 6], pathRefChords[(len(path) + index + 2) % 6]))
|
|
||||||
#is_connected_to(out_edges, pathRefChords)
|
|
||||||
]
|
|
||||||
index += 1
|
|
||||||
weights = [prod(a) for a in zip(*factors)]
|
|
||||||
edge = choices(out_edges, weights=weights)[0]
|
|
||||||
#edge = random.choice(out_edges)
|
|
||||||
|
|
||||||
trans = edge[2]['transposition']
|
|
||||||
movements = edge[2]['movements']
|
|
||||||
current_root = transpose_pitch(current_root, trans)
|
|
||||||
current_ref_chord = chords[-1][0]
|
|
||||||
next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)
|
|
||||||
next_transposed_chord = tuple(transpose_pitch(pitch, current_root) for pitch in next_ref_chord)
|
|
||||||
chords += ((next_ref_chord, next_transposed_chord,),)
|
|
||||||
|
|
||||||
next_node = edge[1]
|
|
||||||
node[1]['count'] += 1
|
|
||||||
path.append(edge)
|
|
||||||
if next_node in check_graph.nodes:
|
|
||||||
check_graph.remove_node(next_node)
|
|
||||||
return tuple(chord[1] for chord in chords)
|
|
||||||
|
|
||||||
|
|
||||||
# In[4]:
|
|
||||||
|
|
||||||
|
|
||||||
dims = (2, 3, 5, 7, 11, 13, 17, 19)
|
|
||||||
root = (0, 0, 0, 0, 0, 0, 0, 0)
|
|
||||||
chord = (root,)
|
|
||||||
chord_set = compact_sets(root, 3, 3)
|
|
||||||
#print(len(list(chord_set)))
|
|
||||||
graph = generate_graph(chord_set, 4, 4, 3)
|
|
||||||
#len(list(chord_set))
|
|
||||||
|
|
||||||
|
|
||||||
# In[5]:
|
|
||||||
|
|
||||||
|
|
||||||
seed(8729743)
|
|
||||||
path = stochastic_hamiltonian(graph, root)
|
|
||||||
#for edge in path:
|
|
||||||
# print(edge)
|
|
||||||
write_chord_sequence(path, "sirens.txt")
|
|
||||||
|
|
||||||
|
|
||||||
# In[58]:
|
|
||||||
|
|
||||||
|
|
||||||
len(list(chord_set))
|
|
||||||
|
|
||||||
|
|
||||||
# In[10]:
|
|
||||||
|
|
||||||
|
|
||||||
from random import choice, choices, seed
|
|
||||||
|
|
||||||
def stochastic_hamiltonian(chord_set, start_root, min_symdiff, max_symdiff, max_chord_size):
|
|
||||||
|
|
||||||
def movement_size_weights(edges):
|
|
||||||
|
|
||||||
def max_cent_diff(edge):
|
|
||||||
res = max([abs(v) for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None])
|
|
||||||
return res
|
|
||||||
|
|
||||||
def min_cent_diff(edge):
|
|
||||||
res = [abs(v) for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None]
|
|
||||||
res.remove(0)
|
|
||||||
return min(res)
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
if ((max_cent_diff(e) < 100) and (min_cent_diff(e)) >= 0):
|
|
||||||
yield 1000
|
|
||||||
elif ((max_cent_diff(e) < 200) and (min_cent_diff(e)) >= 0):
|
|
||||||
yield 10
|
|
||||||
else:
|
|
||||||
yield 0
|
|
||||||
|
|
||||||
def hamiltonian_weights(edges):
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if e[1] not in [path_edge[0] for path_edge in path] else 1 #/ graph.nodes[e[1]]['count']
|
|
||||||
|
|
||||||
def contrary_motion_weights(edges):
|
|
||||||
|
|
||||||
def is_contrary(edge):
|
|
||||||
cent_diffs = [v for val in edge[2]['movements'].values() if (v:=val['cent_difference']) is not None]
|
|
||||||
cent_diffs.sort()
|
|
||||||
return (cent_diffs[0] < 0) and (cent_diffs[1] == 0) and (cent_diffs[2] > 0)
|
|
||||||
#return (cent_diffs[0] < 0) and (cent_diffs[1] == 0) and (cent_diffs[2] == 0) and (cent_diffs[3] > 0)
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 100 if is_contrary(e) else 0
|
|
||||||
|
|
||||||
def is_directly_tunable_weights(edges):
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if e[2]['is_directly_tunable'] else 0
|
|
||||||
|
|
||||||
def transposition_weight(edges):
|
|
||||||
for e in edges:
|
|
||||||
yield 1000 if 0 <= hs_array_to_cents(e[2]['transposition']) < 100 else 0
|
|
||||||
|
|
||||||
def is_sustained_voice(edges, voice):
|
|
||||||
|
|
||||||
def is_sustained(edge):
|
|
||||||
source = list(edge[0])
|
|
||||||
ordered_source = sorted(source, key=hs_array_to_fr)
|
|
||||||
destination = [transpose_pitch(edge[2]['movements'][p]['destination'], edge[2]['transposition']) for p in source]
|
|
||||||
ordered_destination = sorted(destination, key=hs_array_to_fr)
|
|
||||||
return ordered_source[voice] == ordered_destination[voice]
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if is_sustained(e) else 0
|
|
||||||
|
|
||||||
def is_sustained_voice_alt(edges, voice, current_root):
|
|
||||||
|
|
||||||
def is_sustained(edge):
|
|
||||||
|
|
||||||
trans = edge[2]['transposition']
|
|
||||||
movements = edge[2]['movements']
|
|
||||||
tmp_root = transpose_pitch(current_root, trans)
|
|
||||||
current_ref_chord = chords[-1][0]
|
|
||||||
next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)
|
|
||||||
next_transposed_chord = tuple(transpose_pitch(pitch, tmp_root) for pitch in next_ref_chord)
|
|
||||||
return chords[-1][1][voice] == next_transposed_chord[voice]
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if is_sustained(e) else 1
|
|
||||||
|
|
||||||
def is_connected_to(edges, chordrefs):
|
|
||||||
|
|
||||||
def is_connected(edge, chordrefs):
|
|
||||||
trans = edge[2]['transposition']
|
|
||||||
movements = edge[2]['movements']
|
|
||||||
tmp_root = transpose_pitch(current_root, trans)
|
|
||||||
current_ref_chord = chords[-1][0]
|
|
||||||
next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)
|
|
||||||
next_transposed_chord = tuple(transpose_pitch(pitch, tmp_root) for pitch in next_ref_chord)
|
|
||||||
#return min([min([sum(abs(d) for d in collapse_pitch(pitch_difference(c, p))) for p in next_transposed_chord]) for c in chordrefs]) == 0
|
|
||||||
return min([min([sum(abs(d) for d in pitch_difference(c, p)) for p in next_transposed_chord]) for c in chordrefs]) == 0
|
|
||||||
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if is_connected(e, chordrefs) else 0
|
|
||||||
|
|
||||||
def voice_crossing_weights(edges):
|
|
||||||
|
|
||||||
def has_voice_crossing(edge):
|
|
||||||
source = list(edge[0])
|
|
||||||
ordered_source = sorted(source, key=hs_array_to_fr)
|
|
||||||
source_order = [ordered_source.index(p) for p in source]
|
|
||||||
destination = [transpose_pitch(edge[2]['movements'][p]['destination'], edge[2]['transposition']) for p in source]
|
|
||||||
ordered_destination = sorted(destination, key=hs_array_to_fr)
|
|
||||||
destination_order = [ordered_destination.index(p) for p in destination]
|
|
||||||
return source_order != destination_order
|
|
||||||
|
|
||||||
for e in edges:
|
|
||||||
yield 10 if not has_voice_crossing(e) else 0
|
|
||||||
|
|
||||||
def dca_weight(edges, last_chords):
|
|
||||||
for edge in edges:
|
|
||||||
source = list(edge[0])
|
|
||||||
ordered_source = sorted(source, key=hs_array_to_fr)
|
|
||||||
source_order = [ordered_source.index(p) for p in source]
|
|
||||||
destination = [transpose_pitch(edge[2]['movements'][p]['destination'], edge[2]['transposition']) for p in source]
|
|
||||||
ordered_destination = tuple(sorted(destination, key=hs_array_to_fr))
|
|
||||||
test_sequence = tuple(zip(*(last_chords + (ordered_destination, ))))
|
|
||||||
#print('here')
|
|
||||||
#print(test_sequence)
|
|
||||||
if len(test_sequence[0]) < 4:
|
|
||||||
yield 10
|
|
||||||
else:
|
|
||||||
if len(set(test_sequence[0][-2:])) == 1 or len(set(test_sequence[1][-2:])) == 1 or len(set(test_sequence[2][-2:])) == 1:
|
|
||||||
yield 0
|
|
||||||
else:
|
|
||||||
yield 10
|
|
||||||
|
|
||||||
def is_bass_rooted(chord):
|
|
||||||
return max([sum(abs(p) for p in collapse_pitch(pitch_difference(chord[0], p))) for p in chord[1:]]) == 1
|
|
||||||
|
|
||||||
def is_directly_tunable(intersection, diff):
|
|
||||||
# this only works for now when intersection if one element - need to fix that
|
|
||||||
return max([sum(abs(p) for p in collapse_pitch(pitch_difference(d, list(intersection)[0]))) for d in diff]) == 1
|
|
||||||
|
|
||||||
def gen_edges(source, candidates, min_symdiff, max_symdiff, max_chord_size, ostinato_ref):
|
|
||||||
for target in candidates:
|
|
||||||
[expanded_source, expanded_target] = [expand_chord(chord) for chord in [source, target]]
|
|
||||||
edges = []
|
|
||||||
expanded_source_with_ostinato_ref = expanded_source + ostinato_ref
|
|
||||||
#print(expanded_source + ostinato_ref)
|
|
||||||
transpositions = set(pitch_difference(pair[0], pair[1]) for pair in set(product(expanded_source, expanded_target)))
|
|
||||||
#print(transpositions)
|
|
||||||
for trans in transpositions:
|
|
||||||
expanded_target_transposed = transpose_chord(expanded_target, trans)
|
|
||||||
intersection = set(expanded_source) & set(expanded_target_transposed)
|
|
||||||
symdiff_len = sum([len(chord) - len(intersection) for chord in [expanded_source, expanded_target_transposed]])
|
|
||||||
if (min_symdiff <= symdiff_len <= max_symdiff):
|
|
||||||
rev_trans = tuple(t * -1 for t in trans)
|
|
||||||
[diff1, diff2] = [list(set(chord) - intersection) for chord in [expanded_source, expanded_target_transposed]]
|
|
||||||
base_map = {val: {'destination':transpose_pitch(val, rev_trans), 'cent_difference': 0} for val in intersection}
|
|
||||||
#base_map_rev = reverse_movements(base_map)
|
|
||||||
maps = []
|
|
||||||
diff1 += [None] * (max_chord_size - len(diff1) - len(intersection))
|
|
||||||
perms = [list(perm) + [None] * (max_chord_size - len(perm) - len(intersection)) for perm in set(permutations(diff2))]
|
|
||||||
for p in perms:
|
|
||||||
appended_map = {
|
|
||||||
diff1[index]:
|
|
||||||
{
|
|
||||||
'destination': transpose_pitch(val, rev_trans) if val != None else None,
|
|
||||||
'cent_difference': cent_difference(diff1[index], val) if None not in [diff1[index], val] else None
|
|
||||||
} for index, val in enumerate(p)}
|
|
||||||
yield (tuple(expanded_source), tuple(expanded_target), {
|
|
||||||
'transposition': trans,
|
|
||||||
'symmetric_difference': symdiff_len,
|
|
||||||
'is_directly_tunable': is_directly_tunable(intersection, diff2),
|
|
||||||
'movements': base_map | appended_map
|
|
||||||
},)
|
|
||||||
|
|
||||||
current_root = start_root
|
|
||||||
#weighted_chord_set = {
|
|
||||||
# chord:
|
|
||||||
# {
|
|
||||||
# 'weight': 10
|
|
||||||
# } for index, chord in enumerate(chord_set)}
|
|
||||||
next_chord = tuple(sorted(expand_chord(choice(chord_set)), key=hs_array_to_fr))
|
|
||||||
#tuple(sorted(next_node, key=hs_array_to_fr))
|
|
||||||
print(next_chord)
|
|
||||||
#weighted_chord_set[next_chord]['weight'] = 1;
|
|
||||||
chords = ((next_chord, next_chord,),)
|
|
||||||
last_chords = (next_chord,)
|
|
||||||
path = []
|
|
||||||
index = 0
|
|
||||||
pathRefChords = ((0, 0, 0, 0, 0, 0, 0), (-2, 0, 1, 0, 0, 0, 0), (-3, 0, 0, 0, 1, 0, 0), (-1, 1, 0, 0, 0, 0, 0), (-3, 0, 0, 0, 0, 1, 0), (-2, 0, 0, 1, 0, 0, 0))
|
|
||||||
#pathRefChords = ((0, 0, 0, 0, 0, 0), (-1, 1, 0, 0, 0, 0), (-2, 0, 1, 0, 0, 0), (-2, 0, 0, 1, 0, 0), (-3, 0, 0, 0, 1, 0), (-3, 0, 0, 0, 0, 1))
|
|
||||||
while (len(path) < 100):
|
|
||||||
ostinato_ref = (pathRefChords[len(path)%6],)
|
|
||||||
edges = list(gen_edges(next_chord, chord_set, min_symdiff, max_symdiff, max_chord_size, ostinato_ref))
|
|
||||||
#print(edges)
|
|
||||||
factors = [
|
|
||||||
movement_size_weights(edges),
|
|
||||||
hamiltonian_weights(edges),
|
|
||||||
#contrary_motion_weights(edges),
|
|
||||||
is_directly_tunable_weights(edges),
|
|
||||||
voice_crossing_weights(edges),
|
|
||||||
#dca_weight(edges, last_chords),
|
|
||||||
is_sustained_voice_alt(edges, choice([0, 1, 2]), current_root),
|
|
||||||
#is_connected_to(edges, (pathRefChords[len(path)%6],))
|
|
||||||
#is_connected_to(edges, (pathRefChords[(len(path) + index) % 6], pathRefChords[(len(path) + index + 1) % 6], pathRefChords[(len(path) + index + 2) % 6]))
|
|
||||||
#is_connected_to(edges, pathRefChords)
|
|
||||||
]
|
|
||||||
index += 1
|
|
||||||
weights = [prod(a) for a in zip(*factors)]
|
|
||||||
edge = choices(edges, weights=weights)[0]
|
|
||||||
#edge = random.choice(out_edges)
|
|
||||||
#print(edge)
|
|
||||||
|
|
||||||
trans = edge[2]['transposition']
|
|
||||||
movements = edge[2]['movements']
|
|
||||||
current_root = transpose_pitch(current_root, trans)
|
|
||||||
current_ref_chord = chords[-1][0]
|
|
||||||
next_ref_chord = tuple(movements[pitch]['destination'] for pitch in current_ref_chord)
|
|
||||||
next_transposed_chord = tuple(transpose_pitch(pitch, current_root) for pitch in next_ref_chord)
|
|
||||||
chords += ((next_ref_chord, next_transposed_chord,),)
|
|
||||||
|
|
||||||
next_chord = edge[1]
|
|
||||||
#node[1]['count'] += 1
|
|
||||||
|
|
||||||
last_chords = last_chords + (next_chord,)
|
|
||||||
if len(last_chords) > 2:
|
|
||||||
last_chords = last_chords[-2:]
|
|
||||||
#print(last_chords)
|
|
||||||
|
|
||||||
path.append(edge)
|
|
||||||
#if next_node in check_graph.nodes:
|
|
||||||
# check_graph.remove_node(next_node)
|
|
||||||
return tuple(chord[1] for chord in chords)
|
|
||||||
|
|
||||||
|
|
||||||
# In[7]:
|
|
||||||
|
|
||||||
|
|
||||||
seed(872984353450043)
|
|
||||||
dims = (2, 3, 5, 7, 11, 13, 17)
|
|
||||||
root = (0, 0, 0, 0, 0, 0, 0)
|
|
||||||
chord = (root,)
|
|
||||||
chord_set = compact_sets(root, 3, 3)
|
|
||||||
path = stochastic_hamiltonian(list(chord_set), root, 4, 4, 3)
|
|
||||||
#for edge in path:
|
|
||||||
# print(edge)
|
|
||||||
write_chord_sequence(path, "sirens.txt")
|
|
||||||
|
|
||||||
|
|
||||||
# In[18]:
|
|
||||||
|
|
||||||
|
|
||||||
seed(872984353450043)
|
|
||||||
dims = (2, 3, 5, 7)
|
|
||||||
root = (0, 0, 0, 0)
|
|
||||||
chord = (root,)
|
|
||||||
chord_set = compact_sets(root, 5, 5)
|
|
||||||
path = stochastic_hamiltonian(list(chord_set), root, 4, 4, 5)
|
|
||||||
#for edge in path:
|
|
||||||
# print(edge)
|
|
||||||
write_chord_sequence(path, "sirens.txt")
|
|
||||||
|
|
||||||
|
|
||||||
# dims = (2, 3, 5, 7, 11, 13, 17)
|
|
||||||
# root = (0, 0, 0, 0, 0, 0, 0)
|
|
||||||
# chord = (root,)
|
|
||||||
# chord_set = compact_sets(root, 3, 3)
|
|
||||||
# #print(len(list(chord_set)))
|
|
||||||
# reduced_chord_set = tuple()
|
|
||||||
# for chord in chord_set:
|
|
||||||
# c_flag = False
|
|
||||||
# for p1, p2 in combinations(collapse_chord(chord), 2):
|
|
||||||
# diff = pitch_difference(p1, p2)
|
|
||||||
# print(diff)
|
|
||||||
# if diff in ((0, 1, 0, 0, 0, 0, 0), (0, 0, 1, 0, 0, 0, 0), (0, 0, 0, 1, 0, 0, 0), (0, -1, 0, 0, 0, 0, 0), (0, 0, -1, 0, 0, 0, 0), (0, 0, 0, -1, 0, 0, 0)) and not c_flag:
|
|
||||||
# #if (abs(p1[1] - p2[1]) == 1 or abs(p1[2] - p2[2]) == 1 or abs(p1[3] - p2[3]) == 1) and not c_flag:
|
|
||||||
# c_flag = True
|
|
||||||
# #break
|
|
||||||
# if c_flag:
|
|
||||||
# reduced_chord_set += (chord,)
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# len(reduced_chord_set)
|
|
||||||
#
|
|
||||||
# pitch_difference(
|
|
||||||
7204
session-ses_328e.md
7204
session-ses_328e.md
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue