As promised, here are a few updates on some math/music research !

Grégoire Genuys, a Ph.D. student working at IRCAM, went to the MCM 2017 conference, and presented our paper “*Homometry in the Dihedral Groups: Lifting Sets from to *“. We extended the notion of homometry (which I promise I will expose in this blog) to non-abelian groups, in particular dihedral groups since they are of particular interest for music. Since they are non-commutative, we get two different flavors of homometry depending on whether the group acts on the left or on the right. We enumerated non-trivial homometric sets of varying cardinality in these dihedral groups, and gave some characterisation theorems for these sets. Grégoire also has a longer paper on the ArXiV: “Non-Commutative Homometry in the Dihedral Groups”.

The long paper about the categorical formalization of Klumpenhouwer networks that I worked on with Andrée Ehresmann, Moreno Andreatta, and Carlos Agon, “*From K-Nets to PK-Nets: a Categorical Approach*” has finally been published in **Perspectives of New Music** ! You can find it on JSTOR here. This is the continuation of a short paper that was presented at the MCM 2015 conference, and previously published in the MCM proceedings. Now that this is published, I plan on writing several posts to explain the theory behind these papers.

Following previous results on rhythmic canons, I extended the calculation of the lengths of rhythmic canons mod 2 with motive for up to 290. Here is the corresponding graph:

Of course, with lengths , this is not very interesting for musical applications. I was in fact hoping to spot the unusual cases I presented previously, which lead me to the following conjectures:

* Conjecture 1: *In where is prime (except for ), (non-compact) canons of motive have a length of .

* Conjecture 2: *In , canons of motive have a length of .

* Conjecture 3: *In , canons of motive have a length of .

* Conjecture 4: *In , canons of motive have a length of .

* Conjecture 5: *In , canons of motive have a length of .

In fact, I discovered that one can prove a weaker version of these conjectures, namely that the conjectured lengths are upper bounds on the actual length of the rythmic canons. Here is how we prove that. Consider first motives of the form . In the ring we thus have

Exponentiating by , we get

and thus

giving therefore

Therefore, we conclude that an upper bound for the length of rhythmic canons mod 2 with motive is . We know by the results of Hélianthe Caure that this is the actual length. The weaker version of conjecture 1 above is easily obtained from this result.

Similarly, assume that we have a motive of the form .In the ring we thus have

Exponentiating by , we get

and multiplying by X

and thus we conclude that an upper bound for the length of rhythmic canons mod 2 with motive is . Again, by the results of Hélianthe, we know this is the actual length.

Let’s prove the weaker version of conjecture 2 (the other conjectures are approached in a similar way). We consider motives of the form , and the corresponding ring in which we have

Exponentiating by , we get

and thus

equal to

leading to

and finally,

and thus we conclude that an upper bound for the length of rhythmic canons mod 2 with motive is . There would remain to prove that is the actual length !

And finally, I presented in a previous post a link between musical chords, tonnetzes, and topology. And I had provided Matlab codes for calculating Betti numbers of the simplicial complexes formed by different pitch-class sets. I don’t work with Matlab anymore and wanted to update it to Python so here it is. I used an existing Python function to calculate the Smith normal form of a matrix with coefficients in . The resulting code is in fact quite general and could be used to calculate the Betti numbers of any simplicial complex.

##coding: utf-8 import itertools import numpy as np def reduce_matrix(matrix): #Returns [reduced_matrix, rank, nullity] if np.size(matrix)==0: return [matrix,0,0] m=matrix.shape[0] n=matrix.shape[1] def _reduce(x): #We recurse through the diagonal entries. #We move a 1 to the diagonal entry, then #knock out any other 1s in the same col/row. #The rank is the number of nonzero pivots, #so when we run out of nonzero diagonal entries, we will #know the rank. nonzero=False #Searching for a nonzero entry then moving it to the diagonal. for i in range(x,m): for j in range(x,n): if matrix[i,j]==1: matrix[[x,i],:]=matrix[[i,x],:] matrix[:,[x,j]]=matrix[:,[j,x]] nonzero=True break if nonzero: break #Knocking out other nonzero entries. if nonzero: for i in range(x+1,m): if matrix[i,x]==1: matrix[i,:] = np.logical_xor(matrix[x,:], matrix[i,:]) for i in range(x+1,n): if matrix[x,i]==1: matrix[:,i] = np.logical_xor(matrix[:,x], matrix[:,i]) #Proceeding to next diagonal entry. return _reduce(x+1) else: #Run out of nonzero entries so done. return x rank=_reduce(0) return [matrix, rank, n-rank] ### Source: < https://triangleinequality.wordpress.com/2014/01/23/computing-homology/ > ################ def get_boundary_matrices(simplicial_complex): dim_values = [len(x) for x in simplicial_complex] max_dim = np.max(dim_values) boundary_matrices = {} all_simplices={} simplices = [set(x) for x in simplicial_complex if len(x)==max_dim] for dim in range(1,max_dim+1)[::-1]: sub_simplices = [] for simplex in simplices: for subsimplex in itertools.combinations(simplex,dim-1): if not set(subsimplex) in sub_simplices: sub_simplices.append(set(subsimplex)) for x in simplicial_complex: if len(x)==dim-1: if not set(x) in sub_simplices: sub_simplices.append(set(x)) all_simplices[dim-1] = [list(x) for x in simplices] BM_entries=[] for i,simplex in enumerate(simplices): for subsimplex in list(itertools.combinations(simplex,dim-1)): for j,x in enumerate(sub_simplices): if (x==set(subsimplex)): BM_entries.append([j,i]) BM = np.zeros((len(sub_simplices),len(simplices))) for i,j in BM_entries: BM[i,j]=1 boundary_matrices[dim-1] = BM.copy() simplices = [x for x in sub_simplices] return boundary_matrices,all_simplices ################ def get_betti(boundary_matrices,max_betti): betti_numbers = {} for dim in range(max_betti+1)[::-1]: if dim==max_betti: betti = reduce_matrix(boundary_matrices[dim].copy())[2] elif dim==0: betti = np.sum(reduce_matrix(boundary_matrices[dim].copy())[1:3]) betti -= reduce_matrix(boundary_matrices[dim+1].copy())[1] else: betti = reduce_matrix(boundary_matrices[dim].copy())[2] betti -= reduce_matrix(boundary_matrices[dim+1].copy())[1] betti_numbers[dim]=betti return betti_numbers ################ ch_type=[[0,4,7],[0,3,6]] ch_list=[] for x in ch_type: for t in range(12): ch_list.append([np.mod(x[0]+t,12),np.mod(x[1]+t,12),np.mod(x[2]+t,12)]) print ch_list mat,simp = get_boundary_matrices(ch_list) print get_betti(mat,2)

Nice. Out of my range, but nice.

Here is one of my playings about with math-music resonances —

https://oeis.org/wiki/Riffs_and_Rotes