From 1820340357ad8ed0caa92fd14fb0ff16cd84d594 Mon Sep 17 00:00:00 2001 From: yannics <43981447+yannics@users.noreply.github.com> Date: Tue, 2 Aug 2022 09:27:03 +0200 Subject: [PATCH 1/2] Dynamic Markov Chain --- HelpSource/Classes/MarkovSet.schelp | 18 +++- classes/MarkovSet/MarkovSet.sc | 122 ++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 9 deletions(-) diff --git a/HelpSource/Classes/MarkovSet.schelp b/HelpSource/Classes/MarkovSet.schelp index 247d1f9..712d166 100644 --- a/HelpSource/Classes/MarkovSet.schelp +++ b/HelpSource/Classes/MarkovSet.schelp @@ -4,8 +4,7 @@ categories:: Collections>Unordered, Libraries>MathLib>Markov & Fuzzy related:: Classes/MarkovSetN, Classes/ShannonMarkovSet, Classes/ShannonFinger DESCRIPTION:: -MarkovSet is a Dictionary that contains keys pointing to WeighBags that contain objects and their probabilities. By parsing in a stream the Set "learns" what element can possibly follow another.footnote::See: http://www.taygeta.com/rwalks/rwalks.html:: - +MarkovSet is a Dictionary that contains keys pointing to WeighBags that contain objects and their probabilities. By parsing in a stream the Set "learns" what element can possibly follow another (see random walk). Part of link::Guides/MathLib::, a diverse library of mathematical functions. @@ -30,7 +29,10 @@ If args is code::nil::, the set is created and may be trained by the other metho ARGUMENT:: updateSeeds If set to code::true::, each element is always added to the seeds - +ARGUMENT:: remanence +Allows a dynamic Markov chain in the field of remanence as maximun order. +NOTE:: The remanence is effective only in a dynamic Markov chain context, which requires strong::element:: as an Array. In that case only the method code::nextProb::, code::nextIncludes:: and code::parseSeq:: are operative. Otherwise remanence is simply ignored. +:: METHOD:: fill @@ -194,6 +196,16 @@ x = Pfsm2(m, inf).asStream; ) :: +subsection:: Dynamic Markov Chain + +code:: +d = Array.with(seq1.asArray, seq2.asArray...seqN.asArray); +m = MarkovSet.new(remanence: 3, updateSeeds: true); +m.parseSeq(d, { "Parsing data completed!".postln }); +m.nextProb; +:: section:: Authors Julian Rohrhuber, 2004, 2007. + +Yann Ics, 2022 (Dynamic Markov Chain). diff --git a/classes/MarkovSet/MarkovSet.sc b/classes/MarkovSet/MarkovSet.sc index b2e09dd..f9397db 100644 --- a/classes/MarkovSet/MarkovSet.sc +++ b/classes/MarkovSet/MarkovSet.sc @@ -9,16 +9,17 @@ ________________________________________________________________________________ By parsing in a stream the Set 'learns' what element can possibly follow another. for reference see: http://www.taygeta.com/rwalks/rwalks.html ______________________________________________________________________________________ version 3.5, Julian Rohrhuber, 12/2004, 3/2007 +version 3.5.1, contribution Yann Ics, Summer 2022 */ MarkovSet { - var updateSeeds, updateSeeds, 1) } { ^this.nextProb(pk[1..], prob) } + { bag.weights.size == 1 } { ^this.nextProb(pk[1..], prob) } + { true } + { + if (prob.asBoolean) + { ^[bag.items, bag.counts] } + { ^bag.wchoose } + } + } + + nextIncludes { arg prevKey, includes; + var ins, tmp; + var prob = this.nextProb(prevKey, true); + if (prob.isNil.not) + { + // [TODO] when includes.size > 1 + // (it.asArray.asSet & includes.asArray.asSet) + // should respect their respective order as indices + // and be optimised ... + // e.g. [0, 7, 0, 5] should match only with [0, 7], [0, 0], [0, 5], [7, 0], [7, 5], [0, 7, 0], [0, 7, 5], [0, 0, 5], [7, 0, 5] or [0, 7, 0, 5] as includes + ins = prob[0].selectIndices{|it| (it.asArray.asSet & includes.asArray.asSet) == includes.asArray.asSet}; + if (ins.isEmpty && this.updateSeeds) + { + ins = prob[0].selectIndices{|it| (it.asArray.asSet & includes.asArray.asSet).isEmpty.not}; + if (ins.size > 0) { "partial match".postln } + } + }; + if (ins.size == 0) + { + if (this.updateSeeds) + { + tmp = this.dmc.select{|it| (it[0].asArray.asSet & includes.asArray.asSet) == includes.asArray.asSet}; + if (tmp.isEmpty) + { + tmp = this.dmc.select{|it| (it[0].asArray.asSet & includes.asArray.asSet).isEmpty.not}; + if (tmp.isEmpty) + { ^this.nextProb } + { ^tmp.flopChoose } + } + { ^tmp.flopChoose } + } + { ^nil } + } + { + ^ins.collect{|ind| [prob[0][ind], prob[1][ind]]}.flopChoose; + } + } + + parseSeq { arg data, completionFunc; + // data is a list of sequence(s) + // e.g. Array.with(seq1.asArray, seq2.asArray...seqN.asArray) + data.do{|seq| + seq.windloop(this.remanence).do{|len| + len.do{|it| this.read(it[..it.size-2], it.last)}}}; + this.makeSeeds; + completionFunc.value; + } +} + +// get weighted array for method nextProb in MarkovSet ++ ArrayedCollection { + windloop { arg order; + var ord; + // order is an integer strictly superior to zero + if (order > this.size) { order = this.size }; + ^(2..order+1).collect + { + |k| + (this.size-k+1).collect + { + |i| + this[i..i+k-1] + } + } + } + + flopChoose { + // this = [[item1, countItem1], [item2, countItem2] ... [itemN, countItemN]] + var tmp = this.flop; + ^tmp[0].wchoose(tmp[1].normalizeSum) + } +} +//-------------------------------------------------- +// ref: https://yannics.github.io/ +// - Journal of Generative Sonic Art +// - Neuromuse3 +//-------------------------------------------------- + + From f52954ecdb2674ea2cd680c89580c2efabd7a8c0 Mon Sep 17 00:00:00 2001 From: yannics <43981447+yannics@users.noreply.github.com> Date: Sun, 14 Aug 2022 23:21:14 +0200 Subject: [PATCH 2/2] Update MarkovSet.sc --- classes/MarkovSet/MarkovSet.sc | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/classes/MarkovSet/MarkovSet.sc b/classes/MarkovSet/MarkovSet.sc index f9397db..bd626be 100644 --- a/classes/MarkovSet/MarkovSet.sc +++ b/classes/MarkovSet/MarkovSet.sc @@ -460,9 +460,20 @@ MarkovSetN : LookupMarkovSet { pk = if(prevKey.isNil.not) { prevKey.reverse.copyFromStart(this.remanence-1).reverse }; bag = this.dict.at(pk); case - { bag.isNil && (pk.size < 2) } { if (this.updateSeeds && prob.asBoolean.not) { ^this.next.choose } { ^nil} } - { bag.isNil && (pk.size > 1) } { ^this.nextProb(pk[1..], prob) } - { bag.weights.size == 1 } { ^this.nextProb(pk[1..], prob) } + { bag.isNil && (pk.size < 2) } + { + if (this.updateSeeds && prob.asBoolean.not) + { ^this.next.choose } + { ^nil} + } + { bag.isNil && (pk.size > 1) } + { + ^this.nextProb(pk[1..], prob) + } + { bag.weights.size == 1 } + { + ^this.nextProb(pk[1..], prob) + } { true } { if (prob.asBoolean)