OTMulti.cpp 63 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461
  1. /* OTMulti.cpp
  2. *
  3. * Copyright (C) 2005-2018 Paul Boersma
  4. *
  5. * This code is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or (at
  8. * your option) any later version.
  9. *
  10. * This code is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  13. * See the GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this work. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /*
  19. * pb 2005/06/11 the very beginning of computational bidirectional multi-level OT
  20. * pb 2006/05/16 guarded against cells with many violations
  21. * pb 2006/05/17 draw disharmonies above tableau
  22. * pb 2007/05/19 decision strategies
  23. * pb 2007/08/12 wchar
  24. * pb 2007/10/01 leak and constraint plasticity
  25. * pb 2007/10/01 can write as encoding
  26. * pb 2007/11/14 drawTableau: corrected direction of arrows for positive satisfactions
  27. * pb 2008/04/14 OTMulti_getConstraintIndexFromName
  28. * pb 2009/03/18 modern enums
  29. * pb 2010/06/05 corrected colours
  30. * pb 2011/03/01 multiple update rules; more decision strategies
  31. * pb 2011/03/29 C++
  32. * pb 2011/04/27 Melder_debug 41 and 42
  33. * pb 2011/05/15 prevented zero sums of probabilities in MaxEnt
  34. */
  35. #include "OTMulti.h"
  36. #include "oo_DESTROY.h"
  37. #include "OTMulti_def.h"
  38. #include "oo_COPY.h"
  39. #include "OTMulti_def.h"
  40. #include "oo_EQUAL.h"
  41. #include "OTMulti_def.h"
  42. #include "oo_CAN_WRITE_AS_ENCODING.h"
  43. #include "OTMulti_def.h"
  44. #include "oo_WRITE_BINARY.h"
  45. #include "OTMulti_def.h"
  46. #include "oo_READ_BINARY.h"
  47. #include "OTMulti_def.h"
  48. #include "oo_DESCRIPTION.h"
  49. #include "OTMulti_def.h"
  50. void structOTMulti :: v_info ()
  51. {
  52. structDaata :: v_info ();
  53. integer numberOfViolations = 0;
  54. for (integer icand = 1; icand <= numberOfCandidates; icand ++) {
  55. for (integer icons = 1; icons <= numberOfConstraints; icons ++) {
  56. numberOfViolations += candidates [icand]. marks [icons];
  57. }
  58. }
  59. MelderInfo_writeLine (U"Decision strategy: ", kOTGrammar_decisionStrategy_getText (decisionStrategy));
  60. MelderInfo_writeLine (U"Number of constraints: ", numberOfConstraints);
  61. MelderInfo_writeLine (U"Number of candidates: ", numberOfCandidates);
  62. MelderInfo_writeLine (U"Number of violation marks: ", numberOfViolations);
  63. }
  64. void structOTMulti :: v_writeText (MelderFile file) {
  65. MelderFile_write (file, U"\n<", kOTGrammar_decisionStrategy_getText (decisionStrategy),
  66. U">\n", leak, U" ! leak\n", numberOfConstraints, U" constraints");
  67. for (integer icons = 1; icons <= numberOfConstraints; icons ++) {
  68. OTConstraint constraint = & constraints [icons];
  69. MelderFile_write (file, U"\n\t\"");
  70. for (const char32 *p = & constraint -> name [0]; *p != U'\0'; p ++) {
  71. if (*p == U'\"') MelderFile_writeCharacter (file, U'\"'); // double any quotes within quotes
  72. MelderFile_writeCharacter (file, *p);
  73. }
  74. MelderFile_write (file, U"\" ", constraint -> ranking,
  75. U" ", constraint -> disharmony, U" ", constraint -> plasticity);
  76. }
  77. MelderFile_write (file, U"\n\n", numberOfCandidates, U" candidates");
  78. for (integer icand = 1; icand <= numberOfCandidates; icand ++) {
  79. OTCandidate candidate = & candidates [icand];
  80. MelderFile_write (file, U"\n\t\"");
  81. for (const char32 *p = & candidate -> string [0]; *p != U'\0'; p ++) {
  82. if (*p == U'\"') MelderFile_writeCharacter (file, U'\"'); // double any quotes within quotes
  83. MelderFile_writeCharacter (file, *p);
  84. }
  85. MelderFile_write (file, U"\" ");
  86. for (integer icons = 1; icons <= candidate -> numberOfConstraints; icons ++) {
  87. MelderFile_write (file, U" ", candidate -> marks [icons]);
  88. }
  89. }
  90. }
  91. void OTMulti_checkIndex (OTMulti me) {
  92. if (my index) return;
  93. my index = NUMvector <integer> (1, my numberOfConstraints);
  94. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) my index [icons] = icons;
  95. OTMulti_sort (me);
  96. }
  97. void structOTMulti :: v_readText (MelderReadText text, int formatVersion) {
  98. OTMulti_Parent :: v_readText (text, formatVersion);
  99. if (formatVersion >= 1) {
  100. try {
  101. decisionStrategy = (kOTGrammar_decisionStrategy) texgete8 (text, (enum_generic_getValue) kOTGrammar_decisionStrategy_getValue);
  102. } catch (MelderError) {
  103. Melder_throw (U"Decision strategy not read.");
  104. }
  105. }
  106. if (formatVersion >= 2) {
  107. try {
  108. leak = texgetr64 (text);
  109. } catch (MelderError) {
  110. Melder_throw (U"Trying to read leak.");
  111. }
  112. }
  113. if ((numberOfConstraints = texgeti32 (text)) < 1) Melder_throw (U"No constraints.");
  114. constraints = NUMvector <structOTConstraint> (1, numberOfConstraints);
  115. for (integer icons = 1; icons <= numberOfConstraints; icons ++) {
  116. OTConstraint constraint = & constraints [icons];
  117. constraint -> name = texgetw16 (text);
  118. constraint -> ranking = texgetr64 (text);
  119. constraint -> disharmony = texgetr64 (text);
  120. if (formatVersion < 2) {
  121. constraint -> plasticity = 1.0;
  122. } else {
  123. try {
  124. constraint -> plasticity = texgetr64 (text);
  125. } catch (MelderError) {
  126. Melder_throw (U"Plasticity of constraint ", icons, U" not read.");
  127. }
  128. }
  129. }
  130. if ((numberOfCandidates = texgeti32 (text)) < 1) Melder_throw (U"No candidates.");
  131. candidates = NUMvector <structOTCandidate> (1, numberOfCandidates);
  132. for (integer icand = 1; icand <= numberOfCandidates; icand ++) {
  133. OTCandidate candidate = & candidates [icand];
  134. candidate -> string = texgetw16 (text);
  135. candidate -> numberOfConstraints = numberOfConstraints; // redundancy, needed for writing binary
  136. candidate -> marks = INTVECraw (candidate -> numberOfConstraints);
  137. for (integer icons = 1; icons <= candidate -> numberOfConstraints; icons ++)
  138. candidate -> marks [icons] = texgeti16 (text);
  139. }
  140. OTMulti_checkIndex (this);
  141. }
  142. Thing_implement (OTMulti, Daata, 2);
  143. integer OTMulti_getConstraintIndexFromName (OTMulti me, conststring32 name) {
  144. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  145. if (Melder_equ (my constraints [icons]. name.get(), name))
  146. return icons;
  147. return 0;
  148. }
  149. static OTMulti constraintCompare_grammar;
  150. static int constraintCompare (const void *first, const void *second) {
  151. OTMulti me = constraintCompare_grammar;
  152. integer icons = * (integer *) first, jcons = * (integer *) second;
  153. OTConstraint ci = & my constraints [icons], cj = & my constraints [jcons];
  154. /*
  155. * Sort primarily by disharmony.
  156. */
  157. if (ci -> disharmony > cj -> disharmony) return -1;
  158. if (ci -> disharmony < cj -> disharmony) return +1;
  159. /*
  160. * Tied constraints are sorted alphabetically.
  161. */
  162. return str32cmp (my constraints [icons]. name.get(), my constraints [jcons]. name.get());
  163. }
  164. void OTMulti_sort (OTMulti me) {
  165. constraintCompare_grammar = me;
  166. qsort (& my index [1], my numberOfConstraints, sizeof (integer), constraintCompare);
  167. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  168. OTConstraint constraint = & my constraints [my index [icons]];
  169. constraint -> tiedToTheLeft = ( icons > 1 &&
  170. my constraints [my index [icons - 1]]. disharmony == constraint -> disharmony );
  171. constraint -> tiedToTheRight = ( icons < my numberOfConstraints &&
  172. my constraints [my index [icons + 1]]. disharmony == constraint -> disharmony );
  173. }
  174. }
  175. void OTMulti_newDisharmonies (OTMulti me, double evaluationNoise) {
  176. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  177. OTConstraint constraint = & my constraints [icons];
  178. constraint -> disharmony = constraint -> ranking + NUMrandomGauss (0, evaluationNoise);
  179. }
  180. OTMulti_sort (me);
  181. }
  182. int OTMulti_compareCandidates (OTMulti me, integer icand1, integer icand2) {
  183. INTVEC marks1 = my candidates [icand1]. marks.get();
  184. INTVEC marks2 = my candidates [icand2]. marks.get();
  185. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  186. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  187. integer numberOfMarks1 = marks1 [my index [icons]];
  188. integer numberOfMarks2 = marks2 [my index [icons]];
  189. /*
  190. * Count tied constraints as one.
  191. */
  192. while (my constraints [my index [icons]]. tiedToTheRight) {
  193. icons ++;
  194. numberOfMarks1 += marks1 [my index [icons]];
  195. numberOfMarks2 += marks2 [my index [icons]];
  196. }
  197. if (numberOfMarks1 < numberOfMarks2) return -1; /* Candidate 1 is better than candidate 2. */
  198. if (numberOfMarks1 > numberOfMarks2) return +1; /* Candidate 2 is better than candidate 1. */
  199. }
  200. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
  201. my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY)
  202. {
  203. double disharmony1 = 0.0, disharmony2 = 0.0;
  204. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  205. disharmony1 += my constraints [icons]. disharmony * marks1 [icons];
  206. disharmony2 += my constraints [icons]. disharmony * marks2 [icons];
  207. }
  208. if (disharmony1 < disharmony2) return -1; /* Candidate 1 is better than candidate 2. */
  209. if (disharmony1 > disharmony2) return +1; /* Candidate 2 is better than candidate 1. */
  210. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT) {
  211. double disharmony1 = 0.0, disharmony2 = 0.0;
  212. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  213. if (my constraints [icons]. disharmony > 0.0) {
  214. disharmony1 += my constraints [icons]. disharmony * marks1 [icons];
  215. disharmony2 += my constraints [icons]. disharmony * marks2 [icons];
  216. }
  217. }
  218. if (disharmony1 < disharmony2) return -1; /* Candidate 1 is better than candidate 2. */
  219. if (disharmony1 > disharmony2) return +1; /* Candidate 2 is better than candidate 1. */
  220. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
  221. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
  222. {
  223. double disharmony1 = 0.0, disharmony2 = 0.0;
  224. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  225. disharmony1 += exp (my constraints [icons]. disharmony) * marks1 [icons];
  226. disharmony2 += exp (my constraints [icons]. disharmony) * marks2 [icons];
  227. }
  228. if (disharmony1 < disharmony2) return -1; /* Candidate 1 is better than candidate 2. */
  229. if (disharmony1 > disharmony2) return +1; /* Candidate 2 is better than candidate 1. */
  230. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG) {
  231. double disharmony1 = 0.0, disharmony2 = 0.0;
  232. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  233. double constraintDisharmony = my constraints [icons]. disharmony > 1.0 ? my constraints [icons]. disharmony : 1.0;
  234. disharmony1 += constraintDisharmony * marks1 [icons];
  235. disharmony2 += constraintDisharmony * marks2 [icons];
  236. }
  237. if (disharmony1 < disharmony2) return -1; /* Candidate 1 is better than candidate 2. */
  238. if (disharmony1 > disharmony2) return +1; /* Candidate 2 is better than candidate 1. */
  239. } else {
  240. Melder_fatal (U"Unimplemented decision strategy.");
  241. }
  242. return 0; /* None of the comparisons found a difference between the two candidates. Hence, they are equally good. */
  243. }
  244. int OTMulti_candidateMatches (OTMulti me, integer icand, conststring32 form1, conststring32 form2) {
  245. conststring32 string = my candidates [icand]. string.get();
  246. return (form1 [0] == U'\0' || str32str (string, form1)) && (form2 [0] == U'\0' || str32str (string, form2));
  247. }
  248. static void _OTMulti_fillInHarmonies (OTMulti me, conststring32 form1, conststring32 form2) {
  249. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) return;
  250. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) if (OTMulti_candidateMatches (me, icand, form1, form2)) {
  251. OTCandidate candidate = & my candidates [icand];
  252. INTVEC marks = candidate -> marks.get();
  253. double disharmony = 0.0;
  254. if (my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
  255. my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY)
  256. {
  257. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  258. disharmony += my constraints [icons]. disharmony * marks [icons];
  259. }
  260. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
  261. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
  262. {
  263. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  264. disharmony += exp (my constraints [icons]. disharmony) * marks [icons];
  265. }
  266. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT) {
  267. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  268. if (my constraints [icons]. disharmony > 0.0) {
  269. disharmony += my constraints [icons]. disharmony * marks [icons];
  270. }
  271. }
  272. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG) {
  273. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  274. double constraintDisharmony = my constraints [icons]. disharmony > 1.0 ? my constraints [icons]. disharmony : 1.0;
  275. disharmony += constraintDisharmony * marks [icons];
  276. }
  277. } else {
  278. Melder_fatal (U"_OTMulti_fillInHarmonies: unimplemented decision strategy.");
  279. }
  280. candidate -> harmony = - disharmony;
  281. }
  282. }
  283. static void _OTMulti_fillInProbabilities (OTMulti me, conststring32 form1, conststring32 form2) {
  284. double maximumHarmony = -1e308;
  285. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) if (OTMulti_candidateMatches (me, icand, form1, form2)) {
  286. OTCandidate candidate = & my candidates [icand];
  287. if (candidate -> harmony > maximumHarmony) {
  288. maximumHarmony = candidate -> harmony;
  289. }
  290. }
  291. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) if (OTMulti_candidateMatches (me, icand, form1, form2)) {
  292. OTCandidate candidate = & my candidates [icand];
  293. candidate -> probability = exp (candidate -> harmony - maximumHarmony);
  294. Melder_assert (candidate -> probability >= 0.0 && candidate -> probability <= 1.0);
  295. }
  296. double sumOfProbabilities = 0.0;
  297. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) if (OTMulti_candidateMatches (me, icand, form1, form2)) {
  298. OTCandidate candidate = & my candidates [icand];
  299. sumOfProbabilities += candidate -> probability;
  300. }
  301. Melder_assert (sumOfProbabilities > 0.0); // Because at least one of them is 1.0.
  302. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) if (OTMulti_candidateMatches (me, icand, form1, form2)) {
  303. OTCandidate candidate = & my candidates [icand];
  304. candidate -> probability /= sumOfProbabilities;
  305. }
  306. }
  307. class MelderError_OTMulti_NoMatchingCandidate: public MelderError {};
  308. integer OTMulti_getWinner (OTMulti me, conststring32 form1, conststring32 form2) {
  309. try {
  310. integer icand_best = 0;
  311. if (my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
  312. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
  313. {
  314. _OTMulti_fillInHarmonies (me, form1, form2);
  315. _OTMulti_fillInProbabilities (me, form1, form2);
  316. double cutOff = NUMrandomUniform (0.0, 1.0);
  317. double sumOfProbabilities = 0.0;
  318. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) if (OTMulti_candidateMatches (me, icand, form1, form2)) {
  319. sumOfProbabilities += my candidates [icand]. probability;
  320. if (sumOfProbabilities > cutOff) {
  321. icand_best = icand;
  322. break;
  323. }
  324. }
  325. } else {
  326. integer numberOfBestCandidates = 0;
  327. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) if (OTMulti_candidateMatches (me, icand, form1, form2)) {
  328. if (icand_best == 0) {
  329. icand_best = icand;
  330. numberOfBestCandidates = 1;
  331. } else {
  332. int comparison = OTMulti_compareCandidates (me, icand, icand_best);
  333. if (comparison == -1) {
  334. icand_best = icand; // the current candidate is the unique best candidate found so far
  335. numberOfBestCandidates = 1;
  336. } else if (comparison == 0) {
  337. numberOfBestCandidates += 1; // the current candidate is equally good as the best found before
  338. /*
  339. * Give all candidates that are equally good an equal chance to become the winner.
  340. */
  341. if (Melder_debug == 41) {
  342. icand_best = icand_best; // keep first
  343. } else if (Melder_debug == 42) {
  344. icand_best = icand; // take last
  345. } else if (NUMrandomUniform (0.0, numberOfBestCandidates) < 1.0) { // default: take random
  346. icand_best = icand;
  347. }
  348. }
  349. }
  350. }
  351. }
  352. if (icand_best == 0) {
  353. Melder_appendError (U"The forms ", form1, U" and ", form2, U" do not match any candidate.");
  354. throw MelderError_OTMulti_NoMatchingCandidate (); // BUG: NYI
  355. }
  356. return icand_best;
  357. } catch (MelderError) {
  358. Melder_throw (me, U": winner not determined.");
  359. }
  360. }
  361. static void OTMulti_modifyRankings (OTMulti me, integer iwinner, integer iloser,
  362. kOTGrammar_rerankingStrategy updateRule,
  363. double plasticity, double relativePlasticityNoise)
  364. {
  365. bool *grammarHasChanged = nullptr; // to be implemented
  366. bool warnIfStalled = false; // to be implemented
  367. if (iwinner == iloser) return;
  368. OTCandidate winner = & my candidates [iwinner], loser = & my candidates [iloser];
  369. double step = relativePlasticityNoise == 0.0 ? plasticity : NUMrandomGauss (plasticity, relativePlasticityNoise * plasticity);
  370. bool multiplyStepByNumberOfViolations =
  371. my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
  372. my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT ||
  373. my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
  374. my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG ||
  375. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
  376. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY;
  377. if (Melder_debug != 0) {
  378. /*
  379. * Perhaps override the standard update rule.
  380. */
  381. if (Melder_debug == 26) multiplyStepByNumberOfViolations = false; // OT-GLA
  382. else if (Melder_debug == 27) multiplyStepByNumberOfViolations = true; // HG-GLA
  383. }
  384. if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ONE) {
  385. integer icons = NUMrandomInteger (1, my numberOfConstraints);
  386. OTConstraint constraint = & my constraints [icons];
  387. double constraintStep = step * constraint -> plasticity;
  388. integer winnerMarks = winner -> marks [icons];
  389. integer loserMarks = loser -> marks [icons];
  390. if (loserMarks > winnerMarks) {
  391. if (multiplyStepByNumberOfViolations) constraintStep *= loserMarks - winnerMarks;
  392. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak);
  393. if (grammarHasChanged) *grammarHasChanged = true;
  394. }
  395. if (winnerMarks > loserMarks) {
  396. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  397. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak);
  398. if (grammarHasChanged) *grammarHasChanged = true;
  399. }
  400. } else if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ALL) {
  401. bool changed = false;
  402. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  403. OTConstraint constraint = & my constraints [icons];
  404. double constraintStep = step * constraint -> plasticity;
  405. integer winnerMarks = winner -> marks [icons];
  406. integer loserMarks = loser -> marks [icons];
  407. if (loserMarks > winnerMarks) {
  408. if (multiplyStepByNumberOfViolations) constraintStep *= loserMarks - winnerMarks;
  409. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak);
  410. changed = true;
  411. }
  412. if (winnerMarks > loserMarks) {
  413. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  414. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak);
  415. changed = true;
  416. }
  417. }
  418. if (changed && my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG) {
  419. double sumOfWeights = 0.0;
  420. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  421. sumOfWeights += my constraints [icons]. ranking;
  422. }
  423. double averageWeight = sumOfWeights / my numberOfConstraints;
  424. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  425. my constraints [icons]. ranking -= averageWeight;
  426. }
  427. }
  428. if (grammarHasChanged) *grammarHasChanged = changed;
  429. } else if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ALL_SKIPPABLE) {
  430. bool changed = false;
  431. integer winningConstraints = 0, losingConstraints = 0;
  432. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  433. integer winnerMarks = winner -> marks [icons];
  434. integer loserMarks = loser -> marks [icons];
  435. if (loserMarks > winnerMarks) losingConstraints ++;
  436. if (winnerMarks > loserMarks) winningConstraints ++;
  437. }
  438. if (winningConstraints != 0) for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  439. OTConstraint constraint = & my constraints [icons];
  440. double constraintStep = step * constraint -> plasticity;
  441. integer winnerMarks = winner -> marks [icons];
  442. integer loserMarks = loser -> marks [icons];
  443. if (loserMarks > winnerMarks) {
  444. if (multiplyStepByNumberOfViolations) constraintStep *= loserMarks - winnerMarks;
  445. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak);
  446. changed = true;
  447. }
  448. if (winnerMarks > loserMarks) {
  449. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  450. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak);
  451. changed = true;
  452. }
  453. }
  454. if (changed && my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG) {
  455. double sumOfWeights = 0.0;
  456. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  457. sumOfWeights += my constraints [icons]. ranking;
  458. }
  459. double averageWeight = sumOfWeights / my numberOfConstraints;
  460. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  461. my constraints [icons]. ranking -= averageWeight;
  462. }
  463. }
  464. if (grammarHasChanged) *grammarHasChanged = changed;
  465. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_UNCANCELLED) {
  466. integer winningConstraints = 0, losingConstraints = 0;
  467. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  468. integer winnerMarks = winner -> marks [icons];
  469. integer loserMarks = loser -> marks [icons];
  470. if (loserMarks > winnerMarks) losingConstraints ++;
  471. if (winnerMarks > loserMarks) winningConstraints ++;
  472. }
  473. if (winningConstraints != 0) {
  474. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  475. OTConstraint constraint = & my constraints [icons];
  476. double constraintStep = step * constraint -> plasticity;
  477. integer winnerMarks = winner -> marks [icons];
  478. integer loserMarks = loser -> marks [icons];
  479. if (loserMarks > winnerMarks) {
  480. if (multiplyStepByNumberOfViolations) constraintStep *= loserMarks - winnerMarks;
  481. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak) / losingConstraints;
  482. //constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak) * winningConstraints;
  483. if (grammarHasChanged) *grammarHasChanged = true;
  484. }
  485. if (winnerMarks > loserMarks) {
  486. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  487. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) / winningConstraints;
  488. //constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) * losingConstraints;
  489. if (grammarHasChanged) *grammarHasChanged = true;
  490. }
  491. }
  492. }
  493. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL) {
  494. integer winningConstraints = 0, losingConstraints = 0;
  495. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  496. integer winnerMarks = winner -> marks [icons];
  497. integer loserMarks = loser -> marks [icons];
  498. if (loserMarks > 0) losingConstraints ++;
  499. if (winnerMarks > 0) winningConstraints ++;
  500. }
  501. if (winningConstraints != 0) for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  502. OTConstraint constraint = & my constraints [icons];
  503. double constraintStep = step * constraint -> plasticity;
  504. integer winnerMarks = winner -> marks [icons];
  505. integer loserMarks = loser -> marks [icons];
  506. if (loserMarks > 0) {
  507. if (multiplyStepByNumberOfViolations) constraintStep *= loserMarks - winnerMarks;
  508. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak) / losingConstraints;
  509. if (grammarHasChanged) *grammarHasChanged = true;
  510. }
  511. if (winnerMarks > 0) {
  512. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  513. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) / winningConstraints;
  514. if (grammarHasChanged) *grammarHasChanged = true;
  515. }
  516. }
  517. } else if (updateRule == kOTGrammar_rerankingStrategy::EDCD || updateRule == kOTGrammar_rerankingStrategy::EDCD_WITH_VACATION) {
  518. /*
  519. * Determine the crucial winner mark.
  520. */
  521. double pivotRanking;
  522. bool equivalent = true;
  523. integer icons = 1;
  524. for (; icons <= my numberOfConstraints; icons ++) {
  525. integer winnerMarks = winner -> marks [my index [icons]]; // order is important, so indirect
  526. integer loserMarks = loser -> marks [my index [icons]];
  527. if (loserMarks < winnerMarks) break;
  528. if (loserMarks > winnerMarks) equivalent = false;
  529. }
  530. if (icons > my numberOfConstraints) { // completed the loop?
  531. if (warnIfStalled && ! equivalent)
  532. Melder_warning (U"Correct output is harmonically bounded (by having strict superset violations as compared to the learner's output)! EDCD stalls.\n"
  533. U"Correct output: ", loser -> string.get(), U"\nLearner's output: ", winner -> string.get());
  534. return;
  535. }
  536. /*
  537. * Determine the stratum into which some constraints will be demoted.
  538. */
  539. pivotRanking = my constraints [my index [icons]]. ranking;
  540. if (updateRule == kOTGrammar_rerankingStrategy::EDCD_WITH_VACATION) {
  541. integer numberOfConstraintsToDemote = 0;
  542. for (icons = 1; icons <= my numberOfConstraints; icons ++) {
  543. integer winnerMarks = winner -> marks [icons];
  544. integer loserMarks = loser -> marks [icons];
  545. if (loserMarks > winnerMarks) {
  546. OTConstraint constraint = & my constraints [icons];
  547. if (constraint -> ranking >= pivotRanking) {
  548. numberOfConstraintsToDemote += 1;
  549. }
  550. }
  551. }
  552. if (numberOfConstraintsToDemote > 0) {
  553. for (icons = 1; icons <= my numberOfConstraints; icons ++) {
  554. OTConstraint constraint = & my constraints [icons];
  555. if (constraint -> ranking < pivotRanking) {
  556. constraint -> ranking -= numberOfConstraintsToDemote * step * constraint -> plasticity;
  557. if (grammarHasChanged) *grammarHasChanged = true;
  558. }
  559. }
  560. }
  561. }
  562. /*
  563. * Demote all the uniquely violated constraints in the loser
  564. * that have rankings not lower than the pivot.
  565. */
  566. for (icons = 1; icons <= my numberOfConstraints; icons ++) {
  567. integer numberOfConstraintsDemoted = 0;
  568. integer winnerMarks = winner -> marks [my index [icons]]; // For the vacation version, the order is important, so indirect.
  569. integer loserMarks = loser -> marks [my index [icons]];
  570. if (loserMarks > winnerMarks) {
  571. OTConstraint constraint = & my constraints [my index [icons]];
  572. double constraintStep = step * constraint -> plasticity;
  573. if (constraint -> ranking >= pivotRanking) {
  574. numberOfConstraintsDemoted += 1;
  575. constraint -> ranking = pivotRanking - numberOfConstraintsDemoted * constraintStep; // This preserves the order of the demotees.
  576. if (grammarHasChanged) *grammarHasChanged = true;
  577. }
  578. }
  579. }
  580. } else if (updateRule == kOTGrammar_rerankingStrategy::DEMOTION_ONLY) {
  581. /*
  582. * Determine the crucial loser mark.
  583. */
  584. integer crucialLoserMark;
  585. OTConstraint offendingConstraint;
  586. integer icons = 1;
  587. for (; icons <= my numberOfConstraints; icons ++) {
  588. integer winnerMarks = winner -> marks [my index [icons]]; /* Order is important, so indirect. */
  589. integer loserMarks = loser -> marks [my index [icons]];
  590. if (my constraints [my index [icons]]. tiedToTheRight)
  591. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  592. if (loserMarks < winnerMarks) {
  593. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  594. Melder_throw (U"Demotion-only learning step: Loser wins! Should never happen.");
  595. } else {
  596. // do nothing; the whole demotion-only idea does not really apply very well to non-OT decision strategies
  597. }
  598. }
  599. if (loserMarks > winnerMarks) break;
  600. }
  601. if (icons > my numberOfConstraints) { // completed the loop?
  602. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  603. Melder_throw (U"(OTGrammar_step:) Loser equals correct candidate: loser \"",
  604. loser -> string.get(), U"\", winner \"", winner -> string.get(), U"\".");
  605. } else {
  606. // do nothing
  607. }
  608. } else {
  609. crucialLoserMark = icons;
  610. /*
  611. * Demote the highest uniquely violated constraint in the loser.
  612. */
  613. offendingConstraint = & my constraints [my index [crucialLoserMark]];
  614. double constraintStep = step * offendingConstraint -> plasticity;
  615. offendingConstraint -> ranking -= constraintStep;
  616. if (grammarHasChanged) *grammarHasChanged = true;
  617. }
  618. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGHEST_DOWN) {
  619. bool changed = false;
  620. integer numberOfUp = 0;
  621. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  622. integer winnerMarks = winner -> marks [icons];
  623. integer loserMarks = loser -> marks [icons];
  624. if (winnerMarks > loserMarks) {
  625. numberOfUp ++;
  626. }
  627. }
  628. if (numberOfUp > 0) {
  629. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  630. OTConstraint constraint = & my constraints [icons];
  631. double constraintStep = step * constraint -> plasticity;
  632. integer winnerMarks = winner -> marks [icons];
  633. integer loserMarks = loser -> marks [icons];
  634. if (winnerMarks > loserMarks) {
  635. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  636. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) / numberOfUp;
  637. }
  638. }
  639. integer crucialLoserMark, winnerMarks = 0, loserMarks = 0;
  640. OTConstraint offendingConstraint;
  641. integer icons = 1;
  642. for (; icons <= my numberOfConstraints; icons ++) {
  643. winnerMarks = winner -> marks [my index [icons]]; // order is important, so indirect
  644. loserMarks = loser -> marks [my index [icons]];
  645. if (my constraints [my index [icons]]. tiedToTheRight)
  646. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  647. if (loserMarks < winnerMarks) {
  648. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  649. Melder_throw (U"Demotion-only learning step: Loser wins! Should never happen.");
  650. } else {
  651. // do nothing; the whole demotion-only idea does not really apply very well to non-OT decision strategies
  652. }
  653. }
  654. if (loserMarks > winnerMarks) break;
  655. }
  656. if (icons > my numberOfConstraints) { // completed the loop?
  657. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  658. Melder_throw (U"(OTGrammar_step:) Loser equals correct candidate: loser \"",
  659. loser -> string.get(), U"\", winner \"", winner -> string.get(), U"\".");
  660. } else {
  661. // do nothing
  662. }
  663. } else {
  664. crucialLoserMark = icons;
  665. /*
  666. * Demote the highest uniquely violated constraint in the loser.
  667. */
  668. offendingConstraint = & my constraints [my index [crucialLoserMark]];
  669. double constraintStep = step * offendingConstraint -> plasticity;
  670. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  671. offendingConstraint -> ranking -= /*numberOfUp **/ constraintStep * (1.0 - offendingConstraint -> ranking * my leak);
  672. }
  673. }
  674. if (grammarHasChanged) *grammarHasChanged = changed;
  675. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGHEST_DOWN_2012) {
  676. bool changed = false;
  677. integer numberOfUp = 0;
  678. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  679. integer winnerMarks = winner -> marks [icons];
  680. integer loserMarks = loser -> marks [icons];
  681. if (winnerMarks > loserMarks) {
  682. numberOfUp ++;
  683. }
  684. }
  685. if (/*true ||*/ numberOfUp > 0) {
  686. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  687. OTConstraint constraint = & my constraints [icons];
  688. double constraintStep = step * constraint -> plasticity;
  689. integer winnerMarks = winner -> marks [icons];
  690. integer loserMarks = loser -> marks [icons];
  691. if (winnerMarks > loserMarks) {
  692. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  693. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) / (numberOfUp + 1);
  694. }
  695. }
  696. integer crucialLoserMark, winnerMarks = 0, loserMarks = 0;
  697. OTConstraint offendingConstraint;
  698. integer icons = 1;
  699. for (; icons <= my numberOfConstraints; icons ++) {
  700. winnerMarks = winner -> marks [my index [icons]]; /* Order is important, so indirect. */
  701. loserMarks = loser -> marks [my index [icons]];
  702. if (my constraints [my index [icons]]. tiedToTheRight)
  703. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  704. if (loserMarks < winnerMarks) {
  705. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  706. Melder_throw (U"Demotion-only learning step: Loser wins! Should never happen.");
  707. } else {
  708. // do nothing; the whole demotion-only idea does not really apply very well to non-OT decision strategies
  709. }
  710. }
  711. if (loserMarks > winnerMarks) break;
  712. }
  713. if (icons > my numberOfConstraints) { // completed the loop?
  714. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  715. Melder_throw (U"(OTGrammar_step:) Loser equals correct candidate: loser \"",
  716. loser -> string.get(), U"\", winner \"", winner -> string.get(), U"\".");
  717. } else {
  718. // do nothing
  719. }
  720. } else {
  721. crucialLoserMark = icons;
  722. /*
  723. * Demote the highest uniquely violated constraint in the loser.
  724. */
  725. offendingConstraint = & my constraints [my index [crucialLoserMark]];
  726. double constraintStep = step * offendingConstraint -> plasticity;
  727. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  728. offendingConstraint -> ranking -= /*numberOfUp **/ constraintStep * (1.0 - offendingConstraint -> ranking * my leak);
  729. }
  730. }
  731. if (grammarHasChanged) *grammarHasChanged = changed;
  732. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGH_DOWN) {
  733. integer numberOfDown = 0, numberOfUp = 0, lowestDemotableConstraint = 0;
  734. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  735. integer winnerMarks = winner -> marks [my index [icons]]; // the order is important, therefore indirect
  736. integer loserMarks = loser -> marks [my index [icons]];
  737. if (loserMarks < winnerMarks) {
  738. numberOfUp ++;
  739. } else if (loserMarks > winnerMarks) {
  740. if (numberOfUp == 0) {
  741. numberOfDown ++;
  742. lowestDemotableConstraint = icons;
  743. }
  744. }
  745. }
  746. if (numberOfUp > 0) {
  747. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  748. integer constraintIndex = my index [icons];
  749. OTConstraint constraint = & my constraints [constraintIndex];
  750. double constraintStep = step * constraint -> plasticity;
  751. integer winnerMarks = winner -> marks [constraintIndex]; // the order is important, therefore indirect
  752. integer loserMarks = loser -> marks [constraintIndex];
  753. if (my constraints [constraintIndex]. tiedToTheRight)
  754. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  755. if (loserMarks < winnerMarks) {
  756. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  757. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) * numberOfDown / (numberOfUp + 0.0);
  758. } else if (loserMarks > winnerMarks) {
  759. if (icons <= lowestDemotableConstraint) {
  760. if (multiplyStepByNumberOfViolations) constraintStep *= loserMarks - winnerMarks;
  761. constraint -> ranking -= constraintStep * (1.0 - constraint -> ranking * my leak);
  762. }
  763. }
  764. }
  765. if (grammarHasChanged) *grammarHasChanged = true;
  766. }
  767. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGH_DOWN_2012) {
  768. integer numberOfDown = 0, numberOfUp = 0, lowestDemotableConstraint = 0;
  769. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  770. integer winnerMarks = winner -> marks [my index [icons]]; // the order is important, therefore indirect
  771. integer loserMarks = loser -> marks [my index [icons]];
  772. if (loserMarks < winnerMarks) {
  773. numberOfUp ++;
  774. } else if (loserMarks > winnerMarks) {
  775. if (numberOfUp == 0) {
  776. numberOfDown ++;
  777. lowestDemotableConstraint = icons;
  778. }
  779. }
  780. }
  781. if (numberOfUp > 0) {
  782. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  783. integer constraintIndex = my index [icons];
  784. OTConstraint constraint = & my constraints [constraintIndex];
  785. double constraintStep = step * constraint -> plasticity;
  786. integer winnerMarks = winner -> marks [constraintIndex]; // the order is important, therefore indirect
  787. integer loserMarks = loser -> marks [constraintIndex];
  788. if (my constraints [constraintIndex]. tiedToTheRight)
  789. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  790. if (loserMarks < winnerMarks) {
  791. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - loserMarks;
  792. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) * numberOfDown / (numberOfUp + 1.0);
  793. } else if (loserMarks > winnerMarks) {
  794. if (icons <= lowestDemotableConstraint) {
  795. if (multiplyStepByNumberOfViolations) constraintStep *= loserMarks - winnerMarks;
  796. constraint -> ranking -= constraintStep * (1.0 - constraint -> ranking * my leak);
  797. }
  798. }
  799. }
  800. if (grammarHasChanged) *grammarHasChanged = true;
  801. }
  802. }
  803. }
  804. int OTMulti_learnOne (OTMulti me, conststring32 form1, conststring32 form2,
  805. enum kOTGrammar_rerankingStrategy updateRule, int direction, double plasticity, double relativePlasticityNoise)
  806. {
  807. integer iloser = OTMulti_getWinner (me, form1, form2);
  808. if (direction & OTMulti_LEARN_FORWARD) {
  809. if (Melder_debug == 47) OTMulti_newDisharmonies (me, 2.0);
  810. integer iwinner = OTMulti_getWinner (me, form1, U"");
  811. if (Melder_debug != 47 || ! OTMulti_candidateMatches (me, iwinner, form2, U""))
  812. OTMulti_modifyRankings (me, iwinner, iloser, updateRule, plasticity, relativePlasticityNoise);
  813. }
  814. if (direction & OTMulti_LEARN_BACKWARD) {
  815. if (Melder_debug == 47) OTMulti_newDisharmonies (me, 2.0);
  816. integer iwinner = OTMulti_getWinner (me, form2, U"");
  817. if (Melder_debug != 47 || ! OTMulti_candidateMatches (me, iwinner, form1, U""))
  818. OTMulti_modifyRankings (me, iwinner, iloser, updateRule, plasticity, relativePlasticityNoise);
  819. }
  820. return 1;
  821. }
  822. static autoTable OTMulti_createHistory (OTMulti me, integer storeHistoryEvery, integer numberOfData)
  823. {
  824. try {
  825. integer numberOfSamplingPoints = numberOfData / storeHistoryEvery; // e.g. 0, 20, 40, ...
  826. autoTable thee = Table_createWithoutColumnNames (1 + numberOfSamplingPoints, 3 + my numberOfConstraints);
  827. Table_setColumnLabel (thee.get(), 1, U"Datum");
  828. Table_setColumnLabel (thee.get(), 2, U"Form1");
  829. Table_setColumnLabel (thee.get(), 3, U"Form2");
  830. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  831. Table_setColumnLabel (thee.get(), 3 + icons, my constraints [icons]. name.get());
  832. }
  833. Table_setNumericValue (thee.get(), 1, 1, 0);
  834. Table_setStringValue (thee.get(), 1, 2, U"(initial)");
  835. Table_setStringValue (thee.get(), 1, 3, U"(initial)");
  836. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  837. Table_setNumericValue (thee.get(), 1, 3 + icons, my constraints [icons]. ranking);
  838. }
  839. return thee;
  840. } catch (MelderError) {
  841. Melder_throw (me, U": history not created.");
  842. }
  843. }
  844. static int OTMulti_updateHistory (OTMulti me, Table thee, integer storeHistoryEvery, integer idatum, conststring32 form1, conststring32 form2)
  845. {
  846. try {
  847. if (idatum % storeHistoryEvery == 0) {
  848. integer irow = 1 + idatum / storeHistoryEvery;
  849. Table_setNumericValue (thee, irow, 1, idatum);
  850. Table_setStringValue (thee, irow, 2, form1);
  851. Table_setStringValue (thee, irow, 3, form2);
  852. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  853. Table_setNumericValue (thee, irow, 3 + icons, my constraints [icons]. ranking);
  854. }
  855. }
  856. return 1;
  857. } catch (MelderError) {
  858. Melder_throw (me, U": history not updated.");
  859. }
  860. }
  861. void OTMulti_PairDistribution_learn (OTMulti me, PairDistribution thee, double evaluationNoise, enum kOTGrammar_rerankingStrategy updateRule, int direction,
  862. double initialPlasticity, integer replicationsPerPlasticity, double plasticityDecrement,
  863. integer numberOfPlasticities, double relativePlasticityNoise, integer storeHistoryEvery, autoTable *history_out)
  864. {
  865. integer idatum = 0, numberOfData = numberOfPlasticities * replicationsPerPlasticity;
  866. try {
  867. double plasticity = initialPlasticity;
  868. autoMelderMonitor monitor (U"Learning from partial pairs...");
  869. if (monitor.graphics()) {
  870. Graphics_clearWs (monitor.graphics());
  871. }
  872. autoTable history;
  873. if (storeHistoryEvery) {
  874. history = OTMulti_createHistory (me, storeHistoryEvery, numberOfData);
  875. }
  876. for (integer iplasticity = 1; iplasticity <= numberOfPlasticities; iplasticity ++) {
  877. for (integer ireplication = 1; ireplication <= replicationsPerPlasticity; ireplication ++) {
  878. conststring32 form1, form2;
  879. PairDistribution_peekPair (thee, & form1, & form2);
  880. ++ idatum;
  881. if (monitor.graphics() && idatum % (numberOfData / 400 + 1) == 0) {
  882. integer numberOfDrawnConstraints = my numberOfConstraints < 14 ? my numberOfConstraints : 14;
  883. if (numberOfDrawnConstraints > 0) {
  884. double sumOfRankings = 0.0;
  885. for (integer icons = 1; icons <= numberOfDrawnConstraints; icons ++) {
  886. sumOfRankings += my constraints [icons]. ranking;
  887. }
  888. double meanRanking = sumOfRankings / numberOfDrawnConstraints;
  889. Graphics_beginMovieFrame (monitor.graphics(), nullptr);
  890. Graphics_setWindow (monitor.graphics(), 0, numberOfData, meanRanking - 50, meanRanking + 50);
  891. for (integer icons = 1; icons <= numberOfDrawnConstraints; icons ++) {
  892. Graphics_setGrey (monitor.graphics(), (double) icons / numberOfDrawnConstraints);
  893. Graphics_line (monitor.graphics(), idatum, my constraints [icons]. ranking,
  894. idatum, my constraints [icons]. ranking+1);
  895. }
  896. Graphics_endMovieFrame (monitor.graphics(), 0.0);
  897. }
  898. }
  899. try {
  900. Melder_monitor ((double) idatum / numberOfData,
  901. U"Processing partial pair ", idatum, U" out of ", numberOfData,
  902. U":\n ", form1, U" ", form2);
  903. } catch (MelderError) {
  904. if (history_out)
  905. *history_out = history.move(); // so that we can inspect
  906. throw;
  907. }
  908. OTMulti_newDisharmonies (me, evaluationNoise);
  909. try {
  910. OTMulti_learnOne (me, form1, form2, updateRule, direction, plasticity, relativePlasticityNoise);
  911. } catch (MelderError) {
  912. if (history) {
  913. OTMulti_updateHistory (me, history.get(), storeHistoryEvery, idatum, form1, form2);
  914. }
  915. throw;
  916. }
  917. if (history) {
  918. OTMulti_updateHistory (me, history.get(), storeHistoryEvery, idatum, form1, form2);
  919. }
  920. }
  921. plasticity *= plasticityDecrement;
  922. }
  923. if (history_out)
  924. *history_out = history.move();
  925. } catch (MelderError) {
  926. if (idatum > 1)
  927. Melder_appendError (U"Only ", idatum - 1, U" input-output pairs out of ", numberOfData, U" were processed.");
  928. Melder_throw (me, U" & ", thee, U": learning from partial pairs not completed.");
  929. }
  930. }
  931. static integer OTMulti_crucialCell (OTMulti me, integer icand, integer iwinner, integer numberOfOptimalCandidates, conststring32 form1, conststring32 form2)
  932. {
  933. if (my numberOfCandidates < 2) return 0; // if there is only one candidate, all cells can be greyed
  934. if (OTMulti_compareCandidates (me, icand, iwinner) == 0) { // candidate equally good as winner?
  935. if (numberOfOptimalCandidates > 1) {
  936. /* All cells are important. */
  937. } else {
  938. integer secondBest = 0;
  939. for (integer jcand = 1; jcand <= my numberOfCandidates; jcand ++) {
  940. if (OTMulti_candidateMatches (me, jcand, form1, form2) && OTMulti_compareCandidates (me, jcand, iwinner) != 0) { // a non-optimal candidate?
  941. if (secondBest == 0) {
  942. secondBest = jcand; // first guess
  943. } else if (OTMulti_compareCandidates (me, jcand, secondBest) < 0) {
  944. secondBest = jcand; // better guess
  945. }
  946. }
  947. }
  948. if (secondBest == 0) return 0; // if all candidates are equally good, all cells can be greyed
  949. return OTMulti_crucialCell (me, secondBest, iwinner, 1, form1, form2);
  950. }
  951. } else {
  952. const constINTVEC candidateMarks = my candidates [icand]. marks.get();
  953. const constINTVEC winnerMarks = my candidates [iwinner]. marks.get();
  954. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  955. integer numberOfCandidateMarks = candidateMarks [my index [icons]];
  956. integer numberOfWinnerMarks = winnerMarks [my index [icons]];
  957. if (numberOfCandidateMarks > numberOfWinnerMarks)
  958. return icons;
  959. }
  960. }
  961. return my numberOfConstraints; /* Nothing grey. */
  962. }
  963. static double OTMulti_constraintWidth (Graphics g, OTConstraint constraint, bool showDisharmony) {
  964. char32 text [100], *newLine;
  965. double maximumWidth = showDisharmony ? 0.8 * Graphics_textWidth_ps (g, Melder_fixed (constraint -> disharmony, 1), true) : 0.0,
  966. firstWidth, secondWidth;
  967. str32cpy (text, constraint -> name.get());
  968. newLine = str32chr (text, U'\n');
  969. if (newLine) {
  970. *newLine = U'\0';
  971. firstWidth = Graphics_textWidth_ps (g, text, true);
  972. if (firstWidth > maximumWidth) maximumWidth = firstWidth;
  973. secondWidth = Graphics_textWidth_ps (g, newLine + 1, true);
  974. if (secondWidth > maximumWidth) maximumWidth = secondWidth;
  975. return maximumWidth;
  976. }
  977. firstWidth = Graphics_textWidth_ps (g, text, true);
  978. if (firstWidth > maximumWidth) maximumWidth = firstWidth;
  979. return maximumWidth;
  980. }
  981. void OTMulti_drawTableau (OTMulti me, Graphics g, conststring32 form1, conststring32 form2, bool vertical, bool showDisharmonies) {
  982. integer winner, winner1 = 0, winner2 = 0;
  983. double x, y, fontSize = Graphics_inqFontSize (g);
  984. Graphics_Colour colour = Graphics_inqColour (g);
  985. char32 text [200];
  986. bool bidirectional = form1 [0] != U'\0' && form2 [0] != U'\0';
  987. try {
  988. winner = OTMulti_getWinner (me, form1, form2);
  989. } catch (MelderError) {
  990. Melder_clearError ();
  991. Graphics_setWindow (g, 0.0, 1.0, 0.0, 1.0);
  992. Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_HALF);
  993. Graphics_rectangle (g, 0, 1, 0, 1);
  994. Graphics_text (g, 0.0, 0.5, U"(no matching candidates)");
  995. return;
  996. }
  997. if (bidirectional) {
  998. winner1 = OTMulti_getWinner (me, form1, U"");
  999. winner2 = OTMulti_getWinner (me, form2, U"");
  1000. }
  1001. Graphics_setWindow (g, 0.0, 1.0, 0.0, 1.0);
  1002. const double margin = Graphics_dxMMtoWC (g, 1.0);
  1003. const double fingerWidth = Graphics_dxMMtoWC (g, 7.0) * fontSize / 12.0;
  1004. const double doubleLineDx = Graphics_dxMMtoWC (g, 0.9);
  1005. const double doubleLineDy = Graphics_dyMMtoWC (g, 0.9);
  1006. const double rowHeight = Graphics_dyMMtoWC (g, 1.5 * fontSize * 25.4 / 72);
  1007. const double descent = rowHeight * 0.5;
  1008. const double worldAspectRatio = Graphics_dyMMtoWC (g, 1.0) / Graphics_dxMMtoWC (g, 1.0); // because Graphics_textWidth measures in the x direction only
  1009. /*
  1010. * Compute height of header row.
  1011. */
  1012. double headerHeight;
  1013. if (vertical) {
  1014. headerHeight = 0.0;
  1015. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1016. OTConstraint constraint = & my constraints [icons];
  1017. double constraintTextWidth = Graphics_textWidth (g, constraint -> name.get());
  1018. if (constraintTextWidth > headerHeight)
  1019. headerHeight = constraintTextWidth;
  1020. }
  1021. headerHeight += margin * 2;
  1022. headerHeight *= worldAspectRatio;
  1023. } else {
  1024. headerHeight = rowHeight;
  1025. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1026. OTConstraint constraint = & my constraints [icons];
  1027. if (str32chr (constraint -> name.get(), U'\n')) {
  1028. headerHeight += 0.7 * rowHeight;
  1029. break;
  1030. }
  1031. }
  1032. }
  1033. /*
  1034. * Compute longest candidate string.
  1035. * Also count the number of optimal candidates (if there are more than one, the fingers will be drawn in red).
  1036. */
  1037. double candWidth = Graphics_textWidth_ps (g, form1, true) + Graphics_textWidth_ps (g, form2, true);
  1038. integer numberOfMatchingCandidates = 0;
  1039. integer numberOfOptimalCandidates = 0;
  1040. integer numberOfOptimalCandidates1 = 0;
  1041. integer numberOfOptimalCandidates2 = 0;
  1042. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) {
  1043. if ((form1 [0] != U'\0' && OTMulti_candidateMatches (me, icand, form1, U"")) ||
  1044. (form2 [0] != U'\0' && OTMulti_candidateMatches (me, icand, form2, U"")) ||
  1045. (form1 [0] == U'\0' && form2 [0] == U'\0'))
  1046. {
  1047. double width = Graphics_textWidth_ps (g, my candidates [icand]. string.get(), true);
  1048. if (width > candWidth) candWidth = width;
  1049. numberOfMatchingCandidates ++;
  1050. if (OTMulti_compareCandidates (me, icand, winner) == 0) {
  1051. numberOfOptimalCandidates ++;
  1052. }
  1053. if (winner1 != 0 && OTMulti_compareCandidates (me, icand, winner1) == 0) {
  1054. numberOfOptimalCandidates1 ++;
  1055. }
  1056. if (winner2 != 0 && OTMulti_compareCandidates (me, icand, winner2) == 0) {
  1057. numberOfOptimalCandidates2 ++;
  1058. }
  1059. }
  1060. }
  1061. candWidth += fingerWidth * (bidirectional ? 3 : 1) + margin * 3;
  1062. /*
  1063. * Compute tableau width.
  1064. */
  1065. double tableauWidth = candWidth + doubleLineDx;
  1066. if (vertical) {
  1067. tableauWidth += rowHeight * my numberOfConstraints / worldAspectRatio;
  1068. } else {
  1069. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1070. OTConstraint constraint = & my constraints [icons];
  1071. tableauWidth += OTMulti_constraintWidth (g, constraint, showDisharmonies);
  1072. }
  1073. tableauWidth += margin * 2 * my numberOfConstraints;
  1074. }
  1075. /*
  1076. * Draw box.
  1077. */
  1078. x = doubleLineDx; // left side of tableau
  1079. y = 1.0 - doubleLineDy;
  1080. if (showDisharmonies) y -= 0.6 * rowHeight;
  1081. Graphics_rectangle (g, x, x + tableauWidth,
  1082. y - headerHeight - numberOfMatchingCandidates * rowHeight - doubleLineDy, y);
  1083. /*
  1084. * Draw input.
  1085. */
  1086. y -= headerHeight;
  1087. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
  1088. Graphics_text (g, x + 0.5 * candWidth, y + 0.5 * headerHeight, form1, form2);
  1089. Graphics_rectangle (g, x, x + candWidth, y, y + headerHeight);
  1090. /*
  1091. * Draw constraint names.
  1092. */
  1093. x += candWidth + doubleLineDx;
  1094. if (vertical) Graphics_setTextRotation (g, 90.0);
  1095. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1096. OTConstraint constraint = & my constraints [my index [icons]];
  1097. double width = vertical ? rowHeight / worldAspectRatio : OTMulti_constraintWidth (g, constraint, showDisharmonies) + margin * 2;
  1098. if (str32chr (constraint -> name.get(), U'\n')) {
  1099. char32 *newLine;
  1100. Melder_sprint (text,200, constraint -> name.get());
  1101. newLine = str32chr (text, U'\n');
  1102. *newLine = U'\0';
  1103. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_TOP);
  1104. Graphics_text (g, x + 0.5 * width, y + headerHeight, text);
  1105. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_BOTTOM);
  1106. Graphics_text (g, x + 0.5 * width, y, newLine + 1);
  1107. } else if (vertical) {
  1108. Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_HALF);
  1109. Graphics_text (g, x + 0.5 * width, y + margin, constraint -> name.get());
  1110. } else {
  1111. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
  1112. Graphics_text (g, x + 0.5 * width, y + 0.5 * headerHeight, constraint -> name.get());
  1113. }
  1114. if (showDisharmonies) {
  1115. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_BOTTOM);
  1116. Graphics_setFontSize (g, 0.8 * fontSize);
  1117. Graphics_text (g, x + 0.5 * width, y + headerHeight, Melder_fixed (constraint -> disharmony, 1));
  1118. Graphics_setFontSize (g, fontSize);
  1119. }
  1120. Graphics_line (g, x, y, x, y + headerHeight);
  1121. Graphics_line (g, x, y, x + width, y);
  1122. x += width;
  1123. }
  1124. if (vertical) Graphics_setTextRotation (g, 0.0);
  1125. /*
  1126. * Draw candidates.
  1127. */
  1128. y -= doubleLineDy;
  1129. for (integer icand = 1; icand <= my numberOfCandidates; icand ++)
  1130. if ((form1 [0] != U'\0' && OTMulti_candidateMatches (me, icand, form1, U"")) ||
  1131. (form2 [0] != U'\0' && OTMulti_candidateMatches (me, icand, form2, U"")) ||
  1132. (form1 [0] == U'\0' && form2 [0] == U'\0'))
  1133. {
  1134. const integer crucialCell = OTMulti_crucialCell (me, icand, winner, numberOfOptimalCandidates, form1, form2);
  1135. const bool candidateIsOptimal = ( OTMulti_compareCandidates (me, icand, winner) == 0 );
  1136. const bool candidateIsOptimal1 = ( winner1 != 0 && OTMulti_compareCandidates (me, icand, winner1) == 0 );
  1137. const bool candidateIsOptimal2 = ( winner2 != 0 && OTMulti_compareCandidates (me, icand, winner2) == 0 );
  1138. /*
  1139. * Draw candidate transcription.
  1140. */
  1141. x = doubleLineDx;
  1142. y -= rowHeight;
  1143. Graphics_setTextAlignment (g, Graphics_RIGHT, Graphics_HALF);
  1144. Graphics_text (g, x + candWidth - margin, y + descent, my candidates [icand]. string.get());
  1145. if (candidateIsOptimal) {
  1146. Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_HALF);
  1147. Graphics_setFontSize (g, (int) ((bidirectional ? 1.2 : 1.5) * fontSize));
  1148. if (numberOfOptimalCandidates > 1) Graphics_setColour (g, Graphics_RED);
  1149. Graphics_text (g, x + margin, y + descent - Graphics_dyMMtoWC (g, 0.5) * fontSize / 12.0, bidirectional ? U"\\Vr" : U"\\pf");
  1150. Graphics_setColour (g, colour);
  1151. Graphics_setFontSize (g, (int) fontSize);
  1152. }
  1153. if (candidateIsOptimal1) {
  1154. Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_HALF);
  1155. Graphics_setFontSize (g, (int) (1.5 * fontSize));
  1156. if (numberOfOptimalCandidates1 > 1) Graphics_setColour (g, Graphics_RED);
  1157. Graphics_text (g, x + margin + fingerWidth, y + descent - Graphics_dyMMtoWC (g, 0.5) * fontSize / 12.0, U"\\pf");
  1158. Graphics_setColour (g, colour);
  1159. Graphics_setFontSize (g, (int) fontSize);
  1160. }
  1161. if (candidateIsOptimal2) {
  1162. Graphics_setTextAlignment (g, Graphics_RIGHT, Graphics_HALF);
  1163. Graphics_setFontSize (g, (int) (1.5 * fontSize));
  1164. if (numberOfOptimalCandidates2 > 1) Graphics_setColour (g, Graphics_RED);
  1165. Graphics_setTextRotation (g, 180);
  1166. Graphics_text (g, x + margin + fingerWidth * 2, y + descent - Graphics_dyMMtoWC (g, 0.0) * fontSize / 12.0, U"\\pf");
  1167. Graphics_setTextRotation (g, 0);
  1168. Graphics_setColour (g, colour);
  1169. Graphics_setFontSize (g, (int) fontSize);
  1170. }
  1171. Graphics_rectangle (g, x, x + candWidth, y, y + rowHeight);
  1172. /*
  1173. * Draw grey cell backgrounds.
  1174. */
  1175. if (! bidirectional && my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  1176. x = candWidth + 2 * doubleLineDx;
  1177. Graphics_setGrey (g, 0.9);
  1178. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1179. integer index = my index [icons];
  1180. OTConstraint constraint = & my constraints [index];
  1181. double width = vertical ? rowHeight / worldAspectRatio : OTMulti_constraintWidth (g, constraint, showDisharmonies) + margin * 2;
  1182. if (icons > crucialCell)
  1183. Graphics_fillRectangle (g, x, x + width, y, y + rowHeight);
  1184. x += width;
  1185. }
  1186. Graphics_setColour (g, colour);
  1187. }
  1188. /*
  1189. * Draw cell marks.
  1190. */
  1191. x = candWidth + 2 * doubleLineDx;
  1192. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
  1193. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1194. integer index = my index [icons];
  1195. OTConstraint constraint = & my constraints [index];
  1196. double width = vertical ? rowHeight / worldAspectRatio : OTMulti_constraintWidth (g, constraint, showDisharmonies) + margin * 2;
  1197. char32 markString [40];
  1198. markString [0] = U'\0';
  1199. if (bidirectional && my candidates [icand]. marks [index] > 0) {
  1200. if ((candidateIsOptimal1 || candidateIsOptimal2) && ! candidateIsOptimal) {
  1201. str32cat (markString, U"\\<-");
  1202. }
  1203. }
  1204. if (bidirectional && my candidates [icand]. marks [index] < 0) {
  1205. if (candidateIsOptimal && ! candidateIsOptimal1) {
  1206. str32cat (markString, U"\\<-");
  1207. }
  1208. if (candidateIsOptimal && ! candidateIsOptimal2) {
  1209. str32cat (markString, U"\\<-");
  1210. }
  1211. }
  1212. /*
  1213. * An exclamation mark can be drawn in this cell only if both of the following conditions are met:
  1214. * 1. the candidate is not optimal;
  1215. * 2. this is the crucial cell, i.e. the cells after it are drawn in grey.
  1216. */
  1217. if (! bidirectional && icons == crucialCell && ! candidateIsOptimal &&
  1218. my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY)
  1219. {
  1220. integer winnerMarks = my candidates [winner]. marks [index];
  1221. if (winnerMarks + 1 > 5) {
  1222. str32cat (markString, Melder_integer (winnerMarks + 1));
  1223. } else {
  1224. for (integer imark = 1; imark <= winnerMarks + 1; imark ++)
  1225. str32cat (markString, U"*");
  1226. }
  1227. for (integer imark = my candidates [icand]. marks [index]; imark < 0; imark ++)
  1228. str32cat (markString, U"+");
  1229. str32cat (markString, U"!");
  1230. if (my candidates [icand]. marks [index] - (winnerMarks + 2) + 1 > 5) {
  1231. str32cat (markString, Melder_integer (my candidates [icand]. marks [index] - (winnerMarks + 2) + 1));
  1232. } else {
  1233. for (integer imark = winnerMarks + 2; imark <= my candidates [icand]. marks [index]; imark ++)
  1234. str32cat (markString, U"*");
  1235. }
  1236. } else {
  1237. if (my candidates [icand]. marks [index] > 5) {
  1238. str32cat (markString, Melder_integer (my candidates [icand]. marks [index]));
  1239. } else {
  1240. for (integer imark = 1; imark <= my candidates [icand]. marks [index]; imark ++)
  1241. str32cat (markString, U"*");
  1242. for (integer imark = my candidates [icand]. marks [index]; imark < 0; imark ++)
  1243. str32cat (markString, U"+");
  1244. }
  1245. }
  1246. if (bidirectional && my candidates [icand]. marks [index] > 0) {
  1247. if (candidateIsOptimal && ! candidateIsOptimal1)
  1248. str32cat (markString, U"\\->");
  1249. if (candidateIsOptimal && ! candidateIsOptimal2)
  1250. str32cat (markString, U"\\->");
  1251. }
  1252. if (bidirectional && my candidates [icand]. marks [index] < 0) {
  1253. if ((candidateIsOptimal1 || candidateIsOptimal2) && ! candidateIsOptimal)
  1254. str32cat (markString, U"\\->");
  1255. }
  1256. Graphics_text (g, x + 0.5 * width, y + descent, markString);
  1257. Graphics_setColour (g, colour);
  1258. Graphics_line (g, x, y, x, y + rowHeight);
  1259. Graphics_line (g, x, y + rowHeight, x + width, y + rowHeight);
  1260. x += width;
  1261. }
  1262. }
  1263. /*
  1264. * Draw box.
  1265. */
  1266. x = doubleLineDx; /* Left side of tableau. */
  1267. y = 1.0 - doubleLineDy;
  1268. if (showDisharmonies) y -= 0.6 * rowHeight;
  1269. Graphics_rectangle (g, x, x + tableauWidth,
  1270. y - headerHeight - numberOfMatchingCandidates * rowHeight - doubleLineDy, y);
  1271. }
  1272. void OTMulti_reset (OTMulti me, double ranking) {
  1273. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1274. OTConstraint constraint = & my constraints [icons];
  1275. constraint -> disharmony = constraint -> ranking = ranking;
  1276. }
  1277. OTMulti_sort (me);
  1278. }
  1279. void OTMulti_setRanking (OTMulti me, integer constraint, double ranking, double disharmony) {
  1280. try {
  1281. if (constraint < 1)
  1282. Melder_throw (U"The constraint number should be positive, not ", constraint, U".");
  1283. if (constraint > my numberOfConstraints)
  1284. Melder_throw (U"Constraint ", constraint, U" does not exist (there are only ", my numberOfConstraints, U" constraints).");
  1285. my constraints [constraint]. ranking = ranking;
  1286. my constraints [constraint]. disharmony = disharmony;
  1287. OTMulti_sort (me);
  1288. } catch (MelderError) {
  1289. Melder_throw (me, U": ranking not set.");
  1290. }
  1291. }
  1292. void OTMulti_setConstraintPlasticity (OTMulti me, integer constraint, double plasticity) {
  1293. try {
  1294. if (constraint < 1)
  1295. Melder_throw (U"The constraint number should be positive, not ", constraint, U".");
  1296. if (constraint > my numberOfConstraints)
  1297. Melder_throw (U"Constraint ", constraint, U" does not exist (there are only ", my numberOfConstraints, U" constraints).");
  1298. my constraints [constraint]. plasticity = plasticity;
  1299. } catch (MelderError) {
  1300. Melder_throw (me, U": constraint plasticity not set.");
  1301. }
  1302. }
  1303. void OTMulti_removeConstraint (OTMulti me, conststring32 constraintName) {
  1304. try {
  1305. integer removed = 0;
  1306. if (my numberOfConstraints <= 1)
  1307. Melder_throw (me, U": cannot remove last constraint.");
  1308. /*
  1309. Look for the constraint to be removed.
  1310. */
  1311. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1312. OTConstraint constraint = & my constraints [icons];
  1313. if (str32equ (constraint -> name.get(), constraintName)) {
  1314. removed = icons;
  1315. break;
  1316. }
  1317. }
  1318. if (removed == 0)
  1319. Melder_throw (U"No constraint \"", constraintName, U"\".");
  1320. /*
  1321. Remove the constraint while reusing the memory space.
  1322. */
  1323. for (integer icons = removed; icons < my numberOfConstraints; icons ++) {
  1324. my constraints [icons] = std::move (my constraints [icons + 1]);
  1325. }
  1326. my constraints [my numberOfConstraints]. destroy (); // this will do nothing except if the removed constraint is the last one
  1327. my numberOfConstraints -= 1;
  1328. /*
  1329. Shift tableau rows.
  1330. */
  1331. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) {
  1332. OTCandidate candidate = & my candidates [icand];
  1333. candidate -> numberOfConstraints -= 1;
  1334. for (integer icons = removed; icons <= my numberOfConstraints; icons ++) {
  1335. candidate -> marks [icons] = candidate -> marks [icons + 1];
  1336. }
  1337. }
  1338. /*
  1339. Rebuild index.
  1340. */
  1341. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) my index [icons] = icons;
  1342. OTMulti_sort (me);
  1343. } catch (MelderError) {
  1344. Melder_throw (me, U": constraint not removed.");
  1345. }
  1346. }
  1347. autostring32 OTMulti_generateOptimalForm (OTMulti me, conststring32 form1, conststring32 form2, double evaluationNoise) {
  1348. try {
  1349. OTMulti_newDisharmonies (me, evaluationNoise);
  1350. integer winner = OTMulti_getWinner (me, form1, form2);
  1351. return Melder_dup (my candidates [winner]. string.get());
  1352. } catch (MelderError) {
  1353. Melder_throw (me, U": optimal form not generated.");
  1354. }
  1355. }
  1356. autoStrings OTMulti_Strings_generateOptimalForms (OTMulti me, Strings thee, double evaluationNoise) {
  1357. try {
  1358. autoStrings outputs = Thing_new (Strings);
  1359. integer n = thy numberOfStrings;
  1360. outputs -> numberOfStrings = n;
  1361. outputs -> strings = autostring32vector (n);
  1362. for (integer i = 1; i <= n; i ++)
  1363. outputs -> strings [i] = OTMulti_generateOptimalForm (me, thy strings [i].get(), U"", evaluationNoise);
  1364. return outputs;
  1365. } catch (MelderError) {
  1366. Melder_throw (me, U" & ", thee, U": optimal forms not generated.");
  1367. }
  1368. }
  1369. autoStrings OTMulti_generateOptimalForms (OTMulti me, conststring32 form1, conststring32 form2, integer numberOfTrials, double evaluationNoise) {
  1370. try {
  1371. autoStrings outputs = Thing_new (Strings);
  1372. outputs -> numberOfStrings = numberOfTrials;
  1373. outputs -> strings = autostring32vector (numberOfTrials);
  1374. for (integer i = 1; i <= numberOfTrials; i ++)
  1375. outputs -> strings [i] = OTMulti_generateOptimalForm (me, form1, form2, evaluationNoise);
  1376. return outputs;
  1377. } catch (MelderError) {
  1378. Melder_throw (me, U": optimal forms not generated.");
  1379. }
  1380. }
  1381. autoDistributions OTMulti_to_Distribution (OTMulti me, conststring32 form1, conststring32 form2,
  1382. integer numberOfTrials, double evaluationNoise)
  1383. {
  1384. try {
  1385. integer totalNumberOfOutputs = 0, iout = 0;
  1386. /*
  1387. Count the total number of outputs.
  1388. */
  1389. for (integer icand = 1; icand <= my numberOfCandidates; icand ++)
  1390. if (OTMulti_candidateMatches (me, icand, form1, form2))
  1391. totalNumberOfOutputs ++;
  1392. /*
  1393. Create the distribution. One row for every output form.
  1394. */
  1395. autoDistributions thee = Distributions_create (totalNumberOfOutputs, 1);
  1396. autoINTVEC index = INTVECraw (my numberOfCandidates);
  1397. /*
  1398. Set the row labels to the output strings.
  1399. */
  1400. iout = 0;
  1401. for (integer icand = 1; icand <= my numberOfCandidates; icand ++) {
  1402. if (OTMulti_candidateMatches (me, icand, form1, form2)) {
  1403. thy rowLabels [++ iout] = Melder_dup (my candidates [icand]. string.get());
  1404. index [icand] = iout;
  1405. }
  1406. }
  1407. /*
  1408. Compute a number of outputs and store the results.
  1409. */
  1410. for (integer itrial = 1; itrial <= numberOfTrials; itrial ++) {
  1411. OTMulti_newDisharmonies (me, evaluationNoise);
  1412. integer iwinner = OTMulti_getWinner (me, form1, form2);
  1413. thy data [index [iwinner]] [1] += 1;
  1414. }
  1415. return thee;
  1416. } catch (MelderError) {
  1417. Melder_throw (me, U": distribution not computed.");
  1418. }
  1419. }
  1420. /* End of file OTMulti.cpp */