FactorOracle.hpp
1 #pragma once
2 #include <Engine/Node/SimpleApi.hpp>
3 
4 #include <ossia/detail/hash_map.hpp>
5 #include <ossia/detail/math.hpp>
6 #include <ossia/detail/ssize.hpp>
7 
8 #include <QDebug>
9 
10 #include <rnd/random.hpp>
11 #if !defined(NDEBUG) && !defined(_MSC_VER) && !defined(__clang__)
12 #include <debug/vector>
13 #define debug_vector_t __gnu_debug::vector
14 #else
15 #define debug_vector_t std::vector
16 #endif
17 namespace Nodes::FactorOracle
18 {
19 
20 template <typename T, T default_value>
22 {
23 public:
24  debug_vector_t<T> impl;
25 
26  T& operator[](int i_) { return (*this)[static_cast<std::size_t>(i_)]; }
27  T& operator[](std::size_t i_)
28  {
29  auto i = static_cast<std::size_t>(i_);
30  if(i < impl.size() && impl.size() != 0)
31  {
32  return impl[i];
33  }
34  impl.resize((i + 1) * 2, default_value);
35  return impl[i];
36  }
37 
38  const T& operator[](int i_) const { return (*this)[static_cast<std::size_t>(i_)]; }
39  const T& operator[](std::size_t i_) const
40  {
41  auto i = static_cast<std::size_t>(i_);
42  if(i < impl.size() && impl.size() != 0)
43  {
44  return impl[i];
45  }
46  static constexpr auto dval = default_value;
47  return dval;
48  }
49 };
50 template <typename T>
52 {
53 public:
54  debug_vector_t<T> impl;
55 
56  T& operator[](int i_) { return (*this)[static_cast<std::size_t>(i_)]; }
57  T& operator[](std::size_t i_)
58  {
59  auto i = static_cast<std::size_t>(i_);
60  if(i < impl.size() && impl.size() != 0)
61  {
62  return impl[i];
63  }
64  impl.resize((i + 1) * 2);
65  return impl[i];
66  }
67 };
68 
70 {
71 public:
72  int cur_alphabet_size = 0;
73  debug_vector_t<std::pair<int, ossia::value>> value_map;
74  FactorOracle(int sz = 0)
75  : m_forwardLink(sz + 1, debug_vector_t<int>{-1})
76  , m_rand_engine{}
77  {
78  m_sp.impl.resize(1000);
79  m_lrs.impl.resize(1000);
80  m_sp[0] = -1;
81  m_lrs[0] = 0;
82  }
83 
85  int LCS(int p1, int p2)
86  {
87  if(p2 == m_sp[p1])
88  return m_lrs[p1];
89 
90  while(m_sp[p2] != m_sp[p1])
91  p2 = m_sp[p2];
92 
93  return std::min(m_lrs[p1], m_lrs[p2]);
94  }
95 
96  void add_char(ossia::value c)
97  {
98  if(n < std::ssize(m_forwardLink) - 1)
99  {
100  m_sequence.push_back(std::move(c));
101  auto it = ossia::find_if(
102  value_map, [&](const auto& pair) { return pair.second == c; });
103  if(it != value_map.end())
104  {
105  add_state(it->first);
106  }
107  else
108  {
109  value_map.push_back({cur_alphabet_size, m_sequence.back()});
110  add_state(cur_alphabet_size);
111  cur_alphabet_size++;
112  }
113  }
114  }
115 
116  void add_state(int c)
117  {
118  m_trans[n][c] = n + 1;
119  int p1 = n;
120  int j = m_sp[p1];
121  ++n;
122  while(j != -1 && m_trans[j][c] == -1)
123  {
124  int& m_trans_j_c = m_trans[j][c];
125  m_trans_j_c = n;
126  if(m_forwardLink[j][0] == -1)
127  {
128  m_forwardLink[j][0] = m_trans_j_c;
129  }
130  else
131  {
132  m_forwardLink[j].push_back(m_trans_j_c);
133  }
134  p1 = j;
135  j = m_sp[p1];
136  }
137  int& m_sp_n = m_sp[n];
138  m_sp_n = (j == -1 ? 0 : m_trans[j][c]);
139  m_lrs[n] = (m_sp_n == 0 ? 0 : LCS(p1, m_sp_n - 1) + 1);
140  }
141 
142  debug_vector_t<ossia::value> make_rand_sequence(float continuity, int seqSize) const
143  {
144  auto start = std::uniform_int_distribution<std::size_t>{0, m_sequence.size()}(
145  m_rand_engine);
146  return make_sequence(continuity, start, seqSize);
147  }
148 
149  debug_vector_t<ossia::value>
150  make_sequence(float continuity, std::size_t curState, std::size_t seqSize) const
151  {
152  if(curState > m_sequence.size())
153  {
154  qDebug() << "Le point initial de l'improvisation doit être comprise "
155  "dans la séquence";
156  return {};
157  }
158 
159  debug_vector_t<ossia::value> v;
160  v.reserve(seqSize);
161  for(std::size_t i = 0; i < seqSize; i++)
162  {
163  auto f = std::uniform_real_distribution<float>{}(m_rand_engine);
164  if(f <= continuity && curState < m_sequence.size() - 1)
165  {
166  curState++;
167  v.push_back(m_sequence[curState]);
168  }
169  else
170  {
171  do
172  {
173  int links = (curState == 0 ? 0 : 1);
174  if(m_forwardLink[curState][0] != -1)
175  {
176  links += m_forwardLink[curState].size();
177  }
178 
179  auto linkToFollow
180  = std::uniform_int_distribution<int>{0, links - 1}(m_rand_engine);
181  if(linkToFollow == links - 1)
182  {
183  if(curState != 0)
184  {
185  curState = m_sp[curState];
186  }
187  }
188  else
189  {
190  curState = m_forwardLink[curState][linkToFollow];
191  }
192  } while(curState >= m_sequence.size());
193 
194  v.push_back(m_sequence[curState]);
195  }
196  }
197  return v;
198  }
199 
200 private:
201  int n{};
202  safe_vector<int, 0> m_sp;
203  safe_vector<int, 0> m_lrs;
204  safe_vector_simple<safe_vector<int, -1>> m_trans;
205  debug_vector_t<ossia::value> m_sequence;
206  debug_vector_t<debug_vector_t<int>> m_forwardLink;
207 
208  mutable rnd::pcg m_rand_engine;
209 };
210 
211 struct Node
212 {
214  {
215  static const constexpr auto prettyName = "Factor Oracle";
216  static const constexpr auto objectKey = "Factor Oracle";
217  static const constexpr auto category = "Control/Impro";
218  static const constexpr auto author
219  = "Shlomo Dubnov, Ge Wang, Éric Meaux, Jean-Michaël Celerier";
220  static const constexpr auto kind = Process::ProcessCategory::Mapping;
221  static const constexpr auto description = "Factor Oracle algorithm ."; // TODO cite
222  static const constexpr auto tags = std::array<const char*, 0>{};
223  static const uuid_constexpr auto uuid
224  = make_uuid("d90284c0-4196-47e0-802d-7e07342029ec");
225 
226  static const constexpr auto controls
227  = tuplet::make_tuple(Control::IntSlider{"Sequence length", 1, 64, 8});
228 
229  static const constexpr value_in value_ins[]{"in", "regen", "bang"};
230  static const constexpr value_out value_outs[]{"out"};
231  };
232 
233  struct State
234  {
235  FactorOracle oracle{64};
236  debug_vector_t<ossia::value> sequence;
237  std::size_t sequence_idx{};
238  };
239 
240  using control_policy = ossia::safe_nodes::last_tick;
241  static void
242  run(const ossia::value_port& in, const ossia::value_port& regen,
243  const ossia::value_port& bangs, int seq_len, ossia::value_port& out,
244  ossia::token_request, ossia::exec_state_facade, State& self)
245  {
246  // Entrées sont dans p1
247  for(auto val : in.get_data())
248  {
249  self.oracle.add_char(val.value);
250  }
251 
252  if(!regen.get_data().empty())
253  {
254  self.sequence = self.oracle.make_rand_sequence(0.4, seq_len);
255  }
256 
257  if(!self.sequence.empty())
258  {
259  for(auto& bang : bangs.get_data())
260  {
261  self.sequence_idx = ossia::clamp<int64_t>(
262  (int64_t)self.sequence_idx, 0, (int64_t)self.sequence.size() - 1);
263  out.write_value(self.sequence[self.sequence_idx], bang.timestamp);
264  self.sequence_idx = (self.sequence_idx + 1) % self.sequence.size();
265  }
266  }
267  }
268 };
269 #undef debug_vector_t
270 }
Definition: FactorOracle.hpp:70
int LCS(int p1, int p2)
Function LCS (longest common suffix)
Definition: FactorOracle.hpp:85
Utilities for OSSIA data structures.
Definition: DeviceInterface.hpp:33
Definition: score-lib-process/Control/Widgets.hpp:178
Definition: SimpleApi.hpp:32
Definition: FactorOracle.hpp:214
Definition: FactorOracle.hpp:212
Definition: FactorOracle.hpp:52
Definition: FactorOracle.hpp:22