Skip to content
Snippets Groups Projects
Commit 4a58a076 authored by Axel Marmoret's avatar Axel Marmoret
Browse files

Fixing some typos/errors

parent ac9d0cd0
No related branches found
No related tags found
No related merge requests found
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Hi! # Hi!
Hello and welcome in this tutorial for polytope reconfiguration of music. Hello and welcome in this tutorial for polytope reconfiguration of music.
This work presents a geometrical view for analyzing music, allowing to consider relations between musical elements which don't follow the sequential order. This work presents a geometrical view for analyzing music, allowing to consider relations between musical elements which don't follow the sequential order.
It was developped by C. Guichaoua [1] and C. Louboutin [2], both under the supervision of F. Bimbot. It was developped by C. Guichaoua [1] and C. Louboutin [2], both under the supervision of F. Bimbot.
[TODO: Add My Own Reference]: In the near future, I will upload a reference (probably on ArXiv) summing up both of their work, detailing their similarities, and presenting some new models, which could help understanding the rest of this notebook. In the meantime, you should refer to the PhD thesis previously referenced. [TODO: Add My Own Reference]: In the near future, I will upload a reference (probably on ArXiv) summing up both of their work, detailing their similarities, and presenting some new models, which could help understanding the rest of this notebook. In the meantime, you should refer to the PhD thesis previously referenced.
[1] C. Guichaoua, Modèles de compression et critères de complexité pour la description et l’inférence de structure musicale. PhD thesis, 2017. [1] C. Guichaoua, Modèles de compression et critères de complexité pour la description et l’inférence de structure musicale. PhD thesis, 2017.
[2] C. Louboutin, Modélisation multi-échelle et multi-dimensionnelle de la structure musicale par graphes polytopiques. PhD thesis, Rennes 1, 2019. [2] C. Louboutin, Modélisation multi-échelle et multi-dimensionnelle de la structure musicale par graphes polytopiques. PhD thesis, Rennes 1, 2019.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Installation # Installation
To install the code, you should donwload the project: https://gitlab.inria.fr/amarmore/musiconpolytopes To install the code, you should donwload the project: https://gitlab.inria.fr/amarmore/musiconpolytopes
Then you should put all this in a folder, at a given path (noted /path/to/package). Then you should put all this in a folder, at a given path (noted /path/to/package).
Finally, you should open a bash shell (on Linux distribution) or an anaconda prompt (on Windows) and type: `pip install -e /path/to/package`. Finally, you should open a bash shell (on Linux distribution) or an anaconda prompt (on Windows) and type: `pip install -e /path/to/package`.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# From Polytopes to Patterns # From Polytopes to Patterns
In this work, polytopes are geometrical objects (n-dimensional hypercubes with possible alteration), which support musical elements on their vertices, and link them by edges. In general (at least for now), musical elements are major or minor chords, discretized on a temporal index (beats or upbeats typically). An example of polytope is presented on the figure below. In this work, polytopes are geometrical objects (n-dimensional hypercubes with possible alteration), which support musical elements on their vertices, and link them by edges. In general (at least for now), musical elements are major or minor chords, discretized on a temporal index (beats or upbeats typically). An example of polytope is presented on the figure below.
<img src="imgs/polytope_example.png" width="700"/> <img src="imgs/polytope_example.png" width="700"/>
To represent them computationally, we used nested lists. Indeed, polytopes are n dimensional hypercubes (which can then be altered). In that sense, a n-dimensional hypercube is constructed recursively as the union of two (n-1)-dimensional hypercubes (for instance, a cube can be seen as the concatenation of 2 squares). To represent them computationally, we used nested lists. Indeed, polytopes are n dimensional hypercubes (which can then be altered). In that sense, a n-dimensional hypercube is constructed recursively as the union of two (n-1)-dimensional hypercubes (for instance, a cube can be seen as the concatenation of 2 squares).
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Hence, in our model, a dimension is represented by the level of nesting of lists, and each vertex of the polytope will be represented by a number (for the general shape) or a chord (for the musical polytope). We call this object **pattern**, to differentiate it with the geometrical polytope. Hence, in our model, a dimension is represented by the level of nesting of lists, and each vertex of the polytope will be represented by a number (for the general shape) or a chord (for the musical polytope). We call this object **pattern**, to differentiate it with the geometrical polytope.
The code used for generating and handling patterns is in the file ``pattern_factory.py``. The code used for generating and handling patterns is in the file ``pattern_factory.py``.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from polytopes import pattern_factory as pf from polytopes import pattern_factory as pf
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
We define two types of patterns: We define two types of patterns:
- "patterns of ones", which act as the skeleton of the polytope: every vertex/element in the neseted lists is 1, and represent a genereic element. It is useful to consider the pattern as a whole and when differntiating elements isn't necesseray (for example, evaluating each nested pattern for its shape and not its content). - "patterns of ones", which act as the skeleton of the polytope: every vertex/element in the neseted lists is 1, and represent a genereic element. It is useful to consider the pattern as a whole and when differntiating elements isn't necesseray (for example, evaluating each nested pattern for its shape and not its content).
- The dimension 1 pattern of ones is [1,1], - The dimension 1 pattern of ones is [1,1],
- The dimension 2 pattern of ones is the nesting of 2 dim-1 patterns, so [[1,1],[1,1]], - The dimension 2 pattern of ones is the nesting of 2 dim-1 patterns, so [[1,1],[1,1]],
- The dimension 3 pattern of ones is [[[1,1],[1,1]],[[1,1],[1,1]]], - The dimension 3 pattern of ones is [[[1,1],[1,1]],[[1,1],[1,1]]],
- etc. - etc.
- "indexed patterns", where each element of the pattern represent the index of this vertex in the chronological/sequential order. Indeed, as vertices in the polytope represent elements in a chord progression, we have to know each element's position, to relate it with the musical context. Traditionnally, indexes start at 0. - "indexed patterns", where each element of the pattern represent the index of this vertex in the chronological/sequential order. Indeed, as vertices in the polytope represent elements in a chord progression, we have to know each element's position, to relate it with the musical context. Traditionnally, indexes start at 0.
- The dimension 1 indexed pattern is [0,1], - The dimension 1 indexed pattern is [0,1],
- The dimension 2 indexed pattern is the nesting of 2 dim-1 patterns, so [[0,1],[2,3]], - The dimension 2 indexed pattern is the nesting of 2 dim-1 patterns, so [[0,1],[2,3]],
- The dimension 3 indexed pattern is [[[0,1],[2,3]],[[4,5],[6,7]]], - The dimension 3 indexed pattern is [[[0,1],[2,3]],[[4,5],[6,7]]],
- etc. - etc.
<img src="imgs/patterns_and_polytopes.gif" width="700"/> <img src="imgs/patterns_and_polytopes.gif" width="700"/>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
For example, to generate a dim-3 pattern of ones, you can use the code below: For example, to generate a dim-3 pattern of ones, you can use the code below:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# Feel free to play with the dimension # Feel free to play with the dimension
pf.make_regular_polytope_pattern(dimension = 3) pf.make_regular_polytope_pattern(dimension = 3)
``` ```
%% Output %% Output
[[[1, 1], [1, 1]], [[1, 1], [1, 1]]] [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Irregular polytopes and patterns ## Irregular polytopes and patterns
In the previous part, we presented regular polytopes and patterns, *i.e.* n-dimensional hypercubes. In our model, they represent the core of polytopes, but not the entire story. Indeed, as presented by C. Guichaoua in [1], polytopes are extended by adding and/or deleting some vertices to a n-dimensional hypercubes. The alteration (both addition and deletion) follow the vertices of a m-dimensional polytope, with $m < n - 1$. In that sense, every polytope is a n-dimensional polytope on which some vertices may be deleted, and some other may be added, following the shape of another hypercube. In the case of both addition and deletion, the dimensions of their respective polytopic shape can be of different dimensions. When some vertices are involved with both an addition and a deletion, the deletion has higher priority. In the previous part, we presented regular polytopes and patterns, *i.e.* n-dimensional hypercubes. In our model, they represent the core of polytopes, but not the entire story. Indeed, as presented by C. Guichaoua in [1], polytopes are extended by adding and/or deleting some vertices to a n-dimensional hypercubes. The alteration (both addition and deletion) follow the vertices of a m-dimensional polytope, with $m < n - 1$. In that sense, every polytope is a n-dimensional polytope on which some vertices may be deleted, and some other may be added, following the shape of another hypercube. In the case of both addition and deletion, the dimensions of their respective polytopic shape can be of different dimensions. When some vertices are involved with both an addition and a deletion, the deletion has higher priority.
To represent a deletion in a pattern, we simply delete the involved elements. For example, the pattern associated with the deletion of an element in a dimension 1 polytope is "[1]" (not to confuse with references), and the deletion of the last dim-1 polytope in a dim-3 polytope would be: [[[1,1],[1,1]],[[1,1]]]. (NB: a void dim-1 polytope ([]) will not be displayed). To represent a deletion in a pattern, we simply delete the involved elements. For example, the pattern associated with the deletion of an element in a dimension 1 polytope is "[1]" (not to confuse with references), and the deletion of the last dim-1 polytope in a dim-3 polytope would be: [[[1,1],[1,1]],[[1,1]]]. (NB: a void dim-1 polytope ([]) will not be displayed).
An addition will be represented by the tuple at the position of the addition, where the first element of the tuple will be the element originally present in the polytope, and the second one will be the added element. In that sense, the pattern associated with the addition of an element in a dimension 1 polytope is "[1, (1,1)]", and the addition of a dim-1 polytope in a dim-3 polytope would be: [[[1,1],[1,1]],[[1,1],[(1,1),(1,1)]]]. An addition will be represented by the tuple at the position of the addition, where the first element of the tuple will be the element originally present in the polytope, and the second one will be the added element. In that sense, the pattern associated with the addition of an element in a dimension 1 polytope is "[1, (1,1)]", and the addition of a dim-1 polytope in a dim-3 polytope would be: [[[1,1],[1,1]],[[1,1],[(1,1),(1,1)]]].
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Irregularities in practice ### Irregularities in practice
In practice, we need to define a modus operandis for irregularities. This is made by encoding the positions which need to be altered with "codes", which are lists of booleans (0 or 1). In practice, we need to define a modus operandis to construct the irregularities. This is made by encoding the location of additions or deletions in the pattern. This encoding is in fact two lists of booleans (0 or 1) which we call codes.
Codes are constructed to indicate where and how many positions need to be altered. Even though each element in the polytope can be specified by dichotomy (by construction, by concatenating two polytopes when increasing the dimension, see part "Antecedents"), **the goal of codes here is not to indicate individually each position which need to be altered**. Codes are constructed to indicate where and how many positions need to be altered. Even though each element in the polytope can be specified by dichotomy (by construction, by concatenating two polytopes when increasing the dimension, see part "Antecedents"), **the goal of codes here is not to indicate individually each position which need to be altered**.
- Codes are related to the notion of dimensions in the nesting: - Codes are related to the notion of dimensions in the nesting:
- The first element of the code (left element, the first in the list) will encode information about the 2 polytopes of dimension n-1 - The first element of the code (left element, the first in the list) will encode information about the 2 polytopes of dimension n-1
- The second element of the code will encode information about polytopes at the n-2 dimension - The second element of the code will encode information about polytopes at the n-2 dimension
- The third element of the code will encode information about polytopes at the n-3 dimension - The third element of the code will encode information about polytopes at the n-3 dimension
- And so on until the last one, which represent information at the last nesting dimension (so directly on elements) - And so on until the last one, which represent information at the last nesting dimension (so directly on elements)
- As a dichotomy principle, the alteration will be propagated with binary rules at each dimension: - As a dichotomy principle, the alteration will be propagated with binary rules at each dimension:
- If the current boolean is a 1, this alteration will affect both nested polytopes. - If the current boolean is a 1, this alteration will affect both nested polytopes.
- If the current boolean is a 0, this alteration will only affect the second nested polytope, the one "on the right" (geometrically). - If the current boolean is a 0, this alteration will only affect the second nested polytope, the one "on the right" (geometrically).
- In that sense, at every dimension, if the current boolean is 0, the code will only be propagated to the 2nd nested polytope, and the 1st nested polytope will be left without alteration. Otherwise, if it's a 1, the rest of the code will be copied to both polytopes of lower dimension. - In that sense, at every dimension, if the current boolean is 0, the code will only be propagated to the 2nd nested polytope, and the 1st nested polytope will be left without alteration. Otherwise, if it's a 1, the rest of the code will be copied to both polytopes of lower dimension.
- At the last level, a 0 will indicate to alter only the 2nd polytope (so the element on the right), and a 1 will indicate to alter both. Hence, a code composed of zeroes ([0,0,...,0]) will still alter the last element! To specify "no alteration at all", code must be an empty list. - At the last level, a 0 will indicate to alter only the 2nd polytope (so the element on the right), and a 1 will indicate to alter both. Hence, a code composed of zeroes ([0,0,...,0]) will still alter the last element! To specify "no alteration at all", code must be an empty list.
In that sense, by dichotomy, we specify, for each dimension, to which part of the nesting alteration should be propagated (both or only the second part). As it is a dichotomy principle, codes have to be of the same length than the dimension of the polytope. In that sense, by dichotomy, we specify, for each dimension, to which part of the nesting alteration should be propagated (both or only the second part). As it is a dichotomy principle, codes have to be of the same length than the dimension of the polytope.
In addition, the number of "1" in the code define the dimension of the alteration polytope. In addition, the number of "1" in the code define the dimension of the alteration polytope.
Here is an illustration on a dim-3 polytope. Here is an illustration on a dim-3 polytope.
<img src="imgs/code_gif.gif" width="700"/> <img src="imgs/code_gif.gif" width="700"/>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Let's play with codes a little bit. For example, let's consider a polytope of dimension 3, with an addition and no deletion. The additional polytope is of dimension 1 (so 2 elements, and a unique 1 in the code), and should occur on the last elements of both nested dimension 2 polytopes. It corresponds to the following polytope: Let's play with codes a little bit. For example, let's consider a polytope of dimension 3, with an addition and no deletion. The additional polytope is of dimension 1 (so 2 elements, and a unique 1 in the code), and should occur on the last elements of both nested dimension 2 polytopes. It corresponds to the following polytope:
<img src="imgs/dim_3_add_100_del_.png" width="200"/> <img src="imgs/dim_3_add_100_del_.png" width="200"/>
To represent the addition, we shall propagate addition in both dimension-2 polytopes, but in no other level. The code is hence a 1 for the dimension 2 level of nesting (so the first boolean element), and 0 for all the others, so: [1,0,0]. To represent the addition, we shall propagate addition in both dimension-2 polytopes, but in no other level. The code is hence a 1 for the dimension 2 level of nesting (so the first boolean element), and 0 for all the others, so: [1,0,0].
As there is no deletion, the code should be null, so the deleting code will be an empty list: []. As there is no deletion, the code should be null, so the deleting code will be an empty list: [].
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pf.make_polytope_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = []) pf.make_polytope_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [])
``` ```
%% Output %% Output
[[[1, 1], [1, (1, 1)]], [[1, 1], [1, (1, 1)]]] [[[1, 1], [1, (1, 1)]], [[1, 1], [1, (1, 1)]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
To construct the associated indexed pattern, one could use the following function: To construct the associated indexed pattern, one could use the following function:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = []) pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [])
``` ```
%% Output %% Output
[[[0, 1], [2, (3, 4)]], [[5, 6], [7, (8, 9)]]] [[[0, 1], [2, (3, 4)]], [[5, 6], [7, (8, 9)]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Let's now consider the polytope of dimension 3, where the last 2 elements are deleted. The alteration polytope is of dimension 1 (so 2 elements, and a unique 1 in the code), and should occur on the last elements of both nested dimension 0 polytopes (last two elements). It corresponds to the following polytope: Let's now consider the polytope of dimension 3, where the last 2 elements are deleted. The alteration polytope is of dimension 1 (so 2 elements, and a unique 1 in the code), and should occur on the last elements of both nested dimension 0 polytopes (last two elements). It corresponds to the following polytope:
<img src="imgs/dim_3_add_del_001.png" width="200"/> <img src="imgs/dim_3_add_del_001.png" width="200"/>
To represent the deletion, we shall only propagate addition in the last dimension-0 polytopes, but in no other level. The code is hence a 1 for the dimension 0 level of nesting (so the last boolean element), and 0 for all the others, so: [0,0,1]. To represent the deletion, we shall only propagate addition in the last dimension-0 polytopes, but in no other level. The code is hence a 1 for the dimension 0 level of nesting (so the last boolean element), and 0 for all the others, so: [0,0,1].
As there is no addition, the code should be null, so the addition code will be an empty list: []. As there is no addition, the code should be null, so the addition code will be an empty list: [].
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
#pf.make_polytope_pattern(dimension = 3, adding_code = [], deleting_code = [0,0,1]) #pf.make_polytope_pattern(dimension = 3, adding_code = [], deleting_code = [0,0,1])
pf.make_indexed_pattern(dimension = 3, adding_code = [], deleting_code = [0,0,1]) pf.make_indexed_pattern(dimension = 3, adding_code = [], deleting_code = [0,0,1])
``` ```
%% Output %% Output
[[[0, 1], [2, 3]], [[4, 5]]] [[[0, 1], [2, 3]], [[4, 5]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Mixing both previous codes result in a polytope where the first dimension-2 polytope has an addition, and the second has its last two elements deleted (because deletion has higher priority than addition, by construction). It corresponds to the following polytope: Mixing both previous codes result in a polytope where the first dimension-2 polytope has an addition, and the second has its last two elements deleted (because deletion has higher priority than addition, by construction). It corresponds to the following polytope:
<img src="imgs/dim_3_add_100_del_001.png" width="200"/> <img src="imgs/dim_3_add_100_del_001.png" width="200"/>
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
#pf.make_polytope_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1]) #pf.make_polytope_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1])
pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1]) pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1])
``` ```
%% Output %% Output
[[[0, 1], [2, (3, 4)]], [[5, 6]]] [[[0, 1], [2, (3, 4)]], [[5, 6]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Finally, an example of a dimension-4 polytope, with a dimension 2 deletion and no addition is given below: Finally, an example of a dimension-4 polytope, with a dimension 2 deletion and no addition is given below:
<img src="imgs/dim_4_add_del_0110.png" width="600"/> <img src="imgs/dim_4_add_del_0110.png" width="600"/>
To what codes does it corresponds? To what codes does it corresponds?
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
adding_code = [] ### Change values here if needed adding_code = [] ### Change values here if needed
deleting_code = [] ### Change values here if needed deleting_code = [] ### Change values here if needed
pf.make_indexed_pattern(dimension = 4, adding_code = adding_code, deleting_code = deleting_code) pf.make_indexed_pattern(dimension = 4, adding_code = adding_code, deleting_code = deleting_code)
# Should result in: [[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8], [9]], [[10], [11]]]] # Should result in: [[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8], [9]], [[10], [11]]]]
``` ```
%% Output %% Output
[[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[[0, 1], [2, 3]], [[4, 5], [6, 7]]],
[[[8, 9], [10, 11]], [[12, 13], [14, 15]]]] [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Codes associated to a pattern size ### Codes associated to a pattern size
In practice, we may want to generate patterns according to the size of the current segment. In that sense, we want to anticipate the size of a pattern without constructing it. This is possible because alteration are of size $2^m$ with $m$ the number of 1 in the code. Hence, using the following function, we will anticipate the size of a pattern prior to its construction: In practice, we may want to generate patterns according to the size of the current segment. In that sense, we want to anticipate the size of a pattern without constructing it. This is possible because alteration are of size $2^m$ with $m$ the number of 1 in the code. Hence, using the following function, we will anticipate the size of a pattern prior to its construction:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pf.get_final_pattern_size(dimension = 5, adding_code = [1,0,0,1,0], deleting_code = [0,1,0,1,0]) pf.get_final_pattern_size(dimension = 5, adding_code = [1,0,0,1,0], deleting_code = [0,1,0,1,0])
``` ```
%% Output %% Output
30 30
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Thanks to that anticipation, we can also find all couple of codes which will result in an properly sized pattern: Thanks to that anticipation, we can also find all couple of codes which will result in an properly sized pattern:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
# The first element of each tuple is the addition code, the second is the deletion one. # The first element of each tuple is the addition code, the second is the deletion one.
pf.get_codes(7) pf.get_codes(7)
``` ```
%% Output %% Output
[([], [0, 0, 0]), [([], [0, 0, 0]),
([0, 0, 0], [0, 0, 0]), ([0, 0, 0], [0, 0, 0]),
([0, 0, 1], [0, 1, 0]), ([0, 0, 1], [0, 1, 0]),
([0, 0, 1], [1, 0, 0]), ([0, 0, 1], [1, 0, 0]),
([0, 1, 0], [0, 0, 1]), ([0, 1, 0], [0, 0, 1]),
([0, 1, 0], [1, 0, 0]), ([0, 1, 0], [1, 0, 0]),
([1, 0, 0], [0, 0, 1]), ([1, 0, 0], [0, 0, 1]),
([1, 0, 0], [0, 1, 0])] ([1, 0, 0], [0, 1, 0])]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Nonetheless, it can happen that different couples of codes generate the same pattern. In that sense, we developed an additional function, which should be the generic one: Nonetheless, it can happen that different couples of codes generate the same pattern. In that sense, we developed an additional function, which should be the generic one:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pf.get_unique_codes(7) pf.get_unique_codes(7)
``` ```
%% Output %% Output
[([], [0, 0, 0]), [([], [0, 0, 0]),
([0, 0, 1], [0, 1, 0]), ([0, 0, 1], [0, 1, 0]),
([0, 0, 1], [1, 0, 0]), ([0, 0, 1], [1, 0, 0]),
([0, 1, 0], [0, 0, 1]), ([0, 1, 0], [0, 0, 1]),
([0, 1, 0], [1, 0, 0]), ([0, 1, 0], [1, 0, 0]),
([1, 0, 0], [0, 0, 1]), ([1, 0, 0], [0, 0, 1]),
([1, 0, 0], [0, 1, 0])] ([1, 0, 0], [0, 1, 0])]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
### Switching between pattern of ones and indexed pattern ### Switching between pattern of ones and indexed pattern
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
It can be useful to switch from an indexed pattern to a pattern of ones, or vice-versa. This can be made by using the functions presented below: It can be useful to switch from an indexed pattern to a pattern of ones, or vice-versa. This can be made by using the functions presented below:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern_of_ones = pf.make_polytope_pattern(dimension = 4, adding_code = [], deleting_code = []) pattern_of_ones = pf.make_polytope_pattern(dimension = 4, adding_code = [], deleting_code = [])
print("Pattern of ones:\n{}\n".format(pattern_of_ones)) print("Pattern of ones:\n{}\n".format(pattern_of_ones))
indexed_pattern = pf.index_this_pattern(pattern_of_ones) indexed_pattern = pf.index_this_pattern(pattern_of_ones)
print("Previous pattern, indexed:\n{}\n".format(indexed_pattern)) print("Previous pattern, indexed:\n{}\n".format(indexed_pattern))
re_pattern_of_ones = pf.extract_pattern_from_indexed_pattern(indexed_pattern) re_pattern_of_ones = pf.extract_pattern_from_indexed_pattern(indexed_pattern)
print("Going back to pattern of ones pattern, indexed:\n{}".format(re_pattern_of_ones)) print("Going back to pattern of ones pattern, indexed:\n{}".format(re_pattern_of_ones))
``` ```
%% Output %% Output
Pattern of ones: Pattern of ones:
[[[[1, 1], [1, 1]], [[1, 1], [1, 1]]], [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]] [[[[1, 1], [1, 1]], [[1, 1], [1, 1]]], [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]]
Previous pattern, indexed: Previous pattern, indexed:
[[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]] [[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]]
Going back to pattern of ones pattern, indexed: Going back to pattern of ones pattern, indexed:
[[[[1, 1], [1, 1]], [[1, 1], [1, 1]]], [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]] [[[[1, 1], [1, 1]], [[1, 1], [1, 1]]], [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The following function indicates whether a pattern is indexed or not (*i.e.* is a pattern of ones): The following function indicates whether a pattern is indexed or not (*i.e.* is a pattern of ones):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern_of_ones = pf.make_polytope_pattern(dimension = 4, adding_code = [], deleting_code = []) pattern_of_ones = pf.make_polytope_pattern(dimension = 4, adding_code = [], deleting_code = [])
boolean = pf.is_indexed_pattern(pattern_of_ones) boolean = pf.is_indexed_pattern(pattern_of_ones)
print("Is {} an indexed pattern?\n{}\n".format(pattern_of_ones, boolean)) print("Is {} an indexed pattern?\n{}\n".format(pattern_of_ones, boolean))
indexed_pattern = pf.index_this_pattern(pattern_of_ones) indexed_pattern = pf.index_this_pattern(pattern_of_ones)
boolean = pf.is_indexed_pattern(indexed_pattern) boolean = pf.is_indexed_pattern(indexed_pattern)
print("Is {} an indexed pattern?\n{}".format(indexed_pattern, boolean)) print("Is {} an indexed pattern?\n{}".format(indexed_pattern, boolean))
``` ```
%% Output %% Output
Is [[[[1, 1], [1, 1]], [[1, 1], [1, 1]]], [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]] an indexed pattern? Is [[[[1, 1], [1, 1]], [[1, 1], [1, 1]]], [[[1, 1], [1, 1]], [[1, 1], [1, 1]]]] an indexed pattern?
False False
Is [[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]] an indexed pattern? Is [[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]] an indexed pattern?
True True
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Get informations from a pattern ## Get informations from a pattern
We've previously seen how to construct a pattern. In practice, one also needs to access to informations about a particular pattern. We've previously seen how to construct a pattern. In practice, one also needs to access to informations about a particular pattern.
Let's consider a particular pattern as an example: Let's consider a particular pattern as an example:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern = pf.make_indexed_pattern(dimension = 4, adding_code = [1,0,0,0], deleting_code = [0,1,0,1]) pattern = pf.make_indexed_pattern(dimension = 4, adding_code = [1,0,0,0], deleting_code = [0,1,0,1])
pattern pattern
``` ```
%% Output %% Output
[[[[0, 1], [2, 3]], [[4, 5], [6, (7, 8)]]], [[[9, 10]], [[11, 12]]]] [[[[0, 1], [2, 3]], [[4, 5], [6, (7, 8)]]], [[[9, 10]], [[11, 12]]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Two important informations about a pattern are its dimension and its size. Both can be accessed with these functions: Two important informations about a pattern are its dimension and its size. Both can be accessed with these functions:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pf.get_pattern_dimension(pattern) pf.get_pattern_dimension(pattern)
``` ```
%% Output %% Output
4 4
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pf.get_pattern_size(pattern) pf.get_pattern_size(pattern)
``` ```
%% Output %% Output
13 13
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Flattening patterns ## Flattening patterns
Two functions exist in order to turn a pattern (nested lists) into a simple list. We call this operation "flattening". Two functions exist in order to turn a pattern (nested lists) into a simple list. We call this operation "flattening".
The first one flattens everything in the pattern, including the tuples (which indicate addition): The first one flattens everything in the pattern, including the tuples (which indicate addition):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pf.flatten_pattern(pattern) pf.flatten_pattern(pattern)
``` ```
%% Output %% Output
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The second one keeps tuples as tuples, but flatten the nesting of list: The second one keeps tuples as tuples, but flatten the nesting of list:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pf.flatten_nested_list(pattern) pf.flatten_nested_list(pattern)
``` ```
%% Output %% Output
[0, 1, 2, 3, 4, 5, 6, (7, 8), 9, 10, 11, 12] [0, 1, 2, 3, 4, 5, 6, (7, 8), 9, 10, 11, 12]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
This function may be used in order to keep track of the additions. This function may be used in order to keep track of the additions.
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Applying chords on a pattern ## Applying chords on a pattern
Finally, as polytopes are meant to represent musical elements, we can apply a segment/list of chords on a pattern. Note though that this function is developped for **visualization purpose only**, and is not adapted for cost or complexity computation. Finally, as polytopes are meant to represent musical elements, we can apply a segment/list of chords on a pattern. Note though that this function is developped for **visualization purpose only**, and is not adapted for cost or complexity computation.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
segment = ['Eb', 'Eb', 'Eb', 'Eb', 'Abm', 'Abm', 'Abm', 'Abm', 'E', 'E', 'E', 'E', 'F#', 'F#', 'Eb', 'Eb', 'Abm', 'Abm', 'Abm', 'Abm', 'Abm', 'Abm', 'E', 'Eb', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ab', 'Ebm', 'Ebm', 'Ebm', 'Ebm'] segment = ['Eb', 'Eb', 'Eb', 'Eb', 'Abm', 'Abm', 'Abm', 'Abm', 'E', 'E', 'E', 'E', 'F#', 'F#', 'Eb', 'Eb', 'Abm', 'Abm', 'Abm', 'Abm', 'Abm', 'Abm', 'E', 'Eb', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ab', 'Ebm', 'Ebm', 'Ebm', 'Ebm']
print("List of chords:\n{}\n".format(segment)) print("List of chords:\n{}\n".format(segment))
size = len(segment) size = len(segment)
adding_code, deleting_code = pf.get_unique_codes(size)[0] adding_code, deleting_code = pf.get_unique_codes(size)[0]
import math import math
dimension = round(math.log(size,2)) dimension = round(math.log(size,2))
#pattern = pf.make_polytope_pattern(dimension = dimension, adding_code = adding_code, deleting_code = deleting_code) #pattern = pf.make_polytope_pattern(dimension = dimension, adding_code = adding_code, deleting_code = deleting_code)
pattern = pf.make_indexed_pattern(dimension = dimension, adding_code = adding_code, deleting_code = deleting_code) pattern = pf.make_indexed_pattern(dimension = dimension, adding_code = adding_code, deleting_code = deleting_code)
print("The pattern:\n{}\n".format(pattern)) print("The pattern:\n{}\n".format(pattern))
chords_on_pattern = pf.apply_chords_on_pattern(pattern, segment) chords_on_pattern = pf.apply_chords_on_pattern(pattern, segment)
print("The pattern, with chords as elements:\n{}\n".format(chords_on_pattern)) print("The pattern, with chords as elements:\n{}\n".format(chords_on_pattern))
``` ```
%% Output %% Output
List of chords: List of chords:
['Eb', 'Eb', 'Eb', 'Eb', 'Abm', 'Abm', 'Abm', 'Abm', 'E', 'E', 'E', 'E', 'F#', 'F#', 'Eb', 'Eb', 'Abm', 'Abm', 'Abm', 'Abm', 'Abm', 'Abm', 'E', 'Eb', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ab', 'Ebm', 'Ebm', 'Ebm', 'Ebm'] ['Eb', 'Eb', 'Eb', 'Eb', 'Abm', 'Abm', 'Abm', 'Abm', 'E', 'E', 'E', 'E', 'F#', 'F#', 'Eb', 'Eb', 'Abm', 'Abm', 'Abm', 'Abm', 'Abm', 'Abm', 'E', 'Eb', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ebm', 'Ab', 'Ebm', 'Ebm', 'Ebm', 'Ebm']
The pattern: The pattern:
[[[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]], [[[[16, 17], [18, 19]], [[20, 21], [22, 23]]], [[[24, 25], [26, 27]], [[(28, 29), (30, 31)], [(32, 33), (34, 35)]]]]] [[[[[0, 1], [2, 3]], [[4, 5], [6, 7]]], [[[8, 9], [10, 11]], [[12, 13], [14, 15]]]], [[[[16, 17], [18, 19]], [[20, 21], [22, 23]]], [[[24, 25], [26, 27]], [[(28, 29), (30, 31)], [(32, 33), (34, 35)]]]]]
The pattern, with chords as elements: The pattern, with chords as elements:
[[[[['Eb', 'Eb'], ['Eb', 'Eb']], [['Abm', 'Abm'], ['Abm', 'Abm']]], [[['E', 'E'], ['E', 'E']], [['F#', 'F#'], ['Eb', 'Eb']]]], [[[['Abm', 'Abm'], ['Abm', 'Abm']], [['Abm', 'Abm'], ['E', 'Eb']]], [[['Ebm', 'Ebm'], ['Ebm', 'Ebm']], [[('Ebm', 'Ebm'), ('Ebm', 'Ab')], [('Ebm', 'Ebm'), ('Ebm', 'Ebm')]]]]] [[[[['Eb', 'Eb'], ['Eb', 'Eb']], [['Abm', 'Abm'], ['Abm', 'Abm']]], [[['E', 'E'], ['E', 'E']], [['F#', 'F#'], ['Eb', 'Eb']]]], [[[['Abm', 'Abm'], ['Abm', 'Abm']], [['Abm', 'Abm'], ['E', 'Eb']]], [[['Ebm', 'Ebm'], ['Ebm', 'Ebm']], [[('Ebm', 'Ebm'), ('Ebm', 'Ab')], [('Ebm', 'Ebm'), ('Ebm', 'Ebm')]]]]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# Indexing elements, antecedents and successors # Indexing elements, antecedents and successors
In this part, we will focus on linking and accessing to elements in the pattern. More particularily, we will focus on the paradigm developed by C. Guichaoua [1]. In this paradigm, elements are studied in comparison with the previous ones. In that sense, we need to know the position of each element, and to be able to compare this position with others. In this part, we will focus on linking and accessing elements in the pattern. More particularily, we will focus on the paradigm developed by C. Guichaoua [1]. In this paradigm, elements are studied in comparison with the previous ones. In that sense, we need to know the position of each element, and to be able to compare this position with others.
**Disclaimer: as we need to access to particular elements, all patterns will have to be indexed ones.** **Disclaimer: as we need to access to particular elements, all patterns will have to be indexed ones.**
All the code related to this part is contained in the file pattern_manip.py. All the code related to this part is contained in the file pattern_manip.py.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
from polytopes import pattern_manip as pm from polytopes import pattern_manip as pm
``` ```
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Index of an element ## Index of an element
We developped a system of indexation for every element, based on dichotomy. To find an element, we will recursively indicate whether the element is in the first (left) or the second (right) nested polytope, and search deeper into it. To be precise, we will index each element with a list of booleans, where: We developped a system of indexation for every element, based on dichotomy. To find an element, we will recursively indicate whether the element is in the first (left) or the second (right) nested polytope, and search deeper into it. To be precise, we will index each element with a list of booleans, where:
- 0 indicates that this element is on the 1st (left) nested polytope, - 0 indicates that this element is on the 1st (left) nested polytope,
- 1 indicates that this element is on the 2nd (right) nested polytope. - 1 indicates that this element is on the 2nd (right) nested polytope.
With recursion, we can find every element with as much booleans as the dimension of the polytope. With recursion, we can find every element with as much booleans as the dimension of the polytope.
<img src="imgs/index_gif.gif" width="700"/> <img src="imgs/index_gif.gif" width="700"/>
%% Cell type:markdown id: tags:
A special case appears for additions in the polytope, because we need to specify which element on the added edge we are studying. Similarly to addition in patterns, we use tuples to indicate which element we are looking at. To remain consistent with the dichotomy principle, the tuple contains two booleans, the first one indicating the position of the edge in the last dimension (as for any other element), and the second representing the position of the element on the edge (0 for left, *i.e.* the original element, and 1 for the right, *i.e.* the added element). A special case appears for additions in the polytope, because we need to specify which element on the added edge we are studying. Similarly to addition in patterns, we use tuples to indicate which element we are looking at. To remain consistent with the dichotomy principle, the tuple contains two booleans, the first one indicating the position of the edge in the last dimension (as for any other element), and the second representing the position of the element on the edge (0 for left, *i.e.* the original element, and 1 for the right, *i.e.* the added element).
Using a tuple has the advantage of specifying we're dealing with an addition not another dimension, which could be hard to differentiate otherwise. In addition, indexed still contains $d$ elements (with $d$ the dimension), with the last element being a tuple with two booleans rather than a unique boolean. Using a tuple has the advantage of specifying that we're dealing with an addition, and not another dimension. In addition, indexes still contains $d$ elements (with $d$ the dimension), with the last element being a tuple with two booleans rather than a unique boolean.
<img src="imgs/indexes_examples.png" width="700"/> <img src="imgs/indexes_examples.png" width="700"/>
In both cases, one should use function ``get_index_of_element``, as shown below In both cases, one should use function ``get_index_of_element``, as shown below
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [0,1,0], deleting_code = []) pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [0,1,0], deleting_code = [])
ind_three = pm.get_index_of_element(3, pattern) ind_three = pm.get_index_of_element(3, pattern)
print("a_3 has index: {}".format(ind_three)) print("a_3 has index: {}".format(ind_three))
ind_five = pm.get_index_of_element(5, pattern) ind_five = pm.get_index_of_element(5, pattern)
print("a_5 has index: {}".format(ind_five)) print("a_5 has index: {}".format(ind_five))
ind_six = pm.get_index_of_element(6, pattern) ind_six = pm.get_index_of_element(6, pattern)
print("a_6 has index: {}".format(ind_six)) print("a_6 has index: {}".format(ind_six))
``` ```
%% Output %% Output
a_3 has index: [0, 1, 1] a_3 has index: [0, 1, 1]
a_5 has index: [1, 0, (1, 0)] a_5 has index: [1, 0, (1, 0)]
a_6 has index: [1, 0, (1, 1)] a_6 has index: [1, 0, (1, 1)]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The inverse operation also exists, with the function ``get_element_with_index``: The inverse operation also exists, with the function ``get_element_with_index``:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [0,1,0], deleting_code = []) pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [0,1,0], deleting_code = [])
element = pm.get_element_with_index([0,1,1], pattern) element = pm.get_element_with_index([0,1,1], pattern)
print("[0,1,1] is the element: {}".format(element)) print("[0,1,1] is the element: {}".format(element))
element = pm.get_element_with_index([1, 0, (1, 0)], pattern) element = pm.get_element_with_index([1, 0, (1, 0)], pattern)
print("[1, 0, (1, 0)] is the element: {}".format(element)) print("[1, 0, (1, 0)] is the element: {}".format(element))
element = pm.get_element_with_index([1, 0, (1, 1)], pattern) element = pm.get_element_with_index([1, 0, (1, 1)], pattern)
print("[1, 0, (1, 0)] is the element: {}".format(element)) print("[1, 0, (1, 0)] is the element: {}".format(element))
element = pm.get_element_with_index([0, 0, (1, 1)], pattern) element = pm.get_element_with_index([0, 0, (1, 1)], pattern)
print("[0, 0, (1, 0)] is the element: {} (should be None, as there is no such element added)".format(element)) print("[0, 0, (1, 0)] is the element: {} (should be None, as there is no such element added)".format(element))
``` ```
%% Output %% Output
[0,1,1] is the element: 3 [0,1,1] is the element: 3
[1, 0, (1, 0)] is the element: 5 [1, 0, (1, 0)] is the element: 5
[1, 0, (1, 0)] is the element: 6 [1, 0, (1, 0)] is the element: 6
[0, 0, (1, 0)] is the element: None (should be None, as there is no such element added) [0, 0, (1, 0)] is the element: None (should be None, as there is no such element added)
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
## Antecedents and successors ## Antecedents and successors
A new concept, defined in [1], is the **antecedent** of an element. A new concept, defined in [1], is the **antecedent** of an element.
Looking at a polytope as an oriented graph, edges become oriented arrows, oriented in chronological order ($a_0$ &rarr; $a_1$). In this example, $a_0$ is originating an arrow pointing towards $a_1$. $a_0$ is called "antecedent" of $a_1$, and $a_1$ is called "successor" of $a_0$. Looking at a polytope as an oriented graph, edges become oriented arrows, oriented in chronological order ($a_0$ &rarr; $a_1$). In this example, $a_0$ is spawning an arrow pointing towards $a_1$. $a_0$ is called "antecedent" of $a_1$, and $a_1$ is called "successor" of $a_0$.
Hence, the antecedents of an element are all elements originating an arrow pointing towards them, and the successors of an element are the destination of all arrows they originate. Hence, the antecedents of an element are all elements spawning an arrow pointing towards them, and the successors of an element are the tip of all arrows they spawn.
For instance, in the following polytope, $a_3$ has two antecedents ($a_1$ and $a_2$), $a_4$ has one ($a_3$) and $a_5$ has one ($a_0$). For instance, in the following polytope, $a_3$ has two antecedents ($a_1$ and $a_2$), $a_4$ has one ($a_3$) and $a_5$ has one ($a_0$).
<img src="imgs/dim_3_add_100_del_001.png" width="200"/> <img src="imgs/dim_3_add_100_del_001.png" width="200"/>
> NB: the definition of antecedents in [1] is wider, including every element originating an oriented *path* to the current one. In that definition, $a_0$ would be antecedent of every element. We didn't followed this definition and restricted ourselves with "direct" antecedents, *i.e.* element originating a direct arrow with the current element. > NB: the definition of antecedents in [1] is wider, including every element originating an oriented *path* to the current one. In that definition, $a_0$ would be antecedent of every element. We didn't followed this definition and restricted ourselves with "direct" antecedents, *i.e.* element spawning a direct arrow with the current element.
Antecedents of an elements can be computed using the functions ``get_antecedents_from_element`` or ``get_antecedents_from_idx`` depending or whether the studied element is in the form of an element (element 1, 2, 3, etc) or as an index. Antecedents of an elements can be computed using the functions ``get_antecedents_from_element`` or ``get_antecedents_from_idx`` depending or whether the studied element is in the form of an element (element 1, 2, 3, etc) or as an index.
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1]) pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1])
ant_elt = pm.get_antecedents_from_element(3, pattern) ant_elt = pm.get_antecedents_from_element(3, pattern)
print("Antecedents of the element 3: {}".format(ant_elt)) print("Antecedents of the element 3: {}".format(ant_elt))
ant_idx = pm.get_antecedents_from_idx([0,1,1], pattern) ant_idx = pm.get_antecedents_from_idx([0,1,1], pattern)
print("Antecedents of the element [0,1,1]: {}".format(ant_idx)) print("Antecedents of the element [0,1,1]: {}".format(ant_idx))
``` ```
%% Output %% Output
Antecedents of the element 3: [1, 2] Antecedents of the element 3: [1, 2]
Antecedents of the element [0,1,1]: [1, 2] Antecedents of the element [0,1,1]: [1, 2]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
One can also retreive the index of the antecedent from the index of the element, with the function ``get_antecedents_idx_from_idx``: One can also retreive the index of the antecedent from the index of the element, with the function ``get_antecedents_idx_from_idx``:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pm.get_antecedents_idx_from_idx([0,1,1]) pm.get_antecedents_idx_from_idx([0,1,1])
# This function does not need the pattern, but some of its outputs may not exist in every pattern # This function does not need the pattern, but some of its outputs may not exist in every pattern
``` ```
%% Output %% Output
[[0, 0, 1], [0, 1, 0]] [[0, 0, 1], [0, 1, 0]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
In the paradigm of [1], each antecedent is associated with a "pivot" element, in order to construct a square system (implication system of 4 elements) with the primer ($a_0$, the first element of the polytope). In our example above, if we consider the antecedent $a_5$ of $a_6$, its pivot is $a_1$, because it defines a square system $(a_0, a_1, a_5, a_6)$. For further details, the reader should refer to [1] or [TODO: Add My Own Reference]. In the paradigm of [1], each antecedent is associated with a "pivot" element, in order to construct a square system (implication system of 4 elements) with the primer ($a_0$, the first element of the polytope). In our example above, if we consider the antecedent $a_5$ of $a_6$, its pivot is $a_1$, because it defines a square system $(a_0, a_1, a_5, a_6)$. For further details, the reader should refer to [1] or [TODO: Add My Own Reference].
In the code, this is made with the function ``get_pivot_from_idx`` (NB: the antecedent needs to be under its index form, and it returns the pivot as element, not index): In the code, this is made with the function ``get_pivot_from_idx`` (NB: the antecedent needs to be under its index form, and it returns the pivot as element, not index):
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1]) pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1])
element = 6 element = 6
elt_idx = pm.get_index_of_element(element, pattern) elt_idx = pm.get_index_of_element(element, pattern)
ant_idx = pm.get_antecedents_idx_from_idx(elt_idx)[0] ant_idx = pm.get_antecedents_idx_from_idx(elt_idx)[0]
pivot = pm.get_pivot_from_idx(elt_idx, ant_idx, pattern) pivot = pm.get_pivot_from_idx(elt_idx, ant_idx, pattern)
print("Element: {}, antecedent: {}, pivot: {}".format(element, pm.get_element_with_index(ant_idx, pattern), pivot)) print("Element: {}, antecedent: {}, pivot: {}".format(element, pm.get_element_with_index(ant_idx, pattern), pivot))
``` ```
%% Output %% Output
Element: 6, antecedent: 1, pivot: 5 Element: 6, antecedent: 1, pivot: 5
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
Finally, the function ``get_antecedents_with_pivots_from_idx`` returns couples (as tuples) of antecedents and their pivot from the index of an element: Finally, the function ``get_antecedents_with_pivots_from_idx`` returns couples (as tuples) of antecedents and their pivot from the index of an element:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pm.get_antecedents_with_pivots_from_idx(elt_idx, pattern) pm.get_antecedents_with_pivots_from_idx(elt_idx, pattern)
``` ```
%% Output %% Output
[(1, 5), (5, 1)] [(1, 5), (5, 1)]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
The same functions exist for successors of an element, *i.e.* ``get_successors_from_element``, ``get_successors_from_idx``, which returns the successors of an element (under its element form) when, respectively, it's as an element and as an index, and the function `get_successors_idx_from_idx`, which returns the successors of an element under its index form, from the element as an index. The same functions exist for successors of an element, *i.e.* ``get_successors_from_element``, ``get_successors_from_idx``, which returns the successors of an element (under its element form) when, respectively, it's as an element and as an index, and the function `get_successors_idx_from_idx`, which returns the successors of an element under its index form, from the element as an index.
For example, to find the successors of $a_1$ (which are $a_3$ and $a_6$), one can use the functions: For example, to find the successors of $a_1$ (which are $a_3$ and $a_6$), one can use the functions:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1]) pattern = pf.make_indexed_pattern(dimension = 3, adding_code = [1,0,0], deleting_code = [0,0,1])
element = 1 element = 1
suc_elt_from_elt = pm.get_successors_from_element(element, pattern) suc_elt_from_elt = pm.get_successors_from_element(element, pattern)
print("Successors of {}, as an element: {}".format(element, suc_elt_from_elt)) print("Successors of {}, as an element: {}".format(element, suc_elt_from_elt))
elt_idx = pm.get_index_of_element(element, pattern) elt_idx = pm.get_index_of_element(element, pattern)
suc_elt_from_idx = pm.get_successors_from_idx(elt_idx, pattern) suc_elt_from_idx = pm.get_successors_from_idx(elt_idx, pattern)
print("Successors of {}, as an index: {}".format(element, suc_elt_from_idx)) print("Successors of {}, as an index: {}".format(element, suc_elt_from_idx))
suc_idx_from_idx = pm.get_successors_idx_from_idx(elt_idx) suc_idx_from_idx = pm.get_successors_idx_from_idx(elt_idx)
print("Successors of {}, as an index: {}".format(elt_idx, suc_idx_from_idx)) print("Successors of {}, as an index: {}".format(elt_idx, suc_idx_from_idx))
``` ```
%% Output %% Output
Successors of 1, as an element: [6, 3] Successors of 1, as an element: [6, 3]
Successors of 1, as an index: [6, 3] Successors of 1, as an index: [6, 3]
Successors of [0, 0, 1], as an index: [[1, 0, 1], [0, 1, 1]] Successors of [0, 0, 1], as an index: [[1, 0, 1], [0, 1, 1]]
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
# PPP # PPP
An interesting paradigm developed in [2] by C. Louboutin is the PPP, for Primer Preserving Permutation. The idea is to look at relation in a non-sequential way, and try to find implication systems which explain a music sequence in a different logic, and with different functions between elements (looking at the first beats of all bars between them, then second beats between them, etc). An interesting paradigm developed in [2] by C. Louboutin is the PPP, for Primer Preserving Permutation. The idea is to look at relation in a non-sequential way, and try to find implication systems which explain a music sequence in a different logic, and with different functions between elements (looking at the first beats of all bars between them, then second beats between them, etc).
In this seminal work, PPP were only defined for regular polytopes, *i.e.* polytopes without alteration, with $2^n$ element ($n$ being the dimension). An illustration example is shown below: In this seminal work, PPP were only defined for regular polytopes, *i.e.* polytopes without alteration, with $2^n$ element ($n$ being the dimension). An illustration example is shown below:
<img src="imgs/ppp_16.png" width="400"/> <img src="imgs/ppp_16.png" width="400"/>
%% Cell type:markdown id: tags: %% Cell type:markdown id: tags:
This work has been extended to irregular polytopes, and can be used with the function `generate_ppp`, starting from an indexed pattern. This work has been extended to irregular polytopes, and can be used with the function `generate_ppp`, starting from an indexed pattern.
> This extension is not explained here, but is in [TODO: Add My Own Reference] (not released yet, so this document should be updated with the reference when available). In two words, the idea is to define the interesting faces with different couples of edges (seen as vectors), and compute polytope through these faces. In that sense, alteration does not change the paradigm, and are permuted too. > This extension is not explained here, but is in [TODO: Add My Own Reference] (not released yet, so this document should be updated with the reference when available). In a nutshell, the idea is to define the interesting faces with different couples of edges (seen as vectors), and compute polytope through these faces. In that sense, alteration does not change the paradigm, and are permuted too.
An example of PPP on an irregular polytope is presented in the figure below: An example of PPP on an irregular polytope is presented in the figure below:
<img src="imgs/irregular_ppp.png" width="800"/> <img src="imgs/irregular_ppp.png" width="800"/>
The code is presented below: The code is presented below:
%% Cell type:code id: tags: %% Cell type:code id: tags:
``` python ``` python
pattern = pf.make_indexed_pattern(dimension = 4, adding_code = [1,0,0,1], deleting_code = [0,0,0,1]) pattern = pf.make_indexed_pattern(dimension = 4, adding_code = [1,0,0,1], deleting_code = [0,0,0,1])
print("Pattern: {}\n".format(pattern)) print("Pattern: {}\n".format(pattern))
all_ppps = pm.generate_ppp(pattern) all_ppps = pm.generate_ppp(pattern)
for idx, a_ppp in enumerate(all_ppps): for idx, a_ppp in enumerate(all_ppps):
print("PPP {} of this pattern:\n{}\n".format(idx, a_ppp)) print("PPP {} of this pattern:\n{}\n".format(idx, a_ppp))
``` ```
%% Output %% Output
Pattern: [[[[0, 1], [2, 3]], [[4, 5], [(6, 7), (8, 9)]]], [[[10, 11], [12, 13]], [[14, 15]]]] Pattern: [[[[0, 1], [2, 3]], [[4, 5], [(6, 7), (8, 9)]]], [[[10, 11], [12, 13]], [[14, 15]]]]
PPP 0 of this pattern: PPP 0 of this pattern:
[[[[0, 1], [2, 3]], [[4, 5], [(6, 7), (8, 9)]]], [[[10, 11], [12, 13]], [[14, 15]]]] [[[[0, 1], [2, 3]], [[4, 5], [(6, 7), (8, 9)]]], [[[10, 11], [12, 13]], [[14, 15]]]]
PPP 1 of this pattern: PPP 1 of this pattern:
[[[[0, 1], [4, 5]], [[2, 3], [(6, 7), (8, 9)]]], [[[10, 11], [14, 15]], [[12, 13]]]] [[[[0, 1], [4, 5]], [[2, 3], [(6, 7), (8, 9)]]], [[[10, 11], [14, 15]], [[12, 13]]]]
PPP 2 of this pattern: PPP 2 of this pattern:
[[[[0, 1], [10, 11]], [[2, 3], [12, 13]]], [[[4, 5], [14, 15]], [[(6, 7), (8, 9)]]]] [[[[0, 1], [10, 11]], [[2, 3], [12, 13]]], [[[4, 5], [14, 15]], [[(6, 7), (8, 9)]]]]
PPP 3 of this pattern: PPP 3 of this pattern:
[[[[0, 2], [4, (6, 7)]], [[1, 3], [5, (8, 9)]]], [[[10, 12], [14]], [[11, 13], [15]]]] [[[[0, 2], [4, (6, 7)]], [[1, 3], [5, (8, 9)]]], [[[10, 12], [14]], [[11, 13], [15]]]]
PPP 4 of this pattern: PPP 4 of this pattern:
[[[[0, 2], [10, 12]], [[1, 3], [11, 13]]], [[[4, (6, 7)], [14]], [[5, (8, 9)], [15]]]] [[[[0, 2], [10, 12]], [[1, 3], [11, 13]]], [[[4, (6, 7)], [14]], [[5, (8, 9)], [15]]]]
PPP 5 of this pattern: PPP 5 of this pattern:
[[[[0, 4], [10, 14]], [[1, 5], [11, 15]]], [[[2, (6, 7)], [12]], [[3, (8, 9)], [13]]]] [[[[0, 4], [10, 14]], [[1, 5], [11, 15]]], [[[2, (6, 7)], [12]], [[3, (8, 9)], [13]]]]
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment