DYCI2’s documentation¶
Introduction¶
This version of the DYCI2 library is the first release of a work in progress: a set of models and tools for creative generation of sequences (and in particular musical sequences) from models of sequences. It implements several models, generative heuristics, time management strategies, and architectures of interactive agents.
Repository: https://forge.ircam.fr/p/DYCI2_library/
DYCI2 (“Creative Dynamics of Improvised Interaction”) is a collaborative research and development project funded by the French National Research Agency (ANR). It explores the creative dynamics of improvised interactions between human and artificial agents, featuring an informed artificial listening scheme, a musical structure discovery and learning scheme, and a generalized interaction / knowledge / decision dynamics scheme (see http://repmus.ircam.fr/dyci2/home). The DYCI2 library is part of the DYCI2 project, it is conceived as an autonomous and easily extensible Python library, and can also be used in association with audio or midi listeners and renderers to form DYCI2 agents (see directory “MaxPatches”).
More information on the project: refer to Nika, Déguernel, Chemla–Romeu-Santos, Vincent, Assayag, “DYCI2 agents: merging the “free”, “reactive”, and “scenario-based” music generation paradigms”, in Proceedings of International Computer Music Conference 2017 (https://hal.archives-ouvertes.fr/hal-01583089/document) (please mention this paper to mention the library).
To use the library: see the tuturials in the root directory.
- In this version:
- Definitive architecture of the library: classes Model, Navigator, MetaModelNavigator, Query, Generator, GenerationHandler, and OSCAgent.
- Models: Naive sequence navigation, Factor Oracle automaton.
- Associated navigation strategies: free generation (“Omax-like”), single target generation, scenario-based generation (“ImproteK-like”).
- Architectures of generative agent: Generator and GenerationHandler (see doc), demand-driven generative agents processing queries expressed in “events”. The (short-term and long-term) queries communicate through a shared execution trace to maintain consistency of the generated sequence when rewriting previously generated anticipations.
- Communication: OSC agents embedding the generative agents.
- Alphabets: Generic list labels, Chord Labels.
- Documentation: modules Model, Navigator, PrefixIndexing, MetaModelNavigator, Generator, GeneratorBuilder.
- Tutorials: FactorOracle, PrefixIndexing, Intervals, MetaModelNavigator, FactorOracleNavigator, Generator, GenerationHandler, OSCAgent_Tutorial_1 (text).
- Next steps:
- Models: N-gram.
- Associated navigation strategies: Navigator including a notion of “activity profile”, free generation and single target generation using this navigator (“Somax-like”).
- Architectures of generative agent: “Algebra” of queries, queries expressed in “ms”. output of the queries not directly outputted but redirected to a choice method that will be in charge of outputting the result –> new playing modes “hard hybrid guidance” (long-term queries > short-term queries) and “soft hybrid guidance” (short-term queries > long-term queries).
- Alphabets: Class “contents”.
- Documentation: modules Label, Transforms, Intervals, ParseAnnotationFiles, CorpusBuilder, OSCAgent.
- Tutorials: GeneratorBuilder, OSCAgent_Tutorial_2 (audio), OSCAgent_Tutorial_3 (midi).
Overview¶

Model
is an abstract class. Any new model of sequence must inherit from this class. The classes inheriting from this class are minimal and only implement the construction algorithms and basic methods. Navigation and creative aspects are handled by the classes introduced below. Models of sequence implemented so far: Factor Oracle Automaton (FactorOracle
).¶

The class Navigator
implements parameters and methods that are used to navigate through a model of sequence. These parameters and methods are model-independent. This class defines in particular the naive versions of the methods simply_guided_generation()
and free_generation()
handling the navigation through a sequence when it is respectively guided by target labels and free. These methods are overloaded by model-dependant versions (and other model-dependent parameters or methods can be added) when creating a model navigator class (cf. below).¶

MetaModelNavigator
is a metaclass. A new class created using this metaclass is a model navigator and inherits from: 1) a class inheriting from Model
, 2) a class inheriting from Navigator
. A model navigator implements the different algorithms, strategies, and heuristics to navigate through a given model of sequence for analysis or creative applications, for example generating new sequences using concatenative synthesis of events learned in the model. The class FactorOracleNavigator
introduced below is an example of model navigator created using this metaclass.¶

The class FactorOracleNavigator
implements different algorithms, strategies, and heuristics to navigate through a Factor Oracle Automaton for creative applications. The creation of the class FactorOracleNavigator
in the file ModelNavigator.py
gives an example of easy definition of a new class of model navigator using the metaclass MetaModelNavigator
: 1) chose two bases (here, model = FactorOracle
, navigator = Navigator
), 2) define the methods to overload simply_guided_generation()
and free_generation()
(here ModelNavigator.FactorOracleNavigator.simply_guided_navigation()
and ModelNavigator.FactorOracleNavigator.free_navigation()
)¶

The class Generator
embeds a model navigator (cf. metaclass MetaModelNavigator
) as “memory” and processes queries (class Query
) to generate new sequences. This class uses pattern matching techniques (cf. PrefixIndexing
) to enrich the navigation and generation methods offered by the chosen model. The class GenerationHandler
introduces time management and planning for interactive applications and adds a pool of query, concurrency (e.g. processing of concurrent queries), etc. to the class Generator
.¶

The class Server
defines an OSC (http://opensoundcontrol.org) server to communicate with external applications. The class OSCAgent
defines a process embedding an instance of GenerationHandler
and its own OSC sender, receiver, settings, etc. to receive queries and control parameters and send generation outputs. Both classes can be used in particular in association with a Max interface handling audio or midi rendering.¶
Indices and tables¶
Models and navigation¶
Model¶
This module defines different models of symbolic sequences.
The classes defined in this module are minimal and only implement the construction algorithms and basic methods. Navigation and creative aspects are handled by other classes in the library (cf. Navigator
and ModelNavigator
).
Main classes: Model
, FactorOracle
.
Tutorial for the class FactorOracle
in _Tutorials_/FactorOracleAutomaton_tutorial.py
.
-
class
Model.
FactorOracle
(sequence=[], labels=[], equiv=<function <lambda>>)¶ Bases:
Model.Model
Factor Oracle automaton class. Implementation of the Factor Oracle Automaton (Allauzen, Crochemore, Raffinot, 1999). Convention: since the all the transitions arriving in a same state have the same label, the labels are not carried by the transitions but by the states.
Parameters: - sequence (list or str) – sequence learnt in the Factor Oracle automaton
- labels (list or str) – sequence of labels chosen to describe the sequence
- direct_transitions (dict) – direct transitions in the automaton (key = index state 1, value = tuple: label, index state 2)
- factor_links (dict) – factor links in the automaton (key = index state 1, value = list of tuples: (label, index state 2)
- suffix_links (dict) – suffix links in the automaton (key = index state 1, value = index state 2)
- reverse_suffix_links (dict) – reverse suffix links in the automaton (key = index state 1, value = list of index state 2)
- equiv (function) – compararison function given as a lambda function, default: (lambda x,y : x == y).
!: equiv has to be consistent with the type of the elements in labels.
See also: Tutorial in
_Tutorials_/FactorOracleAutomaton_tutorial.py
.(When there is no need to distinguish the sequence and its labels : FactorOracle(sequence,sequence).)
Example: >>> sequence = ['A','B','B','C','A','B','C','D','A','B','C'] >>> FO = FactorOracle(sequence, sequence) >>> >>> sequence = ['A1','B1','B2','C1','A2','B3','C2','D1','A3','B4','C3'] >>> labels = [s[0] for s in sequence] >>> FO_2 = FactorOracle(sequence, labels) >>> >>> equiv_AC_BD = (lambda x,y: set([x,y]).issubset(set(['A','C'])) or set([x,y]).issubset(set(['B','D']))) >>> FO_3 = FactorOracle(sequence, labels, equiv_AC_BD)
-
add_direct_transition
(index_state1, label, index_state2)¶ Adds a transition labelled by ‘label’ from the state at index ‘index_state1’ to the state at index ‘index_state2’ in the Factor Oracle automaton.
-
add_factor_link
(index_state1, label, index_state2)¶ Adds a factor link labelled by ‘label’ from the state at index ‘index_state1’ to the state at index ‘index_state2’ in the Factor Oracle automaton.
-
add_suffix_link
(index_state1, index_state2)¶ Adds a suffix link (and the associated reverse suffix link) from the state at index ‘index_state1’ to the state at index ‘index_state2’ in the Factor Oracle automaton.
-
continuations
(index_state, forward_context_length_min=1, equiv=None, authorize_direct_transition=True)¶ Possible continuations from the state at index index_state in the automaton, i.e. direct transition and states reached using suffix links and reverse suffix links. These states follow states sharing a common backward context and a common forward context with the state at index index_state in the automaton. The lengths of the common backward contexts are given by the Factor Oracle automaton, the forward context is imposed by a parameter.
Parameters: Returns: Indexes in the automaton of the possible continuations from the state at index index_state in the automaton.
Return type: list (int)
See also: Tutorial in
_Tutorials_/FactorOracleAutomaton_tutorial.py
.!: equiv has to be consistent with the type of the elements in labels.
Example: >>> sequence = ['A1','B1','B2','C1','A2','B3','C2','D1','A3','B4','C3'] >>> labels = [s[0] for s in sequence] >>> FON = FactorOracleNavigator(sequence, labels) >>> >>> index = 6 >>> forward_context_length_min = 1 >>> continuations = FON.continuations(index, forward_context_length_min) >>> print("Possible continuations from state at index {} (with minimum forward context length = {}): {}".format(index, forward_context_length_min, continuations))
-
continuations_with_jump
(authorized_indexes)¶ List of continuations with jumps to indexes with similar contexts direct transition from self.current_navigation_index.
In the method free_generation, this method is called with authorized_indexes = possible continuations filtered to satisfy the constraints of taboos and repetitions. In the method simply_guided_generation, this method is called with authorized_indexes = possible continuations matching the required label filtered to satisfy the constraints of taboos and repetitions.
Parameters: authorized_indexes (list(int)) – list of authorized indexes to filter taboos, repetitions, and label when needed. Returns: indexes of the states Return type: list(int)
-
continuations_with_label
(index_state, required_label, forward_context_length_min=1, equiv=None, authorize_direct_transition=True)¶ Possible continuations labeled by required_label from the state at index index_state in the automaton.
Parameters: Returns: Indexes in the automaton of the possible continuations labeled by required_label from the state at index index_state in the automaton.
Return type: list (int)
See also: method from_state_read_label (class FactorOracle) used in the construction algorithm. Difference : only uses the direct transition and the suffix link leaving the state.
!: equiv has to be consistent with the type of the elements in labels.
-
follow_continuation_using_transition
(authorized_indexes)¶ Continuation using direct transition from self.current_navigation_index.
In the method free_generation, this method is called with authorized_indexes = possible continuations filtered to satisfy the constraints of taboos and repetitions. In the method simply_guided_generation, this method is called with authorized_indexes = possible continuations matching the required label filtered to satisfy the constraints of taboos and repetitions.
Parameters: authorized_indexes (list(int)) – list of authorized indexes to filter taboos, repetitions, and label when needed. Returns: index of the state Return type: int
-
follow_continuation_with_jump
(authorized_indexes)¶ Random selection of a continuation with jump to indexes with similar contexts direct transition from self.current_navigation_index.
In the method free_generation, this method is called with authorized_indexes = possible continuations filtered to satisfy the constraints of taboos and repetitions. In the method simply_guided_generation, this method is called with authorized_indexes = possible continuations matching the required label filtered to satisfy the constraints of taboos and repetitions.
Parameters: authorized_indexes (list(int)) – list of authorized indexes to filter taboos, repetitions, and label when needed. Returns: index of the state Return type: int
-
follow_reverse_suffix_links_from
(index_state)¶ Reverse suffix paths from a given index.
Parameters: index_state (int) – start index Returns: Indexes in the automaton of the states that can be reached from the state at index index_state following reverse suffix links. Return type: list (int)
-
follow_suffix_links_from
(index_state, include_init_state=True)¶ Suffix path from a given index.
Parameters: index_state (int) – start index Returns: Indexes in the automaton of the states that can be reached from the state at index index_state following suffix links. Return type: list (int)
-
follow_suffix_links_then_reverse_from
(index_state)¶ States that can be reached using suffix links from the state at index index_state, and then the reverse suffix links leaving these states.
Parameters: index_state (int) – start index Returns: Indexes in the automaton of the states that can be reached using suffix links from the state at index index_state, and then the reverse suffix links leaving these states. Return type: list (int)
-
from_state_read_label
(index_state, label, equiv=None, authorize_factor_links=True)¶ Reads label ‘label’ from state at index ‘index_state’. First looks for a direct transition, then for a factor link (if authorized).
Parameters: - index_state (int) – Initial state in the Factor Oracle automaton.
- label – Label to read.
- equiv (function) – Compararison function given as a lambda function, default if no parameter is given: self.equiv.
- authorize_factor_links (bool) – Only look for a direct transition (False) or also for a factor link (True).
Returns: Index where the transition leads (when it exists).
Return type:
-
init_model
()¶ Creation of the initial state of the Factor Oracle automaton. (“Empty”, no label, suffix links going “nowhere”)
-
is_recognized
(word, equiv=None)¶ Tests if a word is recognized by the Factor Oracle automaton.
Parameters: - word (list or str) – Input sequence
- equiv (function) – Compararison function given as a lambda function, default if no parameter is given: self.equiv.
!: equiv has to be consistent with the type of label.
See also: Tutorial in
_Tutorials_/FactorOracleAutomaton_tutorial.py
.Example: >>> sequence_FO = "AABBABCBBABAAB" >>> FO = FactorOracle(sequence_FO, sequence_FO) >>> sequence_input_1 = "ABCB" >>> sequence_input_2 = "BBBBBB" >>> print("{} recognized by the Factor Oracle built on {}?\n{}".format(sequence_input_1,sequence_FO,FO.is_recognized(sequence_input_1))) >>> print("{} recognized by the Factor Oracle built on {}?\n{}".format(sequence_input_2,sequence_FO,FO.is_recognized(sequence_input_2)))
-
learn_event
(state, label, equiv=None)¶ Learns (appends) a new state in the Factor Oracle automaton.
Parameters: - state –
- label –
- equiv (function) – Compararison function given as a lambda function, default if no parameter is given: self.equiv.
!: equiv has to be consistent with the type of label.
-
print_model
()¶ Basic representation of a Factor Oracle automaton.
-
similar_backward_context
(index_state)¶ Some states sharing a common (backward) context with the state at index index_state in the automaton.
Parameters: index_state (int) – start index Returns: Indexes in the automaton of the states sharing a common (backward) context with the state at index index_state in the automaton. Return type: list (int) See also: https://hal.archives-ouvertes.fr/hal-01161388 See also: Tutorial in _Tutorials_/FactorOracleAutomaton_tutorial.py
.Example: >>> sequence = ['A1','B1','B2','C1','A2','B3','C2','D1','A3','B4','C3'] >>> labels = [s[0] for s in sequence] >>> FON = FactorOracleNavigator(sequence, labels) >>> >>> index = 6 >>> similar_backward_context = FON.similar_backward_context(index) >>> print("Some states with backward context similar to that of state at index {}: {}".format(index, similar_backward_context))
-
similar_contexts
(index_state, forward_context_length_min=1, equiv=None)¶ Some states sharing a common backward context and a common forward context with the state at index index_state in the automaton. The lengths of the common backward contexts are given by the Factor Oracle automaton, the forward context is imposed by a parameter.
Parameters: Returns: Indexes of the states in the automaton sharing a common backward context and a common forward context with the state at index index_state in the automaton.
Return type: list (int)
See also: Tutorial in
_Tutorials_/FactorOracleAutomaton_tutorial.py
.!: equiv has to be consistent with the type of the elements in labels.
Example: >>> sequence = ['A1','B1','B2','C1','A2','B3','C2','D1','A3','B4','C3'] >>> labels = [s[0] for s in sequence] >>> FON = FactorOracleNavigator(sequence, labels) >>> >>> index = 6 >>> forward_context_length_min = 1 >>> similar_contexts = FON.similar_contexts(index, forward_context_length_min) >>> print("Some states with similar contexts (with minimum forward context length = {}) to that of state at index {}: {}".format(forward_context_length_min, index, similar_contexts))
-
class
Model.
Model
(sequence=[], labels=[], equiv=<function <lambda>>)¶ Bases:
object
The class
Model
is an abstract class. Any new model of sequence must inherit from this class.Parameters: !: equiv has to be consistent with the type of the elements in labels.
-
build
(sequence, labels, equiv=None)¶ Builds the model.
Parameters: !: equiv has to be consistent with the type of the elements in labels.
-
index_last_state
()¶ Index of the last state in the model.
-
init_model
()¶ Initialization method called before learning the sequence in the model.
-
learn_event
(state, label, equiv)¶ Learns (appends) a new state in the model.
Parameters: - state –
- label –
- equiv (function) – Compararison function given as a lambda function, default if no parameter is given: self.equiv.
!: equiv has to be consistent with the type of label.
-
learn_sequence
(sequence, labels, equiv=None)¶ Learns (appends) a new sequence in the model.
Parameters: !: equiv has to be consistent with the type of the elements in labels.
-
print_model
()¶
-
Handle generation¶
Query¶
The class Query
and its subclasses define queries that are then processed by instances of class Generator
or GenerationHandler
to guide or constrain the generation of new sequences.
-
class
Query.
Query
(start_date=0, start_unit='event', start_type='absolute', handle=[None], scope_duration=0, scope_unit='event', behaviour='merge')¶ A query guides or constrains the run of a generation model.
Parameters: - handle (list) – “Handle” of the query / sequence of required labels ([None] for free generation).
- start (dict) – Date concerned by the output of the query (details below).
- start["date"] (int) – Numerical value (unit and relative/absolute time: see below).
- start["unit"] (str) – Unit of the value given in start[“date”]: “event” or “ms”
- start["type"] (str) – Expressed in relative or absolute time ?: “relative” or “absolute”
- scope (dict) – Temporal horizon of the query (details below).
- scope["duration"] (int) – Numerical value (unit: see below).
- scope["unit"] (str) – Unit of the value given in scope[“duration”]: “event” or “ms”
- behaviour (str) – behaviour when a previous query concerned the same dates “merge” or “replace”
- status (str) – “waiting” or “being processed”
-
process
(*args, **kargs)¶
-
relative_to_absolute
(current_performance_time_event=None, current_performance_time_ms=None)¶
-
Query.
new_temporal_query_free_sequence_of_events
(length=1, start_date=0, start_type='relative', behaviour='replace')¶
-
Query.
new_temporal_query_sequence_of_events
(handle=[], start_date=0, start_type='relative', behaviour='replace')¶
Generator¶
This module defines agents generating new sequences from a “memory” (model of sequence) and generation queries.
Main classes: Generator
(oriented towards offline generation), GenerationHandler
(oriented towards interactivity).
-
class
Generator.
GenerationHandler
(sequence=[], labels=[], model_navigator='FactorOracleNavigator', equiv=<function <lambda>>, equiv_mod_interval=<function <lambda>>, authorized_tranformations=[0], sequence_to_interval_fun=<function chord_labels_sequence_to_interval>, continuity_with_future=[0.0, 1.0])¶ Bases:
Generator.Generator
The class GenerationHandler introduces time management and planning for interactive applications and adds a pool of query, concurrency (e.g. processing of concurrent queries), the use of an execution trace etc. to the class
Generator
. More details in Nika, Bouche, Bresson, Chemillier, Assayag, “Guided improvisation as dynamic calls to an offline model”, in Proceedings of Sound and Music Computing conference 2015 (https://hal.archives-ouvertes.fr/hal-01184642/document), describing the first prototype of “improvisation handler”.- The key differences between
Generator
andGenerationHandler
are:
Parameters: - generation_trace (list) – Whole output: current state of the sequence generated from the beginning (
GenerationHandler.start()
). - current_performance_time (dict) – Current time of the performance, see below.
- current_performance_time["event"] (int) – Time expressed in events.
- current_performance_time["ms"] (int) – Time expressed in ms.
- current_performance_time["last_update_event_in_ms"] (int) – Date when the last timing information was received.
- current_generation_time (dict) – Current time of the generation, see below.
- current_generation_time["event"] (int) – Time expressed in events.
- current_generation_time["ms"] (int) – Time expressed in ms.
- current_duration_event_ms (float) – If all the events have (more or less) a same duration (e.g. a clock, pulsed music, non timed sequences…), this attribute is not None. It is then used to convert events into dates in ms.
- query_pool_event (list(
Query
)) – Pool of waiting queries expressed in events. - query_pool_ms (list(
Query
)) – Pool of waiting queries expressed in ms.
See also: GeneratorBuilder
, automatic instanciation of Generator objects and GenerationHandler objects from annotation files.See also: Tutorial in
_Tutorials_/Generator_tutorial.py
.Example: >>> labels = make_sequence_of_chord_labels(["d m7", "d m7", "g 7", "g 7", "c maj7","c maj7","c# maj7","c# maj7", "d# m7", "d# m7", "g# 7", "g# 7", "c# maj7", "c# maj7"]) >>> sequence = make_sequence_of_chord_labels(["d m7(1)", "d m7(2)", "g 7(3)", "g 7(4)", "c maj7(5)","c maj7(6)","c# maj7(7)","c# maj7(8)", "d# m7(9)", "d# m7(10)", "g# 7(11)", "g# 7(12)", "c# maj7(13)", "c# maj7(14)"]) >>> >>> print("\nCreation of a Generation Handler\nModel type = Factor Oracle\nSequence: {}\nLabels: {}".format(sequence, labels)) >>> >>> authorized_intervals = range(-6,6) >>> generation_handler = GenerationHandler(sequence = sequence, labels = labels, model_type = "FactorOracleNavigator", authorized_tranformations = authorized_intervals, sequence_to_interval_fun = chord_labels_sequence_to_interval) >>> generation_handler.memory.avoid_repetitions_mode = 1 >>> generation_handler.memory.max_continuity = 3 >>> generation_handler.memory.no_empty_event = False >>> generation_handler.start()
-
estimation_date_event_of_ms
(date_ms)¶ If all the events have (more or less) a same duration (e.g. a clock, pulsed music, non timed sequences…),
self.current_duration_event_ms
is not None. It is then used to convert dates in ms into indexes of events.Parameters: date_ms (int) – date in ms to convert Returns: estimated corresponding index of event (or None) Return type: int
-
estimation_date_ms_of_event
(num_event)¶ If all the events have (more or less) a same duration (e.g. a clock, pulsed music, non timed sequences…),
self.current_duration_event_ms
is not None. It is then used to convert indexes of events into dates in ms.Parameters: num_event (int) – index of event to convert Returns: estimated corresponding date in ms (or None) Return type: int
-
inc_performance_time
(inc_event=None, inc_ms=None)¶
-
index_previously_generated_event_date_ms
(date_query)¶
-
process_prioritary_query
(unit=None, print_info=False)¶ Processes the prioritary query in the query pool.
-
process_query
(query, print_info=False)¶ - The key differences between
Generator
andGenerationHandler
are:
This methods takes time into account: in addition to what
Generator.process_query()
does, it compares the attributeQuery.start
of the query andself.current_performance_time
to callNavigator.go_to_anterior_state_using_execution_trace()
if needed. This way, it ensures consistency at tiling time when rewriting previously generated anticipations. As inGenerator.process_query()
the output of this query is stored inself.current_generation_output
. In addition it is inserted at the right place in the whole output historyself.generation_trace
.Parameters: query ( Query
) –Returns: query.start[“date”] (converted to “absolute” value) Return type: int - The key differences between
-
receive_query
(query, print_info=False)¶ - The key differences between
Generator
andGenerationHandler
are:
Inserts the received query in the query pool. Handles the interaction between (running and/or waiting) queries: merging compatible queries, killing outdated queries… The queries in the query pool are then run in due time. TODO: for the moment only “append” and immediate processing.
Parameters: query ( Query
) –Example: >>> labels = make_sequence_of_chord_labels(["d m7", "d m7", "g 7", "g 7", "c maj7","c maj7","c# maj7","c# maj7", "d# m7", "d# m7", "g# 7", "g# 7", "c# maj7", "c# maj7"]) >>> sequence = make_sequence_of_chord_labels(["d m7(1)", "d m7(2)", "g 7(3)", "g 7(4)", "c maj7(5)","c maj7(6)","c# maj7(7)","c# maj7(8)", "d# m7(9)", "d# m7(10)", "g# 7(11)", "g# 7(12)", "c# maj7(13)", "c# maj7(14)"]) >>> >>> print("\nCreation of a Generation Handler\nModel type = Factor Oracle\nSequence: {}\nLabels: {}".format(sequence, labels)) >>> >>> authorized_intervals = range(-6,6) >>> generation_handler = GenerationHandler(sequence = sequence, labels = labels, model_type = "FactorOracleNavigator", authorized_tranformations = authorized_intervals, sequence_to_interval_fun = chord_labels_sequence_to_interval) >>> generation_handler.memory.avoid_repetitions_mode = 1 >>> generation_handler.memory.max_continuity = 3 >>> generation_handler.memory.no_empty_event = False >>> generation_handler.start() >>> >>> scenario = make_sequence_of_chord_labels(["g m7", "g m7", "c 7", "c 7", "f maj7", "f maj7"]) >>> query= new_temporal_query_sequence_of_events(scenario) >>> print("\n/!\ Receiving and processing a new query: /!\ \n{}".format(query)) >>> generation_handler.receive_query(query = query, print_info = False) >>> print("Output of the run: {}".format(generation_handler.current_generation_output)) >>> print("/!\ Updated buffered improvisation: {} /!\ ".format(generation_handler.generation_trace)) >>> >>> query= new_temporal_query_free_sequence_of_events(length = 3, start_date = 4, start_type = "absolute") >>> print("\n/!\ Receiving and processing a new query: /!\ \n{}".format(query)) >>> generation_handler.receive_query(query = query, print_info = False) >>> print("Output of the run: {}".format(generation_handler.current_generation_output)) >>> print("/!\ Updated buffered improvisation: {} /!\ ".format(generation_handler.generation_trace))
- The key differences between
-
start
()¶ Sets
self.current_performance_time
to 0.
-
update_performance_time
(date_event=None, date_ms=None)¶
- The key differences between
-
class
Generator.
Generator
(sequence=[], labels=[], model_navigator='FactorOracleNavigtor', equiv=<function <lambda>>, equiv_mod_interval=<function <lambda>>, authorized_tranformations=[0], sequence_to_interval_fun=<function chord_labels_sequence_to_interval>, continuity_with_future=[0.0, 1.0])¶ Bases:
object
The class Generator embeds a model navigator as “memory” (cf. metaclass
MetaModelNavigator
) and processes queries (classQuery
) to generate new sequences. This class uses pattern matching techniques (cf.PrefixIndexing
) to enrich the navigation and generation methods offered by the chosen model with (“ImproteK-like”) anticipative behaviour. More information on “scenario-based generation”: see Nika, “Guiding Human-Computer Music Improvisation: introducing Authoring and Control with Temporal Scenarios”, PhD Thesis, UPMC Paris 6, Ircam, 2016 (https://tel.archives-ouvertes.fr/tel-01361835) and Nika, Chemillier, Assayag, “ImproteK: introducing scenarios into human-computer music improvisation”, ACM Computers in Entertainment, Special issue on Musical metacreation Part I, 2017 (https://hal.archives-ouvertes.fr/hal-01380163).- The key differences between
Generator
andGenerationHandler
are:
Parameters: - model_navigator (str) –
- memory (cf.
ModelNavigator
andMetaModelNavigator
) – “Model navigator” inheriting from (a subclass of)Model
and (a subclass of)Navigator
. - initial_query (bool) –
- current_generation_query (
Query
) – - current_generation_output (list) –
- transfo_current_generation_output (list) –
- continuity_with_future (list) –
- current_transformation_memory (cf.
Transforms
) – - authorized_tranformations (list(int)) –
- sequence_to_interval_fun (function) –
- equiv_mod_interval (function) –
See also: GeneratorBuilder
, automatic instanciation of Generator objects and GenerationHandler objects from annotation files.See also: Tutorial in
_Tutorials_/Generator_tutorial.py
.Example: >>> sequence_1 = ['A1','B1','B2','C1','A2','B3','C2','D1','A3','B4','C3'] >>> labels_1 = [s[0] for s in sequence_1] >>> generator_1 = Generator(sequence=sequence_1, labels=labels_1, model_navigator = "FactorOracleNavigator") >>> >>> sequence_2 = make_sequence_of_chord_labels(["d m7(1)", "d m7(2)", "g 7(3)", "g 7(4)", "c maj7(5)","c maj7(6)","c# maj7(7)","c# maj7(8)", "d# m7(9)", "d# m7(10)", "g# 7(11)", "g# 7(12)", "c# maj7(13)", "c# maj7(14)"]) >>> labels_2 = make_sequence_of_chord_labels(["d m7", "d m7", "g 7", "g 7", "c maj7","c maj7","c# maj7","c# maj7", "d# m7", "d# m7", "g# 7", "g# 7", "c# maj7", "c# maj7"]) >>> authorized_intervals = range(-2,6) >>> generator_2 = Generator(sequence = sequence_2, labels = labels_2, model_navigator = "FactorOracleNavigator", authorized_tranformations = authorized_intervals, sequence_to_interval_fun = chord_labels_sequence_to_interval)
-
decode_memory_with_current_transfo
()¶
-
decode_memory_with_transfo
(transform)¶ Apply the reciprocal transformation of the transformation given in argument to
self.memory.sequence
andself.memory.label
.Parameters: transform (cf. Transforms
) –
-
encode_memory_with_current_transfo
()¶
-
encode_memory_with_transfo
(transform)¶ Apply the transformation given in argument to
self.memory.sequence and :attr:`self.memory.label
.Parameters: transform (cf. Transforms
) –
-
filter_using_history_and_taboos
(list_of_indexes)¶
-
formatted_output_couple_content_transfo
()¶
-
generation_matching_query
(query, print_info=False)¶ - Launches the run of a generation process corresponding to
self.current_generation_query
(more precisely its attributesQuery.handle
andQuery.scope
):
The generated sequence is stored in
self.current_generation_output
.Parameters: query ( Query
) –- Launches the run of a generation process corresponding to
-
handle_free_generation
(length, print_info=False)¶ Generates a sequence using the method
free_generation()
of the model navigator (cf.ModelNavigator
) inself.memory
.Generator.encode_memory_with_current_transfo()
andGenerator.decode_memory_with_current_transfo()
are respectively called before and after this generation.Parameters: length (int) – required length of the sequence Returns: generated sequence Return type: list See also: free_generation()
See also: MetaModelNavigator
-
handle_generation_matching_label
(label, print_info=False)¶ Generates a single event using the method
simply_guided_generation()
of the model navigator (cf.ModelNavigator
) inself.memory
.Generator.encode_memory_with_current_transfo()
andGenerator.decode_memory_with_current_transfo()
are respectively called before and after this generation.Parameters: label (type of the elements in self.memory.label
) – required labelReturns: generated event Return type: type of the elements in self.memory.sequence
See also: simply_generation()
See also: MetaModelNavigator
-
handle_scenario_based_generation
(list_of_labels, print_info=False)¶ Generates a sequence matching a “scenario” (a list of labels). The generation process takes advantage of the scenario to introduce anticatory behaviour, that is, continuity with the future of the scenario. The generation process is divided in successive “generation phases”, cf.
handle_scenario_based_generation_one_phase()
.Parameters: list_of_labels (list or str) – “scenario”, required labels Returns: generated sequence Return type: list
-
handle_scenario_based_generation_one_phase
(list_of_labels, print_info=False, shift_index=0)¶ Parameters: list_of_labels (list or str) – “current scenario”, suffix of the scenario given in argument to Generator.Generator.handle_scenario_based_generation()
.- A “scenario-based” generation phase:
- Anticipation step: looking for an event in the memory sharing a common future with the current scenario.
- Navigation step: starting from the starting point found in the first step, navigation in the memory using
simply_guided_generation()
until launching a new phase is necessary.
-
learn_event
(state, label)¶ Learn a new event in the memory (model navigator).
-
learn_sequence
(new_sequence)¶ Learn a new sequence in the memory (model navigator).
-
process_query
(query, print_info=False)¶ - The key differences between
Generator
andGenerationHandler
are:
This methods stores the query given in argument in
self.current_generation_query
and callsGenerator.generation_matching_query()
to run the execution of a generation process adapted toQuery.handle
andQuery.scope
.Parameters: query ( Query
) –- The key differences between
-
receive_query
(query, print_info=False)¶ - The key differences between
Generator
andGenerationHandler
are:
Here, a query is processed as soon as it is received.
Parameters: query ( Query
) –See also: Generator.process_query()
Example: >>> sequence = ['A1','B1','B2','C1','A2','B3','C2','D1','A3','B4','C3'] >>> labels = [s[0] for s in sequence] >>> generator = Generator(sequence=sequence, labels=labels, model_navigator = "FactorOracleNavigator") >>> print("\nProcessing query 1 - generation guided by a scenario:") >>> query_1 = new_temporal_query_sequence_of_events(['C','A','B','B','C', 'C', 'D']) >>> generator.receive_query(query = query_1, print_info = True) >>> print("Output: {}".format(generator.current_generation_output)) >>> >>> print("\nAfter this generation phase:") >>> print("History and taboos: {}".format(generator.memory.history_and_taboos)) >>> print("Current navigation index: {}".format(generator.memory.current_position_in_sequence)) >>> >>> print("\nProcessing query 2 - free:") >>> query_2 = new_temporal_query_free_sequence_of_events(length = 4) >>> generator.receive_query(query = query_2, print_info = True) >>> print("Output: {}".format(generator.current_generation_output)) >>> >>> print("\nAfter this generation phase:") >>> print("History and taboos: {}".format(generator.memory.history_and_taboos))
- The key differences between
- The key differences between
Pattern matching algorithms¶
Prefix indexing algorithms¶
Algorithms introduced in Nika, “Guiding Human-Computer Music Improvisation: introducing Authoring and Control with Temporal Scenarios”, PhD Thesis, UPMC Paris 6, Ircam, 2016 (https://tel.archives-ouvertes.fr/tel-01361835) and Nika, Chemillier, Assayag, “ImproteK: introducing scenarios into human-computer music improvisation”, ACM Computers in Entertainment, Special issue on Musical metacreation Part I, 2017 (https://hal.archives-ouvertes.fr/hal-01380163).
Tutorial in _Tutorials_/PrefixIndexing_tutorial.py
.
-
PrefixIndexing.
add_shorter_prefixes
(longest_prefixes_pattern_left_pos_in_sequence, internal_prefixes_in_pattern, j, i, print_info=0)¶ Sub-routine used in
PrefixIndexing.prefix_indexing()
.Given the prefixes of the pattern found so far in the sequence at step i,j in
PrefixIndexing.prefix_indexing()
(only the longest in the region [j-length_longest_prefix(step i,j),j-1]), return all the prefixes of the pattern in the sequence at step i,j using the previously computed internal prefixes in the pattern (PrefixIndexing.failure_function_and_right_pos_prefixes()
).Parameters: - longest_prefixes_pattern_left_pos_in_sequence (dict (int -> list of int)) – prefixes of the pattern found so far (only the longest in region [j-length_longest_prefix,j-1]) in the sequence at step i,j of the research in PrefixIndexing.prefix_indexing (key = length, value = list of left positions of the prefixes of the pattern of length ‘length’ in the sequence)
- internal_prefixes_in_pattern (dict (int -> list of int)) – prefixes of the pattern in itself resulting from a call to
PrefixIndexing.failure_function_and_right_pos_prefixes()
(key = index in the pattern (from 0), value = list: lengths of the prefixes of the pattern in itself ending at the corresponding index) - j (int) – current position of the cursor in the sequence
- i (int) – current position of the cursor in the pattern
- print_info (int) – print the details of the research?
Returns: all the prefixes of the pattern of the sequence at step i,j of the research (
PrefixIndexing.prefix_indexing()
) (key = length, value = list: left positions of prefixes of length ‘length’)Return type: dict (int -> list of int)
-
PrefixIndexing.
failure_function
(pattern, equiv=<function <lambda>>)¶ Failure function from the Morris & Pratt algorithm.
Parameters: - pattern (list or str) – pattern on which the failure function is built
- equiv (function) – compararison function given as a lambda function, default: ==
Returns: failure function (key = index in the pattern (from 0), value = failure_function[index])
Return type: dict (int -> int)
Seealso: Tutorial in
_Tutorials_/Generator_tutorial.py
!: equiv has to be consistent with the type of the elements in labels.
Example: >>> failure_function([1,2,3,2,1,2,3,1,2,3], equiv = (lambda x,y : x%2 == y%2))
-
PrefixIndexing.
failure_function_and_right_pos_prefixes
(pattern, equiv=<function <lambda>>)¶ Failure function built on the pattern, and right positions of the prefixes of the pattern in itself.
Parameters: - pattern (list or str) – pattern on which the failure function is built
- equiv (function) – compararison function given as a lambda function, default: ==
Returns: Failure function built on the pattern (key = index in the pattern (from 0), value = failure_function[index]), and right positions of the prefixes of the pattern in itself (key = index in the pattern (from 0), value = list: lengths of the prefixes of the pattern in itself ending at the corresponding index).
Return type: Seealso: Tutorial in
_Tutorials_/Generator_tutorial.py
!: equiv has to be consistent with the type of the elements in labels.
Example: >>> pattern = [1,2,3,2,1,2,3,1,2,3] >>> failure_dict, lengths_ending_prefixes_dict = failure_function_and_right_pos_prefixes(pattern) >>> for idx,length in lengths_ending_prefixes_dict.items(): >>> print("Index {}: right position of prefix(es) of {} in itself of length(s): {}".format(idx, pattern, length))
-
PrefixIndexing.
filtered_prefix_indexing
(sequence, pattern, **args)¶ Filtered index of the prefixes of a pattern in a sequence (filtered regarding lengths and positions).
Parameters: - sequence (list or str) –
- pattern (list or str) –
- authorized_indexes (list (int)) – [args] list of authorized indexes to filter the results
- length_interval (tuple (int, int): absolute lengths** of the prefixes or tuple (float, float): fractions of the length of the longest prefix before filtering) – [args] interval of length to filter the results.
- equiv (function) – [args] compararison function given as a lambda function, default: ==
- print_info (int) – [args] print the details of the research?
Returns: prefixes of the pattern in the sequence after filtering (key = length, value = list of left positions of prefixes of the pattern of length ‘length’ in the sequence) and length of the longest prefix
Return type: Seealso: Tutorial in PrefixIndexing_tutorial.py
!: equiv has to be consistent with the type of the elements in labels.
Example: >>> pattern = [1,2,3,1,2,4] >>> sequence = [1,2,3,1,2,1,1,2,3] >>> authorized_indexes = [1,2,3,7,8] >>> length_interval = 1,3 >>> >>> prefixes, length_longest_prefix = filtered_prefix_indexing(sequence, pattern, authorized_indexes = authorized_indexes, length_interval = length_interval, equiv = (lambda x,y : x%2 == y%2), print_info = 0) >>> print("\nPrefixes of \n{} \nin \n{}\nusing the user-defined comparison function " == %2"\nAuthorized indexes = {} - Authorized length interval (absolute length) = {}:\n----".format(pattern, sequence, authorized_indexes, length_interval)) >>> for length,list_of_left_pos_in_sequence in prefixes.items(): >>> print("Length {}: at left position(s) {}.".format(length,list_of_left_pos_in_sequence))
>>> pattern = [1,2,3,1,2,4] >>> sequence = [1,2,3,1,2,1,1,2,3] >>> length_interval = 1.0/2, 4.0/5 >>> >>> prefixes, length_longest_prefix = filtered_prefix_indexing(sequence, pattern, length_interval = length_interval, equiv = (lambda x,y : x%2 == y%2), print_info = 0) >>> print("\nPrefixes of \n{} \nin \n{}\nusing the user-defined comparison function " == %2"\nAuthorized length interval (fraction of maximum length before filtering) = {}:\n----".format(pattern, sequence, length_interval)) >>> for length,list_of_left_pos_in_sequence in prefixes.items(): >>> print("Length {}: at left position(s) {}.".format(length,list_of_left_pos_in_sequence))
-
PrefixIndexing.
prefix_indexing
(sequence, pattern, **args)¶ Index the prefixes of a pattern in a sequence.
Parameters: Returns: prefixes of the pattern in the sequence (key = length, value = list of left positions of prefixes of the pattern of length ‘length’ in the sequence) and length of the longest prefix
Return type: Seealso: Tutorial in PrefixIndexing_tutorial.py
!: equiv has to be consistent with the type of the elements in labels.
Example: >>> pattern = "ABCD" >>> sequence = "ABCDABAABC" >>> >>> prefixes, length_longest_prefix = prefix_indexing(sequence, pattern) >>> print("\nPrefixes of \n{} \nin \n{}:".format(pattern, sequence)) >>> for length,list_of_left_pos_in_sequence in prefixes.items(): >>> print("Prefixes of length {}: at left position(s) {}.".format(length,list_of_left_pos_in_sequence))
>>> pattern = [["one", "1"],["three", "THREE"],["two", "two"],["two", "TWO"],["four", "FOUR"], ["three", "THREE"], \ >>> ["three", "3"],["one", "ONE"]] >>> sequence = [["two", "two"],["one", "1"],["three", "3"],["two", "two"],["two", "TWO"],["one", "1"], ["three", "three"], \ >>> ["two", "TWO"],["two", "TWO"],["four", "FOUR"],["four", "FOUR"],["one", "1"],["three", "THREE"], ["two", "two"],["three", "THREE"], \ >>> ["three", "3"],["one", "ONE"]] >>> >>> prefixes, length_longest_prefix = prefix_indexing(sequence, pattern, equiv = (lambda x,y: x[0] == y[0])) >>> print("\nPrefixes of \n{} \nin \n{}\nusing the user-defined comparison function '1st elem. == 1st elem.':".format(pattern, sequence)) >>> for length,list_of_left_pos_in_sequence in prefixes.items(): >>> print("Length {}: at left position(s) {}.".format(length,list_of_left_pos_in_sequence))
Using Intervals¶
Some subclasses of Label
enable to define a notion of interval. The tools defined in this module handle such possibilities.
Tutorial in _Tutorials_/Label_and_intervals_tutorial.py
.
/!…documentation in progress… /!
Event, Label, Transformation¶
Label¶
Definition of alphabets of labels to build sequences and use them in creative applications.
/!…documentation in progress… /!
Transforms¶
Class defining transformations on labels and contents.
/!…documentation in progress… /!
OSC communication¶
OSC AGENT¶
Class defining an OSC server embedding an instance of class GenerationHandler
.
See the different tutorials accompanied with Max patches.
/!…documentation in progress… /!
Memory and corpus¶
Generator Builder¶
Tools and functions to create instances of classes Generator
and GenerationHandler
from a json file / dict defining a sequence of events with metadata..
Example of the required json / dict format: _Tutorial_/ExamplesCorpus/ExampleDictMemory.json
.
-
GeneratorBuilder.
extract_labels_and_contents_from_dict_memory
(dict_memory, keys_labels, keys_contents)¶ Extracts a sequence of labels and sequence of contents from a dict / json file defining a sequence of events with metadata..
Parameters: Returns: sequence of labels, sequence of contents
Return type: list, list
See also: Example of the required json / dict format:
_Tutorial_/ExamplesCorpus/ExampleDictMemory.json
.- The dict / json file always contain fields that can be used with the following keys (for the labels as well as the contents):
- “absolute_time”: start date of the event, absolute time
- “relative_time”: start date of the event, relative time
- “tempo”: local tempo
- “index”: index in the sequence
-
GeneratorBuilder.
new_generation_handler_from_dict_memory
(dict_memory, keys_labels, keys_contents, model_navigator='FactorOracleNavigator', equiv=<function <lambda>>, equiv_mod_interval=<function <lambda>>, authorized_tranformations=[0], sequence_to_interval_fun=<function chord_labels_sequence_to_interval>, continuity_with_future=[0.0, 1.0])¶ Creates an instance of class
GenerationHandler
from a dict defining a sequence of events with metadata..Parameters: Other parameters: see
GenerationHandler
.Returns: Generation handler Return type: GenerationHandler
See also: Example of the required json / dict format: _Tutorial_/ExamplesCorpus/ExampleDictMemory.json
.See also: Tutorial in _Tutorials_/GeneratorBuilder_tutorial.py
.- The dict / json file always contain fields that can be used with the following keys (for the labels as well as the contents):
- “absolute_time”: start date of the event, absolute time
- “relative_time”: start date of the event, relative time
- “tempo”: local tempo
- “index”: index in the sequence
-
GeneratorBuilder.
new_generation_handler_from_json_file
(path_json_file, keys_labels, keys_contents, model_navigator='FactorOracleNavigator', equiv=<function <lambda>>, equiv_mod_interval=<function <lambda>>, authorized_tranformations=[0], sequence_to_interval_fun=<function chord_labels_sequence_to_interval>, continuity_with_future=[0.0, 1.0])¶ Creates an instance of class
GenerationHandler
from a json file defining a sequence of events with metadata..Parameters: Other parameters: see
GenerationHandler
.Returns: Generation handler Return type: GenerationHandler
See also: Example of the required json / dict format: _Tutorial_/ExamplesCorpus/ExampleDictMemory.json
.See also: Tutorial in _Tutorials_/GeneratorBuilder_tutorial.py
.- The dict / json file always contain fields that can be used with the following keys (for the labels as well as the contents):
- “absolute_time”: start date of the event, absolute time
- “relative_time”: start date of the event, relative time
- “tempo”: local tempo
- “index”: index in the sequence
-
GeneratorBuilder.
new_generator_from_dict_memory
(dict_memory, keys_labels, keys_contents, model_navigator='FactorOracleNavigator', equiv=<function <lambda>>, equiv_mod_interval=<function <lambda>>, authorized_tranformations=[0], sequence_to_interval_fun=<function chord_labels_sequence_to_interval>, continuity_with_future=[0.0, 1.0])¶ Creates an instance of class
Generator
from a dict defining a sequence of events with metadata..Parameters: Other parameters: see
Generator
.Returns: Generator Return type: Generator
See also: Example of the required json / dict format: _Tutorial_/ExamplesCorpus/ExampleDictMemory.json
.See also: Tutorial in _Tutorials_/GeneratorBuilder_tutorial.py
.- The dict / json file always contain fields that can be used with the following keys (for the labels as well as the contents):
- “absolute_time”: start date of the event, absolute time
- “relative_time”: start date of the event, relative time
- “tempo”: local tempo
- “index”: index in the sequence
-
GeneratorBuilder.
new_generator_from_json_file
(path_json_file, keys_labels, keys_contents, model_navigator='FactorOracleNavigator', equiv=<function <lambda>>, equiv_mod_interval=<function <lambda>>, authorized_tranformations=[0], sequence_to_interval_fun=<function chord_labels_sequence_to_interval>, continuity_with_future=[0.0, 1.0])¶ Creates an instance of class
Generator
from a json file defining a sequence of events with metadata..Parameters: Other parameters: see
Generator
.Returns: Generator Return type: Generator
See also: Example of the required json / dict format: _Tutorial_/ExamplesCorpus/ExampleDictMemory.json
.See also: Tutorial in _Tutorials_/GeneratorBuilder_tutorial.py
.- The dict / json file always contain fields that can be used with the following keys (for the labels as well as the contents):
- “absolute_time”: start date of the event, absolute time
- “relative_time”: start date of the event, relative time
- “tempo”: local tempo
- “index”: index in the sequence
/!…documentation in progress… /!