Skip to content

Instantly share code, notes, and snippets.

@jupdike
Created January 22, 2025 03:36
Show Gist options
  • Save jupdike/f6138924ca85e4d35dfd6bae9a97c73b to your computer and use it in GitHub Desktop.
Save jupdike/f6138924ca85e4d35dfd6bae9a97c73b to your computer and use it in GitHub Desktop.
Python code to generate tables of every set class (OPTC-equivalent) sorted by distance to the perfectly even C-note chord
# run this with python3
# See https://harmoniousapp.net/p/71/Set-Classes for more info
import math
# ------------------------
# Utility functions for dealing with Set Classes
# a number in binary between 0 and 111111111111b (4093 decimal) can be converted
# to and from a list of integers, such as [0,4,7] which is 000010010001b or 145 decimal
def tolist(x):
ret = []
for b in range(12):
if x & (1<<b):
ret.append(b)
return ret
def fromlist(l):
x = 0
for e in l:
x += 1<<e
return x
def lowest(a):
aa = rotations(a)
aa.sort()
return aa[0]
# is a subbits of b?
def issubbits(a,b):
return a & b == a
def bitsset(x):
mask = 1
ret = 0
while mask <=x:
if mask & x:
ret += 1
mask <<= 1
return ret
def permute(l):
n = len(l)
return [l[i:]+l[:i] for i in range(n)]
def rightshift(x):
return ((x>>1) & 4095) | ((x & 1) << 11)
def leftshift(x):
if x & (1<<11):
return ( 1 | (x << 1) ) & 4095
return (x << 1) & 4095
def rotations(num):
ret = []
r = num
for i in range(12):
ret.append(r)
r = rightshift(r)
return ret
clusters = [fromlist([ (x+0)%12, (x+1)%12, (x+2)%12 ]) for x in range(12)]
def hasCluster(c):
noClusters = True
for cluster in clusters:
if issubbits(cluster, c):
noClusters = False
break
return not noClusters
# t = ten
# e = eleven
ordinal = '0 1 2 3 4 5 6 7 8 9 t e'.split(' ')
def pic(setclass):
x = lowest(setclass)
ret = []
for i in range(12):
if x & (1 << i):
ret.append(ordinal[i])
return ''.join(ret)
# ------------------------
# the main algorithm
def circle(card):
cardf = 1.0 * card
card = int(math.ceil(cardf))
return [sorted([( (12.0*y/1000.0) + x*12.0/cardf)%12 for x in range(card)]) for y in range(1000)]
def bestcenter1(card, circ, x):
n = len(x)
bestD = 9999999
for c in circ:
if (len(c) != n):
print("huh?")
exit(1)
continue
d2 = 0
for i in range(n):
delta = c[i] - x[i]
delta = abs(delta)
if delta > 6:
delta = 12 - delta
d2 += delta*delta
if d2 < bestD:
bestD = d2
bestD *= card
bestD = int(round(bestD))
return bestD
def bestcenter(card, centers, num):
x = tolist(num)
bestD = 99999999
for p in permute(x):
d = bestcenter1(card, centers, p)
if d < bestD:
bestD = d
return bestD
def makenumbers(card):
lowest_seen = set()
circ = circle(card)
results = []
cardf = card*1.0
card = int(math.ceil(cardf))
for e in range(4096):
if e == 0: # don't crash for call to lowest(0)
continue
if not bitsset(e) == card:
continue
l = lowest(e)
if l in lowest_seen:
continue
lowest_seen.add(l)
best_d2_numerator = bestcenter(cardf, circ, e)
bestStr = "sqrt(%i/%i)" % (best_d2_numerator, card)
if best_d2_numerator == 0:
bestStr = "0"
# ccc = chromatic-cluster-containing
# cf = chromatic-cluster-free
cluster_str = "ccc" if hasCluster(e) else "cf"
results.append([best_d2_numerator, bestStr, e, pic(e), cluster_str])
results.sort(key=lambda x: 10000 * x[0] + x[2])
print(str(results).replace('],', '],\n'))
if __name__ == "__main__":
for c in [3, 4, 5, 6, 7, 8, 9]:
print()
print("Cardinality: %d" % c)
makenumbers(c)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment