OTGrammar.cpp 125 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931
  1. /* OTGrammar.cpp
  2. *
  3. * Copyright (C) 1997-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 2002/07/16 GPL
  20. * pb 2002/11/04 randomize in case of equal candidates
  21. * pb 2003/05/08 better superset violation warning
  22. * pb 2003/05/23 made superset violation warning conditional
  23. * pb 2003/10/15 backtrack in case of failing multiple chews for EDCD
  24. * pb 2003/10/15 crucial ties option
  25. * pb 2004/01/17 OTGrammar_Distributions_getFractionCorrect
  26. * pb 2004/08/08 OTGrammar_removeHarmonicallyBoundedCandidates
  27. * pb 2004/08/09 bug removal: more complete OTGrammar_save and restore (affected multiple-chew correctness),
  28. * changing the 114.5 in Boersma (Phonology 2003) to 118.1
  29. * pb 2004/08/09 suppressed superset violation in case of identical constraint violation patterns such
  30. * as for /(L L2) L (L2 L) (L1 L)/ and /(L2 L) L (L L2) (L1 L)/, thus restricting the warning to cases
  31. * of *strict* superset violations
  32. * pb 2004/08/11 repaired memory leak in OTGrammarTableau_removeCandidate_unstripped
  33. * pb 2004/09/10 monitor rankings during learning from PairDistribution or Distributions
  34. * pb 2004/10/16 struct structOTxx
  35. * pb 2005/01/24 write to headerless spreadsheet file
  36. * pb 2005/04/19 OTHistory
  37. * pb 2005/06/30 learning from partial pairs
  38. * pb 2005/12/11 OTGrammar_honourlocalRankings:
  39. * pb 2005/12/11 OTGrammar_PairDistribution_listObligatoryRankings (depth 1)
  40. * pb 2006/01/05 new decision strategies: HarmonicGrammar and LinearOT
  41. * pb 2006/01/21 better procedure name
  42. * pb 2006/02/02 new decision strategy: ExponentialHG
  43. * pb 2006/12/08 MelderInfo
  44. * pb 2007/04/22 multiply learning step by number of violations (for HarmonicGrammar and LinearOT)
  45. * pb 2007/04/25 new decision strategy: MaximumEntropy
  46. * pb 2007/04/30 many improvements
  47. * pb 2007/05/20 new decision strategy: PositiveHG
  48. * pb 2007/06/21 corrected PositiveHG
  49. * pb 2007/06/21 made spreadsheet file readable as Table
  50. * pb 2007/07/24 leak and constraint plasticity
  51. * pb 2007/07/27 leak and constraint plasticity also written...
  52. * pb 2007/08/08 wchar
  53. * pb 2007/10/01 can write as encoding
  54. * pb 2008/03/03 EDCD with vacation
  55. * pb 2008/03/07 Demote one with vacation
  56. * pb 2008/03/07 Reset to random total ranking
  57. * pb 2008/03/27 Exponential HG: reset average weight to zero after every change
  58. * pb 2008/03/28 Exponential HG: set update rule to HG-GLA rather than OT-GLA
  59. * pb 2008/03/31 OTGrammar_PairDistribution_findPositiveWeights
  60. * pb 2008/04/08 made (OTGrammar & Distributions) learnFromPartialOutputs and getFractionCorrect five times faster
  61. * pb 2008/04/12 split off NUMlinprog
  62. * pb 2008/05/31 new decision strategy: ExponentialMaximumEntropy
  63. * pb 2009/03/09 new update rule: Weighted all up, highest down
  64. * pb 2009/07/07 OTGrammar_PairDistribution_getMinimumNumberCorrect
  65. * pb 2010/06/05 corrected colours
  66. * pb 2011/03/01 renamed "strategy" to "updateRule", "meanLearningStep" to "plasticity", "rankingSpreading" to "evaluationNoise"
  67. * pb 2011/03/22 C++
  68. * pb 2011/04/27 Melder_debug 41 and 42
  69. * pb 2011/07/14 C++
  70. * pb 2014/02/27 skippable symmetric all
  71. * pb 2014/07/25 RRIP
  72. */
  73. #include "OTGrammar.h"
  74. #include "oo_DESTROY.h"
  75. #include "OTGrammar_def.h"
  76. #include "oo_COPY.h"
  77. #include "OTGrammar_def.h"
  78. #include "oo_EQUAL.h"
  79. #include "OTGrammar_def.h"
  80. #include "oo_CAN_WRITE_AS_ENCODING.h"
  81. #include "OTGrammar_def.h"
  82. #include "oo_WRITE_BINARY.h"
  83. #include "OTGrammar_def.h"
  84. #include "oo_READ_BINARY.h"
  85. #include "OTGrammar_def.h"
  86. #include "oo_DESCRIPTION.h"
  87. #include "OTGrammar_def.h"
  88. #include "enums_getText.h"
  89. #include "OTGrammar_enums.h"
  90. #include "enums_getValue.h"
  91. #include "OTGrammar_enums.h"
  92. void structOTGrammar :: v_info ()
  93. {
  94. structDaata :: v_info ();
  95. integer numberOfCandidates = 0, numberOfViolations = 0;
  96. for (integer itab = 1; itab <= numberOfTableaus; itab ++) {
  97. numberOfCandidates += tableaus [itab]. numberOfCandidates;
  98. for (integer icand = 1; icand <= tableaus [itab]. numberOfCandidates; icand ++)
  99. for (integer icons = 1; icons <= numberOfConstraints; icons ++)
  100. numberOfViolations += tableaus [itab]. candidates [icand]. marks [icons];
  101. }
  102. MelderInfo_writeLine (U"Decision strategy: ", kOTGrammar_decisionStrategy_getText (decisionStrategy));
  103. MelderInfo_writeLine (U"Number of constraints: ", numberOfConstraints);
  104. MelderInfo_writeLine (U"Number of tableaus: ", numberOfTableaus);
  105. MelderInfo_writeLine (U"Number of candidates: ", numberOfCandidates);
  106. MelderInfo_writeLine (U"Number of violation marks: ", numberOfViolations);
  107. }
  108. void structOTGrammar :: v_writeText (MelderFile file) {
  109. MelderFile_write (file, U"\n<", kOTGrammar_decisionStrategy_getText (decisionStrategy),
  110. U">\n", leak, U" ! leak\n", numberOfConstraints, U" constraints");
  111. for (integer icons = 1; icons <= numberOfConstraints; icons ++) {
  112. OTGrammarConstraint constraint = & constraints [icons];
  113. MelderFile_write (file, U"\nconstraint [", icons, U"]: \"");
  114. for (const char32 *p = & constraint -> name [0]; *p; p ++) {
  115. if (*p == U'\"')
  116. MelderFile_writeCharacter (file, U'\"'); // double any quotes within quotes
  117. MelderFile_writeCharacter (file, *p);
  118. }
  119. MelderFile_write (file, U"\" ", constraint -> ranking,
  120. U" ", constraint -> disharmony, U" ", constraint -> plasticity, U" ! ");
  121. for (const char32 *p = & constraint -> name [0]; *p; p ++) {
  122. if (*p == U'\n')
  123. MelderFile_writeCharacter (file, U' ');
  124. else if (*p == U'\\' && p [1] == U's' && p [2] == U'{')
  125. p += 2;
  126. else if (*p == U'}')
  127. { }
  128. else
  129. MelderFile_writeCharacter (file, *p);
  130. }
  131. }
  132. MelderFile_write (file, U"\n\n", numberOfFixedRankings, U" fixed rankings");
  133. for (integer irank = 1; irank <= numberOfFixedRankings; irank ++) {
  134. OTGrammarFixedRanking fixedRanking = & fixedRankings [irank];
  135. MelderFile_write (file, U"\n ", fixedRanking -> higher, U" ", fixedRanking -> lower);
  136. }
  137. MelderFile_write (file, U"\n\n", numberOfTableaus, U" tableaus");
  138. for (integer itab = 1; itab <= numberOfTableaus; itab ++) {
  139. OTGrammarTableau tableau = & tableaus [itab];
  140. MelderFile_write (file, U"\ninput [", itab, U"]: \"");
  141. for (const char32 *p = & tableau -> input [0]; *p; p ++) {
  142. if (*p == U'\"')
  143. MelderFile_writeCharacter (file, U'\"'); // double any quotes within quotes
  144. MelderFile_writeCharacter (file, *p);
  145. }
  146. MelderFile_write (file, U"\" ", tableau -> numberOfCandidates);
  147. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  148. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  149. MelderFile_write (file, U"\n candidate [", icand, U"]: \"");
  150. for (const char32 *p = & candidate -> output [0]; *p; p ++) {
  151. if (*p == U'\"')
  152. MelderFile_writeCharacter (file, U'\"'); // double any quotes within quotes
  153. MelderFile_writeCharacter (file, *p);
  154. }
  155. MelderFile_writeCharacter (file, U'\"');
  156. for (integer icons = 1; icons <= candidate -> numberOfConstraints; icons ++) {
  157. MelderFile_write (file, U" ", candidate -> marks [icons]);
  158. }
  159. }
  160. }
  161. }
  162. void OTGrammar_checkIndex (OTGrammar me) {
  163. if (my index) return;
  164. my index = NUMvector <integer> (1, my numberOfConstraints);
  165. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) my index [icons] = icons;
  166. OTGrammar_sort (me);
  167. }
  168. void structOTGrammar :: v_readText (MelderReadText text, int formatVersion) {
  169. OTGrammar_Parent :: v_readText (text, formatVersion);
  170. if (formatVersion >= 1) {
  171. try {
  172. decisionStrategy = (kOTGrammar_decisionStrategy) texgete8 (text, (enum_generic_getValue) kOTGrammar_decisionStrategy_getValue);
  173. } catch (MelderError) {
  174. Melder_throw (U"Trying to read decision strategy.");
  175. }
  176. }
  177. if (formatVersion >= 2) {
  178. try {
  179. leak = texgetr64 (text);
  180. } catch (MelderError) {
  181. Melder_throw (U"Trying to read leak.");
  182. }
  183. }
  184. try {
  185. numberOfConstraints = texgeti32 (text);
  186. } catch (MelderError) {
  187. Melder_throw (U"Trying to read number of constraints.");
  188. }
  189. if (numberOfConstraints < 1) Melder_throw (U"No constraints.");
  190. constraints = NUMvector <structOTGrammarConstraint> (1, numberOfConstraints);
  191. for (integer icons = 1; icons <= numberOfConstraints; icons ++) {
  192. OTGrammarConstraint constraint = & constraints [icons];
  193. try {
  194. constraint -> name = texgetw16 (text);
  195. } catch (MelderError) {
  196. Melder_throw (U"Trying to read name of constraint ", icons, U".");
  197. }
  198. try {
  199. constraint -> ranking = texgetr64 (text);
  200. } catch (MelderError) {
  201. Melder_throw (U"Trying to read ranking of constraint ", icons, U".");
  202. }
  203. try {
  204. constraint -> disharmony = texgetr64 (text);
  205. } catch (MelderError) {
  206. Melder_throw (U"Trying to read disharmony of constraint ", icons, U".");
  207. }
  208. if (formatVersion < 2) {
  209. constraint -> plasticity = 1.0;
  210. } else {
  211. try {
  212. constraint -> plasticity = texgetr64 (text);
  213. } catch (MelderError) {
  214. Melder_throw (U"Trying to read plasticity of constraint ", icons, U".");
  215. }
  216. }
  217. }
  218. try {
  219. numberOfFixedRankings = texgeti32 (text);
  220. } catch (MelderError) {
  221. Melder_throw (U"Trying to read number of fixed rankings.");
  222. }
  223. if (numberOfFixedRankings >= 1) {
  224. fixedRankings = NUMvector <structOTGrammarFixedRanking> (1, numberOfFixedRankings);
  225. for (integer irank = 1; irank <= numberOfFixedRankings; irank ++) {
  226. OTGrammarFixedRanking fixedRanking = & fixedRankings [irank];
  227. try {
  228. fixedRanking -> higher = texgeti32 (text);
  229. } catch (MelderError) {
  230. Melder_throw (U"Trying to read the higher of constraint pair ", irank, U".");
  231. }
  232. try {
  233. fixedRanking -> lower = texgeti32 (text);
  234. } catch (MelderError) {
  235. Melder_throw (U"Trying to read the lower of constraint pair ", irank, U".");
  236. }
  237. }
  238. }
  239. try {
  240. numberOfTableaus = texgeti32 (text);
  241. } catch (MelderError) {
  242. Melder_throw (U"Trying to read number of tableaus.");
  243. }
  244. if (numberOfTableaus < 1) Melder_throw (U"No tableaus.");
  245. tableaus = NUMvector <structOTGrammarTableau> (1, numberOfTableaus);
  246. for (integer itab = 1; itab <= numberOfTableaus; itab ++) {
  247. OTGrammarTableau tableau = & tableaus [itab];
  248. try {
  249. tableau -> input = texgetw16 (text);
  250. } catch (MelderError) {
  251. Melder_throw (U"Trying to read input of tableau ", itab, U".");
  252. }
  253. try {
  254. tableau -> numberOfCandidates = texgeti32 (text);
  255. } catch (MelderError) {
  256. Melder_throw (U"Trying to read number of candidates of tableau ", itab, U".");
  257. }
  258. Melder_require (tableau -> numberOfCandidates > 0,
  259. U"No candidates in tableau ", itab,
  260. U" (input: ", tableau -> input.get(), U")"
  261. U" in line ", MelderReadText_getLineNumber (text),
  262. itab == 1 ? U"." : U", or perhaps wrong number of candidates for input «",
  263. itab == 1 ? nullptr : tableaus [itab - 1]. input.get(),
  264. itab == 1 ? nullptr : U"»."
  265. );
  266. tableau -> candidates = NUMvector <structOTGrammarCandidate> (1, tableau -> numberOfCandidates);
  267. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  268. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  269. try {
  270. candidate -> output = texgetw16 (text);
  271. } catch (MelderError) {
  272. Melder_throw (U"Trying to read candidate ", icand, U" of tableau ", itab,
  273. U" (input: ", tableau -> input.get(), U") in line ", MelderReadText_getLineNumber (text), U".");
  274. }
  275. candidate -> numberOfConstraints = numberOfConstraints; // redundancy, needed for writing binary
  276. candidate -> marks = INTVECzero (candidate -> numberOfConstraints);
  277. for (integer icons = 1; icons <= candidate -> numberOfConstraints; icons ++) {
  278. try {
  279. candidate -> marks [icons] = texgeti16 (text);
  280. } catch (MelderError) {
  281. Melder_throw
  282. (U"Trying to read number of violations of constraint ", icons,
  283. U" (", constraints [icons]. name.get(), U")"
  284. U" of candidate ", icand,
  285. U" (", candidate -> output.get(), U")"
  286. U" of tableau ", itab,
  287. U" (input: ", tableau -> input.get(), U")"
  288. U" in line ", MelderReadText_getLineNumber (text), U".");
  289. }
  290. }
  291. }
  292. }
  293. OTGrammar_checkIndex (this);
  294. }
  295. Thing_implement (OTGrammar, Daata, 2);
  296. Thing_implement (OTHistory, TableOfReal, 0);
  297. static OTGrammar constraintCompare_grammar;
  298. static int constraintCompare (const void *first, const void *second) {
  299. OTGrammar me = constraintCompare_grammar;
  300. integer icons = * (integer *) first, jcons = * (integer *) second;
  301. OTGrammarConstraint ci = & my constraints [icons], cj = & my constraints [jcons];
  302. /*
  303. Sort primarily by disharmony.
  304. */
  305. if (ci -> disharmony > cj -> disharmony) return -1;
  306. if (ci -> disharmony < cj -> disharmony) return +1;
  307. /*
  308. Tied constraints are sorted alphabetically.
  309. */
  310. return str32cmp (my constraints [icons]. name.get(), my constraints [jcons]. name.get());
  311. }
  312. void OTGrammar_sort (OTGrammar me) {
  313. constraintCompare_grammar = me;
  314. qsort (& my index [1], my numberOfConstraints, sizeof (integer), constraintCompare);
  315. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  316. OTGrammarConstraint constraint = & my constraints [my index [icons]];
  317. constraint -> tiedToTheLeft = ( icons > 1 &&
  318. my constraints [my index [icons - 1]]. disharmony == constraint -> disharmony );
  319. constraint -> tiedToTheRight = ( icons < my numberOfConstraints &&
  320. my constraints [my index [icons + 1]]. disharmony == constraint -> disharmony );
  321. }
  322. }
  323. void OTGrammar_newDisharmonies (OTGrammar me, double spreading) {
  324. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  325. OTGrammarConstraint constraint = & my constraints [icons];
  326. constraint -> disharmony = constraint -> ranking + NUMrandomGauss (0, spreading)
  327. /*NUMrandomUniform (-spreading, spreading)*/;
  328. }
  329. OTGrammar_sort (me);
  330. }
  331. integer OTGrammar_getTableau (OTGrammar me, conststring32 input) {
  332. integer n = my numberOfTableaus;
  333. for (integer i = 1; i <= n; i ++)
  334. if (str32equ (my tableaus [i]. input.get(), input))
  335. return i;
  336. Melder_throw (U"Input \"", input, U"\" not in list of tableaus.");
  337. }
  338. static void _OTGrammar_fillInHarmonies (OTGrammar me, integer itab) noexcept {
  339. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) return;
  340. OTGrammarTableau tableau = & my tableaus [itab];
  341. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  342. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  343. INTVEC marks = candidate -> marks.get();
  344. longdouble disharmony = 0.0;
  345. if (my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
  346. my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY)
  347. {
  348. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  349. disharmony += my constraints [icons]. disharmony * marks [icons];
  350. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
  351. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
  352. {
  353. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  354. disharmony += exp (my constraints [icons]. disharmony) * marks [icons];
  355. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT) {
  356. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  357. if (my constraints [icons]. disharmony > 0.0)
  358. disharmony += my constraints [icons]. disharmony * marks [icons];
  359. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG) {
  360. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  361. double constraintDisharmony = my constraints [icons]. disharmony > 1.0 ? my constraints [icons]. disharmony : 1.0;
  362. disharmony += constraintDisharmony * marks [icons];
  363. }
  364. } else {
  365. Melder_fatal (U"_OTGrammar_fillInHarmonies: unimplemented decision strategy.");
  366. }
  367. candidate -> harmony = - (double) disharmony;
  368. }
  369. }
  370. int OTGrammar_compareCandidates (OTGrammar me, integer itab1, integer icand1, integer itab2, integer icand2) noexcept {
  371. INTVEC marks1 = my tableaus [itab1]. candidates [icand1]. marks.get();
  372. INTVEC marks2 = my tableaus [itab2]. candidates [icand2]. marks.get();
  373. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  374. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  375. integer numberOfMarks1 = marks1 [my index [icons]];
  376. integer numberOfMarks2 = marks2 [my index [icons]];
  377. /*
  378. Count tied constraints as one.
  379. */
  380. while (my constraints [my index [icons]]. tiedToTheRight) {
  381. icons ++;
  382. numberOfMarks1 += marks1 [my index [icons]];
  383. numberOfMarks2 += marks2 [my index [icons]];
  384. }
  385. if (numberOfMarks1 < numberOfMarks2) return -1; // candidate 1 is better than candidate 2
  386. if (numberOfMarks1 > numberOfMarks2) return +1; // candidate 2 is better than candidate 1
  387. }
  388. /* If we arrive here, None of the comparisons found a difference between the two candidates. Hence, they are equally good. */
  389. return 0;
  390. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
  391. my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY)
  392. {
  393. double disharmony1 = 0.0, disharmony2 = 0.0;
  394. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  395. disharmony1 += my constraints [icons]. disharmony * marks1 [icons];
  396. disharmony2 += my constraints [icons]. disharmony * marks2 [icons];
  397. }
  398. if (disharmony1 < disharmony2) return -1; // candidate 1 is better than candidate 2
  399. if (disharmony1 > disharmony2) return +1; // candidate 2 is better than candidate 1
  400. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT) {
  401. double disharmony1 = 0.0, disharmony2 = 0.0;
  402. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  403. if (my constraints [icons]. disharmony > 0.0) {
  404. disharmony1 += my constraints [icons]. disharmony * marks1 [icons];
  405. disharmony2 += my constraints [icons]. disharmony * marks2 [icons];
  406. }
  407. }
  408. if (disharmony1 < disharmony2) return -1; // candidate 1 is better than candidate 2
  409. if (disharmony1 > disharmony2) return +1; // candidate 2 is better than candidate 1
  410. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
  411. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
  412. {
  413. double disharmony1 = 0.0, disharmony2 = 0.0;
  414. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  415. disharmony1 += exp (my constraints [icons]. disharmony) * marks1 [icons];
  416. disharmony2 += exp (my constraints [icons]. disharmony) * marks2 [icons];
  417. }
  418. if (disharmony1 < disharmony2) return -1; // candidate 1 is better than candidate 2
  419. if (disharmony1 > disharmony2) return +1; // candidate 2 is better than candidate 1
  420. } else if (my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG) {
  421. double disharmony1 = 0.0, disharmony2 = 0.0;
  422. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  423. double constraintDisharmony = my constraints [icons]. disharmony > 1.0 ? my constraints [icons]. disharmony : 1.0;
  424. disharmony1 += constraintDisharmony * marks1 [icons];
  425. disharmony2 += constraintDisharmony * marks2 [icons];
  426. }
  427. if (disharmony1 < disharmony2) return -1; // candidate 1 is better than candidate 2
  428. if (disharmony1 > disharmony2) return +1; // candidate 2 is better than candidate 1
  429. } else Melder_fatal (U"Unimplemented decision strategy.");
  430. return 0; // the two total disharmonies are equal
  431. }
  432. static void _OTGrammar_fillInProbabilities (OTGrammar me, integer itab) noexcept {
  433. OTGrammarTableau tableau = & my tableaus [itab];
  434. double maximumHarmony = tableau -> candidates [1]. harmony;
  435. for (integer icand = 2; icand <= tableau -> numberOfCandidates; icand ++) {
  436. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  437. if (candidate -> harmony > maximumHarmony)
  438. maximumHarmony = candidate -> harmony;
  439. }
  440. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  441. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  442. candidate -> probability = exp (candidate -> harmony - maximumHarmony);
  443. Melder_assert (candidate -> probability >= 0.0 && candidate -> probability <= 1.0);
  444. }
  445. double sumOfProbabilities = 0.0;
  446. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  447. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  448. sumOfProbabilities += candidate -> probability;
  449. }
  450. Melder_assert (sumOfProbabilities > 0.0); // because at least one of them is 1.0
  451. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  452. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  453. candidate -> probability /= sumOfProbabilities;
  454. }
  455. }
  456. integer OTGrammar_getWinner (OTGrammar me, integer itab) noexcept {
  457. integer icand_best = 1;
  458. if (my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
  459. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
  460. {
  461. _OTGrammar_fillInHarmonies (me, itab);
  462. _OTGrammar_fillInProbabilities (me, itab);
  463. double cutOff = NUMrandomUniform (0.0, 1.0);
  464. double sumOfProbabilities = 0.0;
  465. for (integer icand = 1; icand <= my tableaus [itab]. numberOfCandidates; icand ++) {
  466. sumOfProbabilities += my tableaus [itab]. candidates [icand]. probability;
  467. if (sumOfProbabilities > cutOff) {
  468. icand_best = icand;
  469. break;
  470. }
  471. }
  472. } else {
  473. integer numberOfBestCandidates = 1;
  474. for (integer icand = 2; icand <= my tableaus [itab]. numberOfCandidates; icand ++) {
  475. int comparison = OTGrammar_compareCandidates (me, itab, icand, itab, icand_best);
  476. if (comparison == -1) {
  477. icand_best = icand; // the current candidate is the unique best candidate found so far
  478. numberOfBestCandidates = 1;
  479. } else if (comparison == 0) {
  480. numberOfBestCandidates += 1; // the current candidate is equally good as the best found before
  481. /*
  482. * Give all candidates that are equally good an equal chance to become the winner.
  483. */
  484. if (Melder_debug == 41) {
  485. icand_best = icand_best; // keep first
  486. } else if (Melder_debug == 42) {
  487. icand_best = icand; // take last
  488. } else if (NUMrandomUniform (0.0, numberOfBestCandidates) < 1.0) { // default: take random
  489. icand_best = icand;
  490. }
  491. }
  492. }
  493. }
  494. return icand_best;
  495. }
  496. integer OTGrammar_getNumberOfOptimalCandidates (OTGrammar me, integer itab) {
  497. if (my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
  498. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY) return 1;
  499. integer icand_best = 1, numberOfBestCandidates = 1;
  500. for (integer icand = 2; icand <= my tableaus [itab]. numberOfCandidates; icand ++) {
  501. int comparison = OTGrammar_compareCandidates (me, itab, icand, itab, icand_best);
  502. if (comparison == -1) {
  503. icand_best = icand; // the current candidate is the best candidate found so far
  504. numberOfBestCandidates = 1;
  505. } else if (comparison == 0) {
  506. numberOfBestCandidates += 1; // the current candidate is equally good as the best found before
  507. }
  508. }
  509. return numberOfBestCandidates;
  510. }
  511. bool OTGrammar_isCandidateGrammatical (OTGrammar me, integer itab, integer icand) {
  512. for (integer jcand = 1; jcand <= my tableaus [itab]. numberOfCandidates; jcand ++)
  513. if (jcand != icand && OTGrammar_compareCandidates (me, itab, jcand, itab, icand) < 0)
  514. return false;
  515. return true;
  516. }
  517. bool OTGrammar_isCandidateSinglyGrammatical (OTGrammar me, integer itab, integer icand) {
  518. for (integer jcand = 1; jcand <= my tableaus [itab]. numberOfCandidates; jcand ++)
  519. if (jcand != icand && OTGrammar_compareCandidates (me, itab, jcand, itab, icand) <= 0)
  520. return false;
  521. return true;
  522. }
  523. void OTGrammar_getInterpretiveParse (OTGrammar me, conststring32 partialOutput, integer *out_bestTableau, integer *out_bestCandidate) {
  524. try {
  525. integer itab_best = 0, icand_best = 0, numberOfBestCandidates = 0;
  526. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  527. OTGrammarTableau tableau = & my tableaus [itab];
  528. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  529. OTGrammarCandidate cand = & tableau -> candidates [icand];
  530. if (str32str (cand -> output.get(), partialOutput)) { // T&S' idea of surface->overt mapping
  531. if (itab_best == 0) {
  532. itab_best = itab; // the first compatible input/output pair found is the first guess for the best candidate
  533. icand_best = icand;
  534. numberOfBestCandidates = 1;
  535. } else {
  536. int comparison = OTGrammar_compareCandidates (me, itab, icand, itab_best, icand_best);
  537. if (comparison == -1) {
  538. itab_best = itab; // the current input/output pair is the best candidate found so far
  539. icand_best = icand;
  540. numberOfBestCandidates = 1;
  541. } else if (comparison == 0) {
  542. numberOfBestCandidates += 1; // the current input/output pair is equally good as the best found before
  543. /*
  544. * Give all candidates that are equally good an equal chance to become the winner.
  545. */
  546. if (Melder_debug == 41) {
  547. itab_best = itab_best;
  548. icand_best = icand_best; // keep first
  549. } else if (Melder_debug == 42) {
  550. itab_best = itab;
  551. icand_best = icand; // take last
  552. } else if (NUMrandomUniform (0.0, numberOfBestCandidates) < 1.0) { // default: take random
  553. itab_best = itab;
  554. icand_best = icand;
  555. }
  556. }
  557. }
  558. }
  559. }
  560. }
  561. if (itab_best == 0)
  562. Melder_throw (U"The partial output \"", partialOutput, U"\" does not match any candidate for any input form.");
  563. if (out_bestTableau)
  564. *out_bestTableau = itab_best;
  565. if (out_bestCandidate)
  566. *out_bestCandidate = icand_best;
  567. } catch (MelderError) {
  568. Melder_throw (U"Interpretive parse not computed.");
  569. }
  570. }
  571. static void OTGrammar_getInterpretiveParse_opt (OTGrammar me, integer ipartialOutput, integer *out_bestTableau, integer *out_bestCandidate) {
  572. try {
  573. integer itab_best = 0, icand_best = 0, numberOfBestCandidates = 0;
  574. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  575. OTGrammarTableau tableau = & my tableaus [itab];
  576. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  577. OTGrammarCandidate cand = & tableau -> candidates [icand];
  578. Melder_assert (cand -> partialOutputMatches);
  579. if (cand -> partialOutputMatches [ipartialOutput]) { // T&S' idea of surface->overt mapping
  580. if (itab_best == 0) {
  581. itab_best = itab; // the first compatible input/output pair found is the first guess for the best candidate
  582. icand_best = icand;
  583. numberOfBestCandidates = 1;
  584. } else {
  585. int comparison = OTGrammar_compareCandidates (me, itab, icand, itab_best, icand_best);
  586. if (comparison == -1) {
  587. itab_best = itab; // the current input/output pair is the best candidate found so far
  588. icand_best = icand;
  589. numberOfBestCandidates = 1;
  590. } else if (comparison == 0) {
  591. numberOfBestCandidates += 1; // the current input/output pair is equally good as the best found before
  592. /*
  593. * Give all candidates that are equally good an equal chance to become the winner.
  594. */
  595. if (Melder_debug == 41) {
  596. itab_best = itab_best;
  597. icand_best = icand_best; // keep first
  598. } else if (Melder_debug == 42) {
  599. itab_best = itab;
  600. icand_best = icand; // take last
  601. } else if (NUMrandomUniform (0.0, numberOfBestCandidates) < 1.0) { // default: take random
  602. itab_best = itab;
  603. icand_best = icand;
  604. }
  605. }
  606. }
  607. }
  608. }
  609. }
  610. Melder_assert (itab_best != 0);
  611. if (out_bestTableau)
  612. *out_bestTableau = itab_best;
  613. if (out_bestCandidate)
  614. *out_bestCandidate = icand_best;
  615. } catch (MelderError) {
  616. Melder_throw (U"Interpretive parse not computed.");
  617. }
  618. }
  619. bool OTGrammar_isPartialOutputGrammatical (OTGrammar me, conststring32 partialOutput) {
  620. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  621. OTGrammarTableau tableau = & my tableaus [itab];
  622. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  623. if (str32str (tableau -> candidates [icand]. output.get(), partialOutput))
  624. if (OTGrammar_isCandidateGrammatical (me, itab, icand))
  625. return true;
  626. }
  627. }
  628. return false;
  629. }
  630. bool OTGrammar_areAllPartialOutputsGrammatical (OTGrammar me, Strings thee) {
  631. for (integer ioutput = 1; ioutput <= thy numberOfStrings; ioutput ++) {
  632. conststring32 partialOutput = thy strings [ioutput].get();
  633. if (! OTGrammar_isPartialOutputGrammatical (me, partialOutput))
  634. return false;
  635. }
  636. return true;
  637. }
  638. bool OTGrammar_isPartialOutputSinglyGrammatical (OTGrammar me, conststring32 partialOutput) {
  639. bool found = false;
  640. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  641. OTGrammarTableau tableau = & my tableaus [itab];
  642. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  643. if (str32str (tableau -> candidates [icand]. output.get(), partialOutput)) {
  644. if (OTGrammar_isCandidateGrammatical (me, itab, icand)) {
  645. found = true;
  646. /*
  647. All other grammatical candidates should match.
  648. */
  649. for (integer jcand = 1; jcand <= tableau -> numberOfCandidates; jcand ++) {
  650. if (OTGrammar_compareCandidates (me, itab, jcand, itab, icand) == 0)
  651. if (! str32str (tableau -> candidates [jcand]. output.get(), partialOutput))
  652. return false; // partial output is multiply optimal
  653. }
  654. }
  655. }
  656. }
  657. }
  658. return found;
  659. }
  660. bool OTGrammar_areAllPartialOutputsSinglyGrammatical (OTGrammar me, Strings thee) {
  661. for (integer ioutput = 1; ioutput <= thy numberOfStrings; ioutput ++) {
  662. conststring32 partialOutput = thy strings [ioutput].get();
  663. if (! OTGrammar_isPartialOutputSinglyGrammatical (me, partialOutput))
  664. return false;
  665. }
  666. return true;
  667. }
  668. static integer OTGrammar_crucialCell (OTGrammar me, integer itab, integer icand, integer iwinner, integer numberOfOptimalCandidates) {
  669. OTGrammarTableau tableau = & my tableaus [itab];
  670. if (tableau -> numberOfCandidates < 2) return 0; // if there is only one candidate, all cells can be greyed
  671. if (my decisionStrategy != kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) return my numberOfConstraints; // nothing grey
  672. if (OTGrammar_compareCandidates (me, itab, icand, itab, iwinner) == 0) { // candidate equally good as winner?
  673. if (numberOfOptimalCandidates > 1) {
  674. /* All cells are important. */
  675. } else {
  676. integer secondBest = 0;
  677. for (integer jcand = 1; jcand <= tableau -> numberOfCandidates; jcand ++) {
  678. if (OTGrammar_compareCandidates (me, itab, jcand, itab, iwinner) != 0) { // a non-optimal candidate?
  679. if (secondBest == 0) {
  680. secondBest = jcand; // first guess
  681. } else if (OTGrammar_compareCandidates (me, itab, jcand, itab, secondBest) < 0) {
  682. secondBest = jcand; // better guess
  683. }
  684. }
  685. }
  686. if (secondBest == 0) return 0; // if all candidates are equally good, all cells can be greyed
  687. return OTGrammar_crucialCell (me, itab, secondBest, iwinner, 1);
  688. }
  689. } else {
  690. const constINTVEC candidateMarks = tableau -> candidates [icand]. marks.get();
  691. const constINTVEC winnerMarks = tableau -> candidates [iwinner]. marks.get();
  692. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  693. integer numberOfCandidateMarks = candidateMarks [my index [icons]];
  694. integer numberOfWinnerMarks = winnerMarks [my index [icons]];
  695. while (my constraints [my index [icons]]. tiedToTheRight) {
  696. icons ++;
  697. numberOfCandidateMarks += candidateMarks [my index [icons]];
  698. numberOfWinnerMarks += winnerMarks [my index [icons]];
  699. }
  700. if (numberOfCandidateMarks > numberOfWinnerMarks)
  701. return icons;
  702. }
  703. }
  704. return my numberOfConstraints; // nothing grey
  705. }
  706. static double OTGrammar_constraintWidth (Graphics g, conststring32 name) {
  707. char32 text [100];
  708. str32cpy (text, name);
  709. char32 *newLine = str32chr (text, U'\n');
  710. if (newLine) {
  711. double firstWidth, secondWidth;
  712. *newLine = U'\0';
  713. firstWidth = Graphics_textWidth (g, text);
  714. secondWidth = Graphics_textWidth (g, newLine + 1);
  715. return firstWidth > secondWidth ? firstWidth : secondWidth;
  716. }
  717. return Graphics_textWidth (g, text);
  718. }
  719. void OTGrammar_drawTableau (OTGrammar me, Graphics g, bool vertical, conststring32 input) {
  720. try {
  721. const double fontSize = Graphics_inqFontSize (g);
  722. Graphics_Colour colour = Graphics_inqColour (g);
  723. const integer itab = OTGrammar_getTableau (me, input);
  724. _OTGrammar_fillInHarmonies (me, itab);
  725. const integer winner = OTGrammar_getWinner (me, itab);
  726. Graphics_setWindow (g, 0.0, 1.0, 0.0, 1.0);
  727. const double margin = Graphics_dxMMtoWC (g, 1.0);
  728. const double fingerWidth = Graphics_dxMMtoWC (g, 7.0) * fontSize / 12.0;
  729. const double doubleLineDx = Graphics_dxMMtoWC (g, 0.9);
  730. const double doubleLineDy = Graphics_dyMMtoWC (g, 0.9);
  731. const double rowHeight = Graphics_dyMMtoWC (g, 1.5 * fontSize * 25.4 / 72);
  732. const double descent = rowHeight * 0.5;
  733. const double worldAspectRatio = Graphics_dyMMtoWC (g, 1.0) / Graphics_dxMMtoWC (g, 1.0); // because Graphics_textWidth measures in the x direction only
  734. /*
  735. Compute the height of the header row.
  736. */
  737. double headerHeight;
  738. if (vertical) {
  739. headerHeight = 0.0;
  740. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  741. const OTGrammarConstraint constraint = & my constraints [icons];
  742. const double constraintTextWidth = Graphics_textWidth (g, constraint -> name.get());
  743. if (constraintTextWidth > headerHeight)
  744. headerHeight = constraintTextWidth;
  745. }
  746. headerHeight += margin * 2;
  747. headerHeight *= worldAspectRatio;
  748. } else {
  749. headerHeight = rowHeight;
  750. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  751. OTGrammarConstraint constraint = & my constraints [icons];
  752. if (str32chr (constraint -> name.get(), U'\n')) {
  753. headerHeight *= 1.6;
  754. break;
  755. }
  756. }
  757. }
  758. /*
  759. Compute longest candidate string.
  760. Also count the number of optimal candidates (if there are more than one, the fingers will be drawn in red).
  761. */
  762. double candWidth = Graphics_textWidth (g, input);
  763. OTGrammarTableau tableau = & my tableaus [itab];
  764. integer numberOfOptimalCandidates = 0;
  765. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  766. double width = Graphics_textWidth (g, tableau -> candidates [icand]. output.get());
  767. if (OTGrammar_compareCandidates (me, itab, icand, itab, winner) == 0) {
  768. width += fingerWidth;
  769. numberOfOptimalCandidates ++;
  770. }
  771. if (width > candWidth) candWidth = width;
  772. }
  773. candWidth += margin * 3;
  774. /*
  775. Compute tableau width.
  776. */
  777. double tableauWidth = candWidth + doubleLineDx;
  778. if (vertical) {
  779. tableauWidth += rowHeight * my numberOfConstraints / worldAspectRatio;
  780. } else {
  781. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  782. OTGrammarConstraint constraint = & my constraints [icons];
  783. tableauWidth += OTGrammar_constraintWidth (g, constraint -> name.get());
  784. }
  785. tableauWidth += margin * 2 * my numberOfConstraints;
  786. }
  787. /*
  788. Draw box.
  789. */
  790. double x = doubleLineDx; // left side of tableau
  791. double y = 1.0 - doubleLineDy;
  792. Graphics_rectangle (g, x, x + tableauWidth,
  793. y - headerHeight - tableau -> numberOfCandidates * rowHeight - doubleLineDy, y);
  794. /*
  795. Draw input.
  796. */
  797. y -= headerHeight;
  798. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
  799. Graphics_text (g, x + 0.5 * candWidth, y + 0.5 * headerHeight, input);
  800. Graphics_rectangle (g, x, x + candWidth, y, y + headerHeight);
  801. /*
  802. Draw constraint names.
  803. */
  804. x += candWidth + doubleLineDx;
  805. if (vertical)
  806. Graphics_setTextRotation (g, 90.0);
  807. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  808. OTGrammarConstraint constraint = & my constraints [my index [icons]];
  809. double width = vertical ? rowHeight / worldAspectRatio : OTGrammar_constraintWidth (g, constraint -> name.get()) + margin * 2;
  810. if (str32chr (constraint -> name.get(), U'\n') && ! vertical) {
  811. autoMelderString text;
  812. MelderString_copy (& text, constraint -> name.get());
  813. char32 *newLine = str32chr (text.string, U'\n');
  814. *newLine = U'\0';
  815. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_TOP);
  816. Graphics_text (g, x + 0.5 * width, y + headerHeight, text.string);
  817. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_BOTTOM);
  818. Graphics_text (g, x + 0.5 * width, y, newLine + 1);
  819. } else if (vertical) {
  820. Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_HALF);
  821. Graphics_text (g, x + 0.5 * width, y + margin, constraint -> name.get());
  822. } else {
  823. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
  824. Graphics_text (g, x + 0.5 * width, y + 0.5 * headerHeight, constraint -> name.get());
  825. }
  826. if (constraint -> tiedToTheLeft)
  827. Graphics_setLineType (g, Graphics_DOTTED);
  828. Graphics_line (g, x, y, x, y + headerHeight);
  829. Graphics_setLineType (g, Graphics_DRAWN);
  830. Graphics_line (g, x, y, x + width, y);
  831. x += width;
  832. }
  833. if (vertical) Graphics_setTextRotation (g, 0.0);
  834. /*
  835. Draw candidates.
  836. */
  837. y -= doubleLineDy;
  838. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  839. integer crucialCell = OTGrammar_crucialCell (me, itab, icand, winner, numberOfOptimalCandidates);
  840. bool candidateIsOptimal = OTGrammar_compareCandidates (me, itab, icand, itab, winner) == 0;
  841. /*
  842. Draw candidate transcription.
  843. */
  844. x = doubleLineDx;
  845. y -= rowHeight;
  846. Graphics_setTextAlignment (g, Graphics_RIGHT, Graphics_HALF);
  847. Graphics_text (g, x + candWidth - margin, y + descent, tableau -> candidates [icand]. output.get());
  848. if (candidateIsOptimal) {
  849. Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_HALF);
  850. Graphics_setFontSize (g, (int) (1.5 * fontSize));
  851. if (numberOfOptimalCandidates > 1) Graphics_setColour (g, Graphics_RED);
  852. Graphics_text (g, x + margin, y + descent - Graphics_dyMMtoWC (g, 1.0) * fontSize / 12.0, U"☞");
  853. Graphics_setColour (g, colour);
  854. Graphics_setFontSize (g, (int) fontSize);
  855. }
  856. Graphics_rectangle (g, x, x + candWidth, y, y + rowHeight);
  857. /*
  858. Draw grey cell backgrounds.
  859. */
  860. x = candWidth + 2 * doubleLineDx;
  861. Graphics_setGrey (g, 0.9);
  862. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  863. const integer index = my index [icons];
  864. OTGrammarConstraint constraint = & my constraints [index];
  865. const double width = ( vertical ? rowHeight / worldAspectRatio :
  866. OTGrammar_constraintWidth (g, constraint -> name.get()) + margin * 2 );
  867. if (icons > crucialCell)
  868. Graphics_fillRectangle (g, x, x + width, y, y + rowHeight);
  869. x += width;
  870. }
  871. Graphics_setColour (g, colour);
  872. /*
  873. Draw cell marks.
  874. */
  875. x = candWidth + 2 * doubleLineDx;
  876. Graphics_setTextAlignment (g, Graphics_CENTRE, Graphics_HALF);
  877. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  878. const integer index = my index [icons];
  879. OTGrammarConstraint constraint = & my constraints [index];
  880. const double width = vertical ? rowHeight / worldAspectRatio : OTGrammar_constraintWidth (g, constraint -> name.get()) + margin * 2;
  881. static MelderString markString;
  882. MelderString_empty (& markString);
  883. if (my decisionStrategy == kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  884. /*
  885. An exclamation mark can be drawn in this cell only if all of the following conditions are met:
  886. 1. the candidate is not optimal;
  887. 2. the constraint is not tied;
  888. 3. this is the crucial cell, i.e. the cells after it are drawn in grey.
  889. */
  890. if (icons == crucialCell && ! candidateIsOptimal && ! constraint -> tiedToTheLeft && ! constraint -> tiedToTheRight) {
  891. const integer winnerMarks = tableau -> candidates [winner]. marks [index];
  892. for (integer imark = 1; imark <= winnerMarks + 1; imark ++)
  893. MelderString_appendCharacter (& markString, U'*');
  894. for (integer imark = tableau -> candidates [icand]. marks [index]; imark < 0; imark ++)
  895. MelderString_appendCharacter (& markString, U'+');
  896. MelderString_appendCharacter (& markString, U'!');
  897. for (integer imark = winnerMarks + 2; imark <= tableau -> candidates [icand]. marks [index]; imark ++)
  898. MelderString_appendCharacter (& markString, U'*');
  899. } else {
  900. if (! candidateIsOptimal && (constraint -> tiedToTheLeft || constraint -> tiedToTheRight) &&
  901. crucialCell >= 1 && constraint -> disharmony == my constraints [my index [crucialCell]]. disharmony)
  902. {
  903. Graphics_setColour (g, Graphics_RED);
  904. }
  905. for (integer imark = 1; imark <= tableau -> candidates [icand]. marks [index]; imark ++)
  906. MelderString_appendCharacter (& markString, U'*');
  907. for (integer imark = tableau -> candidates [icand]. marks [index]; imark < 0; imark ++)
  908. MelderString_appendCharacter (& markString, U'+');
  909. }
  910. } else {
  911. for (integer imark = 1; imark <= tableau -> candidates [icand]. marks [index]; imark ++)
  912. MelderString_appendCharacter (& markString, U'*');
  913. for (integer imark = tableau -> candidates [icand]. marks [index]; imark < 0; imark ++)
  914. MelderString_appendCharacter (& markString, U'+');
  915. }
  916. Graphics_text (g, x + 0.5 * width, y + descent, markString.string);
  917. Graphics_setColour (g, colour);
  918. if (constraint -> tiedToTheLeft)
  919. Graphics_setLineType (g, Graphics_DOTTED);
  920. Graphics_line (g, x, y, x, y + rowHeight);
  921. Graphics_setLineType (g, Graphics_DRAWN);
  922. Graphics_line (g, x, y + rowHeight, x + width, y + rowHeight);
  923. x += width;
  924. }
  925. /*
  926. Draw harmony.
  927. */
  928. if (my decisionStrategy != kOTGrammar_decisionStrategy::OPTIMALITY_THEORY) {
  929. Graphics_setTextAlignment (g, Graphics_LEFT, Graphics_HALF);
  930. const double value = tableau -> candidates [icand]. harmony;
  931. if (my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
  932. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY)
  933. {
  934. //value = value > 1e-308 ? 1000 : value < -1e308 ? -1000 : - log (- value);
  935. Graphics_text (g, x, y + descent, Melder_float (Melder_half (value)));
  936. } else {
  937. Graphics_text (g, x, y + descent, Melder_fixed (value, 3));
  938. }
  939. }
  940. }
  941. /*
  942. Draw box.
  943. */
  944. x = doubleLineDx; // left side of tableau
  945. y = 1.0 - doubleLineDy;
  946. Graphics_rectangle (g, x, x + tableauWidth,
  947. y - headerHeight - tableau -> numberOfCandidates * rowHeight - doubleLineDy, y);
  948. } catch (MelderError) {
  949. Melder_throw (me, U": tableau not drawn.");
  950. }
  951. }
  952. autoStrings OTGrammar_generateInputs (OTGrammar me, integer numberOfTrials) {
  953. try {
  954. autoStrings thee = Thing_new (Strings);
  955. thy strings = autostring32vector (thy numberOfStrings = numberOfTrials);
  956. for (integer i = 1; i <= numberOfTrials; i ++) {
  957. integer itab = NUMrandomInteger (1, my numberOfTableaus);
  958. thy strings [i] = Melder_dup (my tableaus [itab]. input.get());
  959. }
  960. return thee;
  961. } catch (MelderError) {
  962. Melder_throw (me, U": inputs not generated.");
  963. }
  964. }
  965. autoStrings OTGrammar_getInputs (OTGrammar me) {
  966. try {
  967. autoStrings thee = Thing_new (Strings);
  968. thy strings = autostring32vector (thy numberOfStrings = my numberOfTableaus);
  969. for (integer i = 1; i <= my numberOfTableaus; i ++)
  970. thy strings [i] = Melder_dup (my tableaus [i]. input.get());
  971. return thee;
  972. } catch (MelderError) {
  973. Melder_throw (me, U": inputs not gotten.");
  974. }
  975. }
  976. autostring32 OTGrammar_inputToOutput (OTGrammar me, conststring32 input, double evaluationNoise) {
  977. try {
  978. OTGrammar_newDisharmonies (me, evaluationNoise);
  979. integer itab = OTGrammar_getTableau (me, input);
  980. integer winner = OTGrammar_getWinner (me, itab);
  981. if (winner == 0)
  982. Melder_throw (U"No winner");
  983. return Melder_dup (my tableaus [itab]. candidates [winner]. output.get());
  984. } catch (MelderError) {
  985. Melder_throw (me, U": output not computed from input \"", input, U"\".");
  986. }
  987. }
  988. autoStrings OTGrammar_inputsToOutputs (OTGrammar me, Strings inputs, double evaluationNoise) {
  989. try {
  990. autoStrings him = Thing_new (Strings);
  991. integer n = inputs -> numberOfStrings;
  992. his numberOfStrings = n;
  993. his strings = autostring32vector (n);
  994. for (integer i = 1; i <= n; i ++)
  995. his strings [i] = OTGrammar_inputToOutput (me, inputs -> strings [i].get(), evaluationNoise);
  996. return him;
  997. } catch (MelderError) {
  998. Melder_throw (me, U": outputs not computed.");
  999. }
  1000. }
  1001. autoStrings OTGrammar_inputToOutputs (OTGrammar me, conststring32 input, integer n, double evaluationNoise) {
  1002. try {
  1003. autoStrings thee = Thing_new (Strings);
  1004. thy numberOfStrings = n;
  1005. thy strings = autostring32vector (n);
  1006. for (integer i = 1; i <= n; i ++)
  1007. thy strings [i] = OTGrammar_inputToOutput (me, input, evaluationNoise);
  1008. return thee;
  1009. } catch (MelderError) {
  1010. Melder_throw (me, U": output not computed.");
  1011. }
  1012. }
  1013. autoDistributions OTGrammar_to_Distribution (OTGrammar me, integer trialsPerInput, double noise) {
  1014. try {
  1015. integer totalNumberOfOutputs = 0, nout = 0;
  1016. /*
  1017. Count the total number of outputs.
  1018. */
  1019. for (integer itab = 1; itab <= my numberOfTableaus; itab ++)
  1020. totalNumberOfOutputs += my tableaus [itab]. numberOfCandidates;
  1021. /*
  1022. Create the distribution. One row for every output form.
  1023. */
  1024. autoDistributions thee = Distributions_create (totalNumberOfOutputs, 1);
  1025. /*
  1026. Measure every input form.
  1027. */
  1028. autoMelderProgress progress (U"OTGrammar: compute output distribution.");
  1029. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  1030. OTGrammarTableau tableau = & my tableaus [itab];
  1031. Melder_progress ((itab - 0.5) / my numberOfTableaus, U"Measuring input \"", tableau -> input.get(), U"\"");
  1032. /*
  1033. Set the row labels to the output strings.
  1034. */
  1035. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  1036. thy rowLabels [nout + icand] = Melder_dup (Melder_cat (tableau -> input.get(), U" \\-> ", tableau -> candidates [icand]. output.get()));
  1037. }
  1038. /*
  1039. Compute a number of outputs and store the results.
  1040. */
  1041. for (integer itrial = 1; itrial <= trialsPerInput; itrial ++) {
  1042. OTGrammar_newDisharmonies (me, noise);
  1043. integer iwinner = OTGrammar_getWinner (me, itab);
  1044. thy data [nout + iwinner] [1] += 1;
  1045. }
  1046. /*
  1047. Update the offset.
  1048. */
  1049. nout += tableau -> numberOfCandidates;
  1050. }
  1051. return thee;
  1052. } catch (MelderError) {
  1053. Melder_throw (me, U": output distribution not computed.");
  1054. }
  1055. }
  1056. autoPairDistribution OTGrammar_to_PairDistribution (OTGrammar me, integer trialsPerInput, double noise) {
  1057. try {
  1058. integer nout = 0;
  1059. /*
  1060. Create the distribution. One row for every output form.
  1061. */
  1062. autoPairDistribution thee = PairDistribution_create ();
  1063. /*
  1064. Measure every input form.
  1065. */
  1066. autoMelderProgress progress (U"OTGrammar: compute output distribution.");
  1067. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  1068. OTGrammarTableau tableau = & my tableaus [itab];
  1069. Melder_progress ((itab - 0.5) / my numberOfTableaus, U"Measuring input \"", tableau -> input.get(), U"\"");
  1070. /*
  1071. Copy the input and output strings to the target object.
  1072. */
  1073. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  1074. PairDistribution_add (thee.get(), tableau -> input.get(), tableau -> candidates [icand]. output.get(), 0.0);
  1075. }
  1076. /*
  1077. Compute a number of outputs and store the results.
  1078. */
  1079. for (integer itrial = 1; itrial <= trialsPerInput; itrial ++) {
  1080. OTGrammar_newDisharmonies (me, noise);
  1081. integer iwinner = OTGrammar_getWinner (me, itab);
  1082. thy pairs.at [nout + iwinner] -> weight += 1.0;
  1083. }
  1084. /*
  1085. Update the offset.
  1086. */
  1087. nout += tableau -> numberOfCandidates;
  1088. }
  1089. return thee;
  1090. } catch (MelderError) {
  1091. Melder_throw (me, U": output distribution not computed.");
  1092. }
  1093. }
  1094. static bool honoursFixedRankings (OTGrammar me) {
  1095. for (integer i = 1; i <= my numberOfFixedRankings; i ++) {
  1096. integer higher = my fixedRankings [i]. higher, lower = my fixedRankings [i]. lower;
  1097. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1098. if (my index [icons] == higher) break; // detected higher before lower: OK
  1099. if (my index [icons] == lower) return false;
  1100. }
  1101. }
  1102. return true;
  1103. }
  1104. autoDistributions OTGrammar_measureTypology_WEAK (OTGrammar me) {
  1105. try {
  1106. integer totalNumberOfOutputs = 0, nout = 0, ncons = my numberOfConstraints, nperm, factorial [1+12];
  1107. if (ncons > 12)
  1108. Melder_throw (U"Cannot handle more than 12 constraints.");
  1109. factorial [0] = 1;
  1110. for (integer icons = 1; icons <= ncons; icons ++)
  1111. factorial [icons] = factorial [icons - 1] * icons;
  1112. nperm = factorial [ncons];
  1113. /*
  1114. Count the total number of outputs.
  1115. */
  1116. for (integer itab = 1; itab <= my numberOfTableaus; itab ++)
  1117. totalNumberOfOutputs += my tableaus [itab]. numberOfCandidates;
  1118. /*
  1119. Create the distribution. One row for every output form.
  1120. */
  1121. autoDistributions thee = Distributions_create (totalNumberOfOutputs, 1);
  1122. /*
  1123. Measure every input form.
  1124. */
  1125. autoMelderProgress progress (U"Measuring typology...");
  1126. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  1127. OTGrammarTableau tableau = & my tableaus [itab];
  1128. Melder_progress ((itab - 0.5) / my numberOfTableaus, U"Measuring input \"", tableau -> input.get(), U"\"");
  1129. /*
  1130. Set the row labels to the output strings.
  1131. */
  1132. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  1133. thy rowLabels [nout + icand] = Melder_dup (Melder_cat (tableau -> input.get(), U" \\-> ", tableau -> candidates [icand]. output.get()));
  1134. }
  1135. /*
  1136. Compute a number of outputs and store the results.
  1137. */
  1138. for (integer iperm = 0; iperm < nperm; iperm ++) {
  1139. integer permleft = iperm, iwinner;
  1140. /* Initialize to 12345 before permuting. */
  1141. for (integer icons = 1; icons <= ncons; icons ++)
  1142. my index [icons] = icons;
  1143. for (integer icons = 1; icons < ncons; icons ++) {
  1144. integer fac = factorial [ncons - icons], shift = permleft / fac, dummy;
  1145. /*
  1146. Swap constraint with the one at a distance 'shift'.
  1147. */
  1148. dummy = my index [icons];
  1149. my index [icons] = my index [icons + shift];
  1150. my index [icons + shift] = dummy;
  1151. permleft %= fac;
  1152. }
  1153. if (honoursFixedRankings (me)) {
  1154. iwinner = OTGrammar_getWinner (me, itab);
  1155. thy data [nout + iwinner] [1] += 1;
  1156. }
  1157. }
  1158. /*
  1159. Update the offset.
  1160. */
  1161. nout += tableau -> numberOfCandidates;
  1162. }
  1163. return thee;
  1164. } catch (MelderError) {
  1165. Melder_throw (me, U": typology not measured.");
  1166. }
  1167. }
  1168. static double learningStep (double mean, double relativeSpreading) {
  1169. return relativeSpreading == 0.0 ? mean : NUMrandomGauss (mean, relativeSpreading * mean);
  1170. }
  1171. static void OTGrammar_honourLocalRankings (OTGrammar me, double plasticity, double relativePlasticityNoise, bool *grammarHasChanged) {
  1172. bool improved;
  1173. do {
  1174. improved = false;
  1175. for (integer irank = 1; irank <= my numberOfFixedRankings; irank ++) {
  1176. OTGrammarFixedRanking fixedRanking = & my fixedRankings [irank];
  1177. OTGrammarConstraint higher = & my constraints [fixedRanking -> higher], lower = & my constraints [fixedRanking -> lower];
  1178. while (higher -> ranking <= lower -> ranking) {
  1179. lower -> ranking -= learningStep (plasticity, relativePlasticityNoise);
  1180. if (grammarHasChanged)
  1181. *grammarHasChanged = true;
  1182. improved = true;
  1183. }
  1184. }
  1185. } while (improved);
  1186. }
  1187. static void OTGrammar_modifyRankings (OTGrammar me, integer itab, integer iwinner, integer iadult,
  1188. kOTGrammar_rerankingStrategy updateRule, bool honourLocalRankings,
  1189. double plasticity, double relativePlasticityNoise, bool warnIfStalled, bool *out_grammarHasChanged)
  1190. {
  1191. try {
  1192. OTGrammarTableau tableau = & my tableaus [itab];
  1193. OTGrammarCandidate winner = & tableau -> candidates [iwinner], adult = & tableau -> candidates [iadult];
  1194. double step = learningStep (plasticity, relativePlasticityNoise);
  1195. bool multiplyStepByNumberOfViolations =
  1196. my decisionStrategy == kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR ||
  1197. my decisionStrategy == kOTGrammar_decisionStrategy::LINEAR_OT ||
  1198. my decisionStrategy == kOTGrammar_decisionStrategy::MAXIMUM_ENTROPY ||
  1199. my decisionStrategy == kOTGrammar_decisionStrategy::POSITIVE_HG ||
  1200. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ||
  1201. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_MAXIMUM_ENTROPY;
  1202. if (Melder_debug != 0) {
  1203. /*
  1204. * Perhaps override the standard update rule.
  1205. */
  1206. if (Melder_debug == 26) multiplyStepByNumberOfViolations = false; // OT-GLA
  1207. else if (Melder_debug == 27) multiplyStepByNumberOfViolations = true; // HG-GLA
  1208. }
  1209. if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ONE) {
  1210. const integer icons = NUMrandomInteger (1, my numberOfConstraints);
  1211. const OTGrammarConstraint constraint = & my constraints [icons];
  1212. double constraintStep = step * constraint -> plasticity;
  1213. const integer winnerMarks = winner -> marks [icons];
  1214. const integer adultMarks = adult -> marks [icons];
  1215. if (adultMarks > winnerMarks) {
  1216. if (multiplyStepByNumberOfViolations)
  1217. constraintStep *= adultMarks - winnerMarks;
  1218. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak);
  1219. if (out_grammarHasChanged)
  1220. *out_grammarHasChanged = true;
  1221. }
  1222. if (winnerMarks > adultMarks) {
  1223. if (multiplyStepByNumberOfViolations)
  1224. constraintStep *= winnerMarks - adultMarks;
  1225. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak);
  1226. if (out_grammarHasChanged)
  1227. *out_grammarHasChanged = true;
  1228. }
  1229. } else if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ALL) {
  1230. bool changed = false;
  1231. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1232. const OTGrammarConstraint constraint = & my constraints [icons];
  1233. double constraintStep = step * constraint -> plasticity;
  1234. const integer winnerMarks = winner -> marks [icons];
  1235. const integer adultMarks = adult -> marks [icons];
  1236. if (adultMarks > winnerMarks) {
  1237. if (multiplyStepByNumberOfViolations)
  1238. constraintStep *= adultMarks - winnerMarks;
  1239. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak);
  1240. changed = true;
  1241. }
  1242. if (winnerMarks > adultMarks) {
  1243. if (multiplyStepByNumberOfViolations)
  1244. constraintStep *= winnerMarks - adultMarks;
  1245. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak);
  1246. changed = true;
  1247. }
  1248. }
  1249. if (changed && my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG)
  1250. {
  1251. longdouble sumOfWeights = 0.0;
  1252. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  1253. sumOfWeights += my constraints [icons]. ranking;
  1254. const double averageWeight = (double) sumOfWeights / my numberOfConstraints;
  1255. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  1256. my constraints [icons]. ranking -= averageWeight;
  1257. }
  1258. if (out_grammarHasChanged) *out_grammarHasChanged = changed;
  1259. } else if (updateRule == kOTGrammar_rerankingStrategy::SYMMETRIC_ALL_SKIPPABLE) {
  1260. bool changed = false;
  1261. integer winningConstraints = 0, adultConstraints = 0;
  1262. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1263. const integer winnerMarks = winner -> marks [icons];
  1264. const integer adultMarks = adult -> marks [icons];
  1265. if (adultMarks > winnerMarks)
  1266. adultConstraints ++;
  1267. if (winnerMarks > adultMarks)
  1268. winningConstraints ++;
  1269. }
  1270. if (winningConstraints != 0) for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1271. const OTGrammarConstraint constraint = & my constraints [icons];
  1272. double constraintStep = step * constraint -> plasticity;
  1273. const integer winnerMarks = winner -> marks [icons];
  1274. const integer adultMarks = adult -> marks [icons];
  1275. if (adultMarks > winnerMarks) {
  1276. if (multiplyStepByNumberOfViolations)
  1277. constraintStep *= adultMarks - winnerMarks;
  1278. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak);
  1279. changed = true;
  1280. }
  1281. if (winnerMarks > adultMarks) {
  1282. if (multiplyStepByNumberOfViolations)
  1283. constraintStep *= winnerMarks - adultMarks;
  1284. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak);
  1285. changed = true;
  1286. }
  1287. }
  1288. if (changed && my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG) {
  1289. longdouble sumOfWeights = 0.0;
  1290. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  1291. sumOfWeights += my constraints [icons]. ranking;
  1292. const double averageWeight = (double) sumOfWeights / my numberOfConstraints;
  1293. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  1294. my constraints [icons]. ranking -= averageWeight;
  1295. }
  1296. if (out_grammarHasChanged) *out_grammarHasChanged = changed;
  1297. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_UNCANCELLED) {
  1298. integer winningConstraints = 0, adultConstraints = 0;
  1299. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1300. const integer winnerMarks = winner -> marks [icons];
  1301. const integer adultMarks = adult -> marks [icons];
  1302. if (adultMarks > winnerMarks)
  1303. adultConstraints ++;
  1304. if (winnerMarks > adultMarks)
  1305. winningConstraints ++;
  1306. }
  1307. if (winningConstraints != 0) for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1308. const OTGrammarConstraint constraint = & my constraints [icons];
  1309. double constraintStep = step * constraint -> plasticity;
  1310. const integer winnerMarks = winner -> marks [icons];
  1311. const integer adultMarks = adult -> marks [icons];
  1312. if (adultMarks > winnerMarks) {
  1313. if (multiplyStepByNumberOfViolations)
  1314. constraintStep *= adultMarks - winnerMarks;
  1315. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak) / adultConstraints;
  1316. //constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak) * winningConstraints;
  1317. if (out_grammarHasChanged)
  1318. *out_grammarHasChanged = true;
  1319. }
  1320. if (winnerMarks > adultMarks) {
  1321. if (multiplyStepByNumberOfViolations)
  1322. constraintStep *= winnerMarks - adultMarks;
  1323. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) / winningConstraints;
  1324. //constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) * adultConstraints;
  1325. if (out_grammarHasChanged)
  1326. *out_grammarHasChanged = true;
  1327. }
  1328. }
  1329. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL) {
  1330. integer winningConstraints = 0, adultConstraints = 0;
  1331. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1332. const integer winnerMarks = winner -> marks [icons];
  1333. const integer adultMarks = adult -> marks [icons];
  1334. if (adultMarks > 0)
  1335. adultConstraints ++;
  1336. if (winnerMarks > 0)
  1337. winningConstraints ++;
  1338. }
  1339. if (winningConstraints != 0) for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1340. const OTGrammarConstraint constraint = & my constraints [icons];
  1341. double constraintStep = step * constraint -> plasticity;
  1342. const integer winnerMarks = winner -> marks [icons];
  1343. const integer adultMarks = adult -> marks [icons];
  1344. if (adultMarks > 0) {
  1345. if (multiplyStepByNumberOfViolations)
  1346. constraintStep *= adultMarks /*- winnerMarks*/;
  1347. constraint -> ranking -= constraintStep * (1.0 + constraint -> ranking * my leak) / adultConstraints;
  1348. if (out_grammarHasChanged)
  1349. *out_grammarHasChanged = true;
  1350. }
  1351. if (winnerMarks > 0) {
  1352. if (multiplyStepByNumberOfViolations)
  1353. constraintStep *= winnerMarks /*- adultMarks*/;
  1354. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) / winningConstraints;
  1355. if (out_grammarHasChanged)
  1356. *out_grammarHasChanged = true;
  1357. }
  1358. }
  1359. } else if (updateRule == kOTGrammar_rerankingStrategy::EDCD || updateRule == kOTGrammar_rerankingStrategy::EDCD_WITH_VACATION) {
  1360. /*
  1361. Determine the crucial winner mark.
  1362. */
  1363. double pivotRanking;
  1364. bool equivalent = true;
  1365. integer icons = 1;
  1366. for (; icons <= my numberOfConstraints; icons ++) {
  1367. const integer winnerMarks = winner -> marks [my index [icons]]; // the order is important, therefore indirect
  1368. const integer adultMarks = adult -> marks [my index [icons]];
  1369. if (adultMarks < winnerMarks)
  1370. break;
  1371. if (adultMarks > winnerMarks)
  1372. equivalent = false;
  1373. }
  1374. if (icons > my numberOfConstraints) { // completed the loop?
  1375. if (warnIfStalled && ! equivalent)
  1376. Melder_warning (U"Correct output is harmonically bounded (by having strict superset violations as compared to the learner's output)! EDCD stalls.\n"
  1377. U"Input: ", tableau -> input.get(), U"\nCorrect output: ", adult -> output.get(), U"\nLearner's output: ", winner -> output.get());
  1378. return; // Tesar & Smolensky (2000: 67): "stopped dead in its tracks"
  1379. }
  1380. /*
  1381. Determine the stratum into which some constraints will be demoted.
  1382. */
  1383. pivotRanking = my constraints [my index [icons]]. ranking;
  1384. if (updateRule == kOTGrammar_rerankingStrategy::EDCD_WITH_VACATION) {
  1385. integer numberOfConstraintsToDemote = 0;
  1386. for (icons = 1; icons <= my numberOfConstraints; icons ++) {
  1387. const integer winnerMarks = winner -> marks [icons];
  1388. const integer adultMarks = adult -> marks [icons];
  1389. if (adultMarks > winnerMarks) {
  1390. const OTGrammarConstraint constraint = & my constraints [icons];
  1391. if (constraint -> ranking >= pivotRanking)
  1392. numberOfConstraintsToDemote += 1;
  1393. }
  1394. }
  1395. if (numberOfConstraintsToDemote > 0) {
  1396. for (icons = 1; icons <= my numberOfConstraints; icons ++) {
  1397. const OTGrammarConstraint constraint = & my constraints [icons];
  1398. if (constraint -> ranking < pivotRanking) {
  1399. constraint -> ranking -= numberOfConstraintsToDemote * step * constraint -> plasticity;
  1400. if (out_grammarHasChanged)
  1401. *out_grammarHasChanged = true;
  1402. }
  1403. }
  1404. }
  1405. }
  1406. /*
  1407. Demote all the uniquely violated constraints in the adult form
  1408. that have rankings not lower than the pivot.
  1409. */
  1410. for (icons = 1; icons <= my numberOfConstraints; icons ++) {
  1411. integer numberOfConstraintsDemoted = 0;
  1412. const integer winnerMarks = winner -> marks [my index [icons]]; // for the vacation version, the order is important, therefore indirect
  1413. const integer adultMarks = adult -> marks [my index [icons]];
  1414. if (adultMarks > winnerMarks) {
  1415. const OTGrammarConstraint constraint = & my constraints [my index [icons]];
  1416. const double constraintStep = step * constraint -> plasticity;
  1417. if (constraint -> ranking >= pivotRanking) {
  1418. numberOfConstraintsDemoted += 1;
  1419. constraint -> ranking = pivotRanking - numberOfConstraintsDemoted * constraintStep; // this preserves the order of the demotees
  1420. if (out_grammarHasChanged)
  1421. *out_grammarHasChanged = true;
  1422. }
  1423. }
  1424. }
  1425. } else if (updateRule == kOTGrammar_rerankingStrategy::DEMOTION_ONLY) {
  1426. /*
  1427. Determine the crucial adult mark.
  1428. */
  1429. integer crucialAdultMark;
  1430. OTGrammarConstraint offendingConstraint;
  1431. integer icons = 1;
  1432. for (; icons <= my numberOfConstraints; icons ++) {
  1433. const integer winnerMarks = winner -> marks [my index [icons]]; // the order is important, so we indirect
  1434. const integer adultMarks = adult -> marks [my index [icons]];
  1435. if (my constraints [my index [icons]]. tiedToTheRight)
  1436. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  1437. if (adultMarks < winnerMarks)
  1438. Melder_throw (U"Demotion-only learning step: Adult form wins! Should never happen.");
  1439. if (adultMarks > winnerMarks)
  1440. break;
  1441. }
  1442. if (icons > my numberOfConstraints) // completed the loop?
  1443. Melder_throw (U"Adult form equals correct candidate.");
  1444. crucialAdultMark = icons;
  1445. /*
  1446. Demote the highest uniquely violated constraint in the adult form.
  1447. */
  1448. offendingConstraint = & my constraints [my index [crucialAdultMark]];
  1449. double constraintStep = step * offendingConstraint -> plasticity;
  1450. offendingConstraint -> ranking -= constraintStep;
  1451. if (out_grammarHasChanged)
  1452. *out_grammarHasChanged = true;
  1453. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGHEST_DOWN) {
  1454. integer numberOfUp = 0;
  1455. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1456. const integer winnerMarks = winner -> marks [icons];
  1457. const integer adultMarks = adult -> marks [icons];
  1458. if (winnerMarks > adultMarks)
  1459. numberOfUp ++;
  1460. }
  1461. if (numberOfUp > 0) {
  1462. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1463. const OTGrammarConstraint constraint = & my constraints [icons];
  1464. double constraintStep = step * constraint -> plasticity;
  1465. const integer winnerMarks = winner -> marks [icons];
  1466. const integer adultMarks = adult -> marks [icons];
  1467. if (winnerMarks > adultMarks) {
  1468. if (multiplyStepByNumberOfViolations)
  1469. constraintStep *= winnerMarks - adultMarks;
  1470. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) / numberOfUp;
  1471. if (out_grammarHasChanged)
  1472. *out_grammarHasChanged = true;
  1473. }
  1474. }
  1475. integer winnerMarks = 0, adultMarks = 0;
  1476. integer icons = 1;
  1477. for (; icons <= my numberOfConstraints; icons ++) {
  1478. winnerMarks = winner -> marks [my index [icons]]; // the order is important, therefore indirect
  1479. adultMarks = adult -> marks [my index [icons]];
  1480. if (my constraints [my index [icons]]. tiedToTheRight)
  1481. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  1482. if (adultMarks < winnerMarks)
  1483. Melder_throw (U"Demotion-only learning step: Adult form wins! Should never happen.");
  1484. if (adultMarks > winnerMarks) break;
  1485. }
  1486. if (icons > my numberOfConstraints) // completed the loop?
  1487. Melder_throw (U"Adult form equals correct candidate.");
  1488. const integer crucialAdultMark = icons;
  1489. /*
  1490. Demote the highest uniquely violated constraint in the adult form.
  1491. */
  1492. const OTGrammarConstraint offendingConstraint = & my constraints [my index [crucialAdultMark]];
  1493. double constraintStep = step * offendingConstraint -> plasticity;
  1494. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - adultMarks;
  1495. offendingConstraint -> ranking -= /*numberOfUp **/ constraintStep * (1.0 - offendingConstraint -> ranking * my leak);
  1496. if (out_grammarHasChanged) *out_grammarHasChanged = true;
  1497. }
  1498. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGHEST_DOWN_2012) {
  1499. integer numberOfUp = 0;
  1500. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1501. const integer winnerMarks = winner -> marks [icons];
  1502. const integer adultMarks = adult -> marks [icons];
  1503. if (winnerMarks > adultMarks)
  1504. numberOfUp ++;
  1505. }
  1506. if (numberOfUp > 0) {
  1507. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1508. const OTGrammarConstraint constraint = & my constraints [icons];
  1509. double constraintStep = step * constraint -> plasticity;
  1510. const integer winnerMarks = winner -> marks [icons];
  1511. const integer adultMarks = adult -> marks [icons];
  1512. if (winnerMarks > adultMarks) {
  1513. if (multiplyStepByNumberOfViolations)
  1514. constraintStep *= winnerMarks - adultMarks;
  1515. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) / (numberOfUp + 1);
  1516. if (out_grammarHasChanged)
  1517. *out_grammarHasChanged = true;
  1518. }
  1519. }
  1520. integer winnerMarks = 0, adultMarks = 0;
  1521. integer icons = 1;
  1522. for (; icons <= my numberOfConstraints; icons ++) {
  1523. winnerMarks = winner -> marks [my index [icons]]; // the order is important, therefore indirect
  1524. adultMarks = adult -> marks [my index [icons]];
  1525. if (my constraints [my index [icons]]. tiedToTheRight)
  1526. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  1527. if (adultMarks < winnerMarks)
  1528. Melder_throw (U"Demotion-only learning step: Adult form wins! Should never happen.");
  1529. if (adultMarks > winnerMarks) break;
  1530. }
  1531. if (icons > my numberOfConstraints) // completed the loop?
  1532. Melder_throw (U"Adult form equals correct candidate.");
  1533. const integer crucialAdultMark = icons;
  1534. /*
  1535. Demote the highest uniquely violated constraint in the adult form.
  1536. */
  1537. const OTGrammarConstraint offendingConstraint = & my constraints [my index [crucialAdultMark]];
  1538. double constraintStep = step * offendingConstraint -> plasticity;
  1539. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - adultMarks;
  1540. offendingConstraint -> ranking -= /*numberOfUp **/ constraintStep * (1.0 - offendingConstraint -> ranking * my leak);
  1541. if (out_grammarHasChanged) *out_grammarHasChanged = true;
  1542. }
  1543. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGH_DOWN) {
  1544. integer numberOfDown = 0, numberOfUp = 0, lowestDemotableConstraint = 0;
  1545. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1546. const integer winnerMarks = winner -> marks [my index [icons]]; // the order is important, therefore indirect
  1547. const integer adultMarks = adult -> marks [my index [icons]];
  1548. if (adultMarks < winnerMarks) {
  1549. numberOfUp ++;
  1550. } else if (adultMarks > winnerMarks) {
  1551. if (numberOfUp == 0) {
  1552. numberOfDown ++;
  1553. lowestDemotableConstraint = icons;
  1554. }
  1555. }
  1556. }
  1557. if (warnIfStalled && numberOfDown == 0) {
  1558. Melder_warning (U"Correct output is harmonically bounded (by having strict superset violations as compared to the learner's output)! EDCD stalls.\n"
  1559. U"Input: ", tableau -> input.get(), U"\nCorrect output: ", adult -> output.get(), U"\nLearner's output: ", winner -> output.get());
  1560. return;
  1561. }
  1562. if (numberOfUp > 0) {
  1563. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1564. const integer constraintIndex = my index [icons];
  1565. if (my constraints [constraintIndex]. tiedToTheRight)
  1566. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  1567. const OTGrammarConstraint constraint = & my constraints [constraintIndex];
  1568. double constraintStep = step * constraint -> plasticity;
  1569. const integer winnerMarks = winner -> marks [constraintIndex]; // the order is important, therefore indirect
  1570. const integer adultMarks = adult -> marks [constraintIndex];
  1571. if (adultMarks < winnerMarks) {
  1572. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - adultMarks;
  1573. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) * numberOfDown / (numberOfUp + 0.0);
  1574. } else if (adultMarks > winnerMarks) {
  1575. if (icons <= lowestDemotableConstraint) {
  1576. if (multiplyStepByNumberOfViolations) constraintStep *= adultMarks - winnerMarks;
  1577. constraint -> ranking -= constraintStep * (1.0 - constraint -> ranking * my leak);
  1578. }
  1579. }
  1580. }
  1581. if (out_grammarHasChanged) *out_grammarHasChanged = true;
  1582. }
  1583. } else if (updateRule == kOTGrammar_rerankingStrategy::WEIGHTED_ALL_UP_HIGH_DOWN_2012) {
  1584. integer numberOfDown = 0, numberOfUp = 0, lowestDemotableConstraint = 0;
  1585. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1586. const integer winnerMarks = winner -> marks [my index [icons]]; // the order is important, therefore indirect
  1587. const integer adultMarks = adult -> marks [my index [icons]];
  1588. if (adultMarks < winnerMarks) {
  1589. numberOfUp ++;
  1590. } else if (adultMarks > winnerMarks) {
  1591. if (numberOfUp == 0) {
  1592. numberOfDown ++;
  1593. lowestDemotableConstraint = icons;
  1594. }
  1595. }
  1596. }
  1597. if (warnIfStalled && numberOfDown == 0) {
  1598. Melder_warning (U"Correct output is harmonically bounded (by having strict superset violations as compared to the learner's output)! EDCD stalls.\n"
  1599. U"Input: ", tableau -> input.get(), U"\nCorrect output: ", adult -> output.get(), U"\nLearner's output: ", winner -> output.get());
  1600. return;
  1601. }
  1602. if (numberOfUp > 0) {
  1603. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1604. const integer constraintIndex = my index [icons];
  1605. if (my constraints [constraintIndex]. tiedToTheRight)
  1606. Melder_throw (U"Demotion-only learning cannot handle tied constraints.");
  1607. const OTGrammarConstraint constraint = & my constraints [constraintIndex];
  1608. double constraintStep = step * constraint -> plasticity;
  1609. const integer winnerMarks = winner -> marks [constraintIndex]; // the order is important, therefore indirect
  1610. const integer adultMarks = adult -> marks [constraintIndex];
  1611. if (adultMarks < winnerMarks) {
  1612. if (multiplyStepByNumberOfViolations) constraintStep *= winnerMarks - adultMarks;
  1613. constraint -> ranking += constraintStep * (1.0 - constraint -> ranking * my leak) * numberOfDown / (numberOfUp + 1.0);
  1614. } else if (adultMarks > winnerMarks) {
  1615. if (icons <= lowestDemotableConstraint) {
  1616. if (multiplyStepByNumberOfViolations) constraintStep *= adultMarks - winnerMarks;
  1617. constraint -> ranking -= constraintStep * (1.0 - constraint -> ranking * my leak);
  1618. }
  1619. }
  1620. }
  1621. if (out_grammarHasChanged)
  1622. *out_grammarHasChanged = true;
  1623. }
  1624. }
  1625. if (honourLocalRankings && my numberOfFixedRankings)
  1626. OTGrammar_honourLocalRankings (me, plasticity, relativePlasticityNoise, out_grammarHasChanged);
  1627. } catch (MelderError) {
  1628. Melder_throw (me, U": rankings not modified.");
  1629. }
  1630. }
  1631. void OTGrammar_learnOne (OTGrammar me, conststring32 input, conststring32 adultOutput,
  1632. double evaluationNoise, enum kOTGrammar_rerankingStrategy updateRule, bool honourLocalRankings,
  1633. double plasticity, double relativePlasticityNoise, bool newDisharmonies, bool warnIfStalled, bool *out_grammarHasChanged)
  1634. {
  1635. try {
  1636. if (newDisharmonies) OTGrammar_newDisharmonies (me, evaluationNoise);
  1637. if (out_grammarHasChanged) *out_grammarHasChanged = false;
  1638. /*
  1639. Evaluate the input in the learner's hypothesis.
  1640. */
  1641. integer itab = OTGrammar_getTableau (me, input);
  1642. OTGrammarTableau tableau = & my tableaus [itab];
  1643. /*
  1644. Determine the "winner", i.e. the candidate that wins in the learner's grammar
  1645. (Tesar & Smolensky call this the "loser").
  1646. */
  1647. integer iwinner = OTGrammar_getWinner (me, itab);
  1648. OTGrammarCandidate winner = & tableau -> candidates [iwinner];
  1649. /*
  1650. Error-driven: compare the adult winner (the correct candidate) and the learner's winner.
  1651. */
  1652. if (str32equ (winner -> output.get(), adultOutput)) return; // as far as we know, the grammar is already correct: don't update rankings
  1653. /*
  1654. Find (perhaps the learner's interpretation of) the adult output in the learner's own tableau
  1655. (Tesar & Smolensky call this the "winner").
  1656. */
  1657. integer iadult = 1;
  1658. for (; iadult <= tableau -> numberOfCandidates; iadult ++) {
  1659. OTGrammarCandidate cand = & tableau -> candidates [iadult];
  1660. if (str32equ (cand -> output.get(), adultOutput)) break;
  1661. }
  1662. if (iadult > tableau -> numberOfCandidates)
  1663. Melder_throw (U"Cannot generate adult output \"", adultOutput, U"\".");
  1664. /*
  1665. Now we know that the current hypothesis prefers the (wrong) learner's winner over the (correct) adult output.
  1666. The grammar will have to change.
  1667. */
  1668. OTGrammar_modifyRankings (me, itab, iwinner, iadult, updateRule, honourLocalRankings,
  1669. plasticity, relativePlasticityNoise, warnIfStalled, out_grammarHasChanged);
  1670. } catch (MelderError) {
  1671. Melder_throw (me, U": not learned from input \"", input, U"\" and adult output \"", adultOutput, U"\".");
  1672. }
  1673. }
  1674. void OTGrammar_learn (OTGrammar me, Strings inputs, Strings outputs,
  1675. double evaluationNoise, enum kOTGrammar_rerankingStrategy updateRule, bool honourLocalRankings,
  1676. double plasticity, double relativePlasticityNoise, integer numberOfChews)
  1677. {
  1678. if (! inputs) inputs = outputs;
  1679. try {
  1680. integer n = inputs -> numberOfStrings;
  1681. if (outputs -> numberOfStrings != n)
  1682. Melder_throw (U"Numbers of strings in input and output are not equal.");
  1683. for (integer i = 1; i <= n; i ++) {
  1684. for (integer ichew = 1; ichew <= numberOfChews; ichew ++) {
  1685. OTGrammar_learnOne (me, inputs -> strings [i].get(), outputs -> strings [i].get(),
  1686. evaluationNoise, updateRule, honourLocalRankings,
  1687. plasticity, relativePlasticityNoise, true, true, nullptr);
  1688. }
  1689. }
  1690. } catch (MelderError) {
  1691. Melder_throw (me, U": not learned from ", inputs, U" (inputs) and ", outputs, U" (outputs).");
  1692. }
  1693. }
  1694. void OTGrammar_PairDistribution_learn (OTGrammar me, PairDistribution thee,
  1695. double evaluationNoise, enum kOTGrammar_rerankingStrategy updateRule, bool honourLocalRankings,
  1696. double initialPlasticity, integer replicationsPerPlasticity, double plasticityDecrement,
  1697. integer numberOfPlasticities, double relativePlasticityNoise, integer numberOfChews)
  1698. {
  1699. integer idatum = 0, numberOfData = numberOfPlasticities * replicationsPerPlasticity;
  1700. try {
  1701. double plasticity = initialPlasticity;
  1702. autoMelderMonitor monitor (U"Learning with full knowledge...");
  1703. if (monitor.graphics()) {
  1704. Graphics_clearWs (monitor.graphics());
  1705. }
  1706. for (integer iplasticity = 1; iplasticity <= numberOfPlasticities; iplasticity ++) {
  1707. for (integer ireplication = 1; ireplication <= replicationsPerPlasticity; ireplication ++) {
  1708. conststring32 input, output;
  1709. PairDistribution_peekPair (thee, & input, & output);
  1710. ++ idatum;
  1711. if (monitor.graphics() && idatum % (numberOfData / 400 + 1) == 0) {
  1712. Graphics_beginMovieFrame (monitor.graphics(), nullptr);
  1713. Graphics_setWindow (monitor.graphics(), 0, numberOfData, 50, 150);
  1714. for (integer icons = 1; icons <= 14 && icons <= my numberOfConstraints; icons ++) {
  1715. Graphics_setGrey (monitor.graphics(), (double) icons / 14);
  1716. Graphics_line (monitor.graphics(), idatum, my constraints [icons]. ranking,
  1717. idatum, my constraints [icons]. ranking+1);
  1718. }
  1719. Graphics_endMovieFrame (monitor.graphics(), 0.0);
  1720. }
  1721. Melder_monitor ((double) idatum / numberOfData,
  1722. U"Processing input-output pair ", idatum,
  1723. U" out of ", numberOfData, U": ", input, U" -> ", output);
  1724. for (integer ichew = 1; ichew <= numberOfChews; ichew ++) {
  1725. OTGrammar_learnOne (me, input, output,
  1726. evaluationNoise, updateRule, honourLocalRankings,
  1727. plasticity, relativePlasticityNoise, true, true, nullptr);
  1728. }
  1729. }
  1730. plasticity *= plasticityDecrement;
  1731. }
  1732. } catch (MelderError) {
  1733. if (idatum > 1)
  1734. Melder_appendError (U"Only ", idatum - 1, U" input-output pairs out of ", numberOfData, U" were processed.");
  1735. Melder_throw (me, U": did not complete learning from ", thee, U".");
  1736. }
  1737. }
  1738. static integer PairDistribution_getNumberOfAttestedOutputs (PairDistribution me, conststring32 input, conststring32 *out_attestedOutput) {
  1739. integer result = 0;
  1740. for (integer ipair = 1; ipair <= my pairs.size; ipair ++) {
  1741. PairProbability pair = my pairs.at [ipair];
  1742. if (str32equ (pair -> string1.get(), input) && pair -> weight > 0.0) {
  1743. if (out_attestedOutput) *out_attestedOutput = pair -> string2.get();
  1744. result ++;
  1745. }
  1746. }
  1747. return result;
  1748. }
  1749. bool OTGrammar_PairDistribution_findPositiveWeights (OTGrammar me, PairDistribution thee, double weightFloor, double marginOfSeparation) {
  1750. NUMlinprog linprog = nullptr;
  1751. try {
  1752. bool result = false;
  1753. if (my decisionStrategy != kOTGrammar_decisionStrategy::HARMONIC_GRAMMAR &&
  1754. my decisionStrategy != kOTGrammar_decisionStrategy::LINEAR_OT &&
  1755. my decisionStrategy != kOTGrammar_decisionStrategy::POSITIVE_HG &&
  1756. my decisionStrategy != kOTGrammar_decisionStrategy::EXPONENTIAL_HG)
  1757. {
  1758. Melder_throw (U"To find positive weights, the decision strategy has to be HarmonicGrammar, LinearOT, PositiveHG, or ExponentialHG.");
  1759. }
  1760. autoINTVEC optimalCandidates = INTVECraw (my numberOfTableaus);
  1761. /*
  1762. Check that there is exactly one optimal output for each input.
  1763. */
  1764. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  1765. OTGrammarTableau tab = & my tableaus [itab];
  1766. conststring32 attestedOutput = nullptr;
  1767. integer numberOfAttestedOutputs = PairDistribution_getNumberOfAttestedOutputs (thee, tab -> input.get(), & attestedOutput);
  1768. if (numberOfAttestedOutputs == 0) {
  1769. Melder_throw (U"Input \"", tab -> input.get(), U"\" has no attested output.");
  1770. } else if (numberOfAttestedOutputs > 1) {
  1771. Melder_throw (U"Input \"", tab -> input.get(), U"\" has more than one attested output.");
  1772. } else {
  1773. Melder_assert (attestedOutput);
  1774. for (integer icand = 1; icand <= tab -> numberOfCandidates; icand ++) {
  1775. OTGrammarCandidate cand = & tab -> candidates [icand];
  1776. if (str32equ (attestedOutput, cand -> output.get()))
  1777. optimalCandidates [itab] = icand;
  1778. }
  1779. }
  1780. Melder_assert (optimalCandidates [itab] != 0);
  1781. }
  1782. /*
  1783. Create linear programming problem.
  1784. */
  1785. linprog = NUMlinprog_new (false);
  1786. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  1787. NUMlinprog_addVariable (linprog, weightFloor, undefined, 1.0);
  1788. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  1789. OTGrammarTableau tab = & my tableaus [itab];
  1790. integer ioptimalCandidate = optimalCandidates [itab];
  1791. Melder_assert (ioptimalCandidate >= 1);
  1792. Melder_assert (ioptimalCandidate <= tab -> numberOfCandidates);
  1793. OTGrammarCandidate optimalCandidate = & tab -> candidates [ioptimalCandidate];
  1794. for (integer icand = 1; icand <= tab -> numberOfCandidates; icand ++) if (icand != ioptimalCandidate) {
  1795. OTGrammarCandidate cand = & tab -> candidates [icand];
  1796. NUMlinprog_addConstraint (linprog, marginOfSeparation, undefined);
  1797. for (integer icons = 1; icons <= my numberOfConstraints; icons ++)
  1798. NUMlinprog_addConstraintCoefficient (linprog, cand -> marks [icons] - optimalCandidate -> marks [icons]);
  1799. }
  1800. }
  1801. NUMlinprog_run (linprog);
  1802. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1803. double weighting = NUMlinprog_getPrimalValue (linprog, icons);
  1804. Melder_assert (weighting >= weightFloor);
  1805. my constraints [icons]. ranking = my constraints [icons]. disharmony =
  1806. my decisionStrategy == kOTGrammar_decisionStrategy::EXPONENTIAL_HG ? log (weighting) : weighting;
  1807. }
  1808. NUMlinprog_delete (linprog);
  1809. return result;
  1810. } catch (MelderError) {
  1811. NUMlinprog_delete (linprog);
  1812. Melder_throw (me, U": positive weights not found.");
  1813. }
  1814. }
  1815. void OTGrammar_reset (OTGrammar me, double ranking) {
  1816. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1817. OTGrammarConstraint constraint = & my constraints [icons];
  1818. constraint -> disharmony = constraint -> ranking = ranking;
  1819. }
  1820. OTGrammar_sort (me);
  1821. }
  1822. void OTGrammar_resetToRandomRanking (OTGrammar me, double mean, double standardDeviation) {
  1823. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1824. OTGrammarConstraint constraint = & my constraints [my index [icons]];
  1825. constraint -> disharmony = constraint -> ranking = NUMrandomGauss (mean, standardDeviation);
  1826. }
  1827. OTGrammar_sort (me);
  1828. }
  1829. void OTGrammar_resetToRandomTotalRanking (OTGrammar me, double maximumRanking, double rankingDistance) {
  1830. /*
  1831. First put the constraints in a random order and build a random index.
  1832. */
  1833. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1834. OTGrammarConstraint constraint = & my constraints [icons];
  1835. constraint -> ranking = 0.0;
  1836. }
  1837. OTGrammar_newDisharmonies (me, 1.0);
  1838. /*
  1839. Then use the random index to yield a cascade of rankings.
  1840. */
  1841. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1842. OTGrammarConstraint constraint = & my constraints [my index [icons]];
  1843. constraint -> disharmony = constraint -> ranking = maximumRanking - (icons - 1) * rankingDistance;
  1844. }
  1845. OTGrammar_sort (me);
  1846. }
  1847. void OTGrammar_setRanking (OTGrammar me, integer constraint, double ranking, double disharmony) {
  1848. try {
  1849. Melder_require (constraint > 0 && constraint <= my numberOfConstraints,
  1850. U"There is no constraint with number ", constraint, U".");
  1851. my constraints [constraint]. ranking = ranking;
  1852. my constraints [constraint]. disharmony = disharmony;
  1853. OTGrammar_sort (me);
  1854. } catch (MelderError) {
  1855. Melder_throw (me, U": ranking of constraint ", constraint, U" not set.");
  1856. }
  1857. }
  1858. void OTGrammar_setConstraintPlasticity (OTGrammar me, integer constraint, double plasticity) {
  1859. try {
  1860. Melder_require (constraint > 0 && constraint <= my numberOfConstraints,
  1861. U"There is no constraint with number ", constraint, U".");
  1862. my constraints [constraint]. plasticity = plasticity;
  1863. } catch (MelderError) {
  1864. Melder_throw (me, U": plasticity of constraint ", constraint, U" not set.");
  1865. }
  1866. }
  1867. integer theSaveNumberOfConstraints, *theSaveIndex;
  1868. double *theSaveRankings, *theSaveDisharmonies;
  1869. bool *theSaveTiedToTheLeft, *theSaveTiedToTheRight;
  1870. static void OTGrammar_save (OTGrammar me) {
  1871. if (my numberOfConstraints != theSaveNumberOfConstraints) {
  1872. NUMvector_free (theSaveIndex, 1); theSaveIndex = nullptr;
  1873. NUMvector_free (theSaveRankings, 1); theSaveRankings = nullptr;
  1874. NUMvector_free (theSaveDisharmonies, 1); theSaveDisharmonies = nullptr;
  1875. NUMvector_free (theSaveTiedToTheLeft, 1); theSaveTiedToTheLeft = nullptr;
  1876. NUMvector_free (theSaveTiedToTheRight, 1); theSaveTiedToTheRight = nullptr;
  1877. theSaveNumberOfConstraints = my numberOfConstraints;
  1878. }
  1879. if (! theSaveIndex) theSaveIndex = NUMvector <integer> (1, my numberOfConstraints);
  1880. if (! theSaveRankings) theSaveRankings = NUMvector <double> (1, my numberOfConstraints);
  1881. if (! theSaveDisharmonies) theSaveDisharmonies = NUMvector <double> (1, my numberOfConstraints);
  1882. if (! theSaveTiedToTheLeft) theSaveTiedToTheLeft = NUMvector <bool> (1, my numberOfConstraints);
  1883. if (! theSaveTiedToTheRight) theSaveTiedToTheRight = NUMvector <bool> (1, my numberOfConstraints);
  1884. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1885. theSaveIndex [icons] = my index [icons];
  1886. theSaveRankings [icons] = my constraints [icons]. ranking;
  1887. theSaveDisharmonies [icons] = my constraints [icons]. disharmony;
  1888. theSaveTiedToTheLeft [icons] = my constraints [icons]. tiedToTheLeft;
  1889. theSaveTiedToTheRight [icons] = my constraints [icons]. tiedToTheRight;
  1890. }
  1891. }
  1892. static void OTGrammar_restore (OTGrammar me) {
  1893. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  1894. my index [icons] = theSaveIndex [icons];
  1895. my constraints [icons]. ranking = theSaveRankings [icons];
  1896. my constraints [icons]. disharmony = theSaveDisharmonies [icons];
  1897. my constraints [icons]. tiedToTheLeft = theSaveTiedToTheLeft [icons];
  1898. my constraints [icons]. tiedToTheRight = theSaveTiedToTheRight [icons];
  1899. }
  1900. }
  1901. void OTGrammar_learnOneFromPartialOutput (OTGrammar me, conststring32 partialAdultOutput,
  1902. double evaluationNoise, enum kOTGrammar_rerankingStrategy updateRule, bool honourLocalRankings,
  1903. double plasticity, double relativePlasticityNoise, integer numberOfChews, bool warnIfStalled)
  1904. {
  1905. try {
  1906. OTGrammar_newDisharmonies (me, evaluationNoise);
  1907. if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy::EDCD)
  1908. OTGrammar_save (me);
  1909. integer ichew = 1;
  1910. for (; ichew <= numberOfChews; ichew ++) {
  1911. integer assumedAdultInputTableau, assumedAdultCandidate;
  1912. OTGrammar_getInterpretiveParse (me, partialAdultOutput, & assumedAdultInputTableau, & assumedAdultCandidate);
  1913. bool grammarHasChanged = false;
  1914. OTGrammar_learnOne (me,
  1915. my tableaus [assumedAdultInputTableau]. input.get(),
  1916. my tableaus [assumedAdultInputTableau]. candidates [assumedAdultCandidate]. output.get(),
  1917. evaluationNoise, updateRule, honourLocalRankings,
  1918. plasticity, relativePlasticityNoise, Melder_debug == 47, warnIfStalled, & grammarHasChanged
  1919. );
  1920. if (! grammarHasChanged)
  1921. return;
  1922. }
  1923. if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy::EDCD && ichew > numberOfChews) {
  1924. /*
  1925. Is the partial output form grammatical by now?
  1926. */
  1927. integer assumedAdultInputTableau, assumedAdultCandidate;
  1928. OTGrammar_getInterpretiveParse (me, partialAdultOutput, & assumedAdultInputTableau, & assumedAdultCandidate);
  1929. OTGrammarCandidate learnerCandidate = & my tableaus [assumedAdultInputTableau]. candidates [OTGrammar_getWinner (me, assumedAdultInputTableau)];
  1930. if (! str32equ (learnerCandidate -> output.get(),
  1931. my tableaus [assumedAdultInputTableau]. candidates [assumedAdultCandidate]. output.get()))
  1932. { /* Still ungrammatical? */
  1933. /*
  1934. Backtrack as in Tesar & Smolensky 2000:69.
  1935. */
  1936. OTGrammar_restore (me);
  1937. }
  1938. }
  1939. } catch (MelderError) {
  1940. Melder_throw (me, U": not learned from partial adult output \"", partialAdultOutput, U"\".");
  1941. }
  1942. }
  1943. static void OTGrammar_learnOneFromPartialOutput_opt (OTGrammar me, conststring32 partialAdultOutput, integer ipartialAdultOutput,
  1944. double evaluationNoise, enum kOTGrammar_rerankingStrategy updateRule, bool honourLocalRankings,
  1945. double plasticity, double relativePlasticityNoise, integer numberOfChews, bool warnIfStalled,
  1946. bool resampleForVirtualProduction, bool compareOnlyPartialOutput, integer resampleForCorrectForm)
  1947. {
  1948. try {
  1949. OTGrammar_newDisharmonies (me, evaluationNoise);
  1950. if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy::EDCD)
  1951. OTGrammar_save (me);
  1952. integer ichew = 1;
  1953. for (; ichew <= numberOfChews; ichew ++) {
  1954. integer assumedAdultInputTableau, assumedAdultCandidate;
  1955. OTGrammar_getInterpretiveParse_opt (me, ipartialAdultOutput, & assumedAdultInputTableau, & assumedAdultCandidate);
  1956. OTGrammarTableau tableau = & my tableaus [assumedAdultInputTableau];
  1957. OTGrammarCandidate assumedCorrect = & tableau -> candidates [assumedAdultCandidate];
  1958. /*
  1959. Determine the "winner", i.e. the candidate that wins in the learner's grammar
  1960. (Tesar & Smolensky call this the "loser").
  1961. */
  1962. if (resampleForVirtualProduction) OTGrammar_newDisharmonies (me, evaluationNoise);
  1963. integer iwinner = OTGrammar_getWinner (me, assumedAdultInputTableau);
  1964. OTGrammarCandidate winner = & tableau -> candidates [iwinner];
  1965. /*
  1966. Error-driven: compare the adult winner (the correct candidate) and the learner's winner.
  1967. */
  1968. if (compareOnlyPartialOutput) {
  1969. if (str32str (winner -> output.get(), partialAdultOutput)) return; // as far as we know, the grammar is already correct: don't update rankings
  1970. } else {
  1971. if (str32equ (winner -> output.get(), assumedCorrect -> output.get())) return; // as far as we know, the grammar is already correct: don't update rankings
  1972. }
  1973. if (resampleForCorrectForm) {
  1974. integer itry = 1;
  1975. for (; itry <= resampleForCorrectForm; itry ++) {
  1976. OTGrammar_newDisharmonies (me, evaluationNoise);
  1977. integer iwinner2 = OTGrammar_getWinner (me, assumedAdultInputTableau);
  1978. OTGrammarCandidate winner2 = & tableau -> candidates [iwinner2];
  1979. if (compareOnlyPartialOutput) {
  1980. if (str32str (winner2 -> output.get(), partialAdultOutput)) { assumedAdultCandidate = iwinner2; break; }
  1981. } else {
  1982. if (str32equ (winner2 -> output.get(), assumedCorrect -> output.get())) { assumedAdultCandidate = iwinner2; break; }
  1983. }
  1984. }
  1985. if (itry > resampleForCorrectForm) return; // no match, so bail out
  1986. }
  1987. /*
  1988. Now we know that the current hypothesis prefers the (wrong) learner's winner over the (correct) adult output.
  1989. The grammar will have to change.
  1990. */
  1991. bool grammarHasChanged = false;
  1992. OTGrammar_modifyRankings (me, assumedAdultInputTableau, iwinner, assumedAdultCandidate, updateRule, honourLocalRankings,
  1993. plasticity, relativePlasticityNoise, warnIfStalled, & grammarHasChanged);
  1994. if (! grammarHasChanged)
  1995. return;
  1996. }
  1997. if (numberOfChews > 1 && updateRule == kOTGrammar_rerankingStrategy::EDCD && ichew > numberOfChews) {
  1998. /*
  1999. Is the partial output form grammatical by now?
  2000. */
  2001. integer assumedAdultInputTableau, assumedAdultCandidate;
  2002. OTGrammar_getInterpretiveParse_opt (me, ipartialAdultOutput, & assumedAdultInputTableau, & assumedAdultCandidate);
  2003. OTGrammarCandidate learnerCandidate = & my tableaus [assumedAdultInputTableau]. candidates [OTGrammar_getWinner (me, assumedAdultInputTableau)];
  2004. if (! str32equ (learnerCandidate -> output.get(),
  2005. my tableaus [assumedAdultInputTableau]. candidates [assumedAdultCandidate]. output.get()))
  2006. { /* Still ungrammatical? */
  2007. /*
  2008. Backtrack as in Tesar & Smolensky 2000:69.
  2009. */
  2010. OTGrammar_restore (me);
  2011. }
  2012. }
  2013. } catch (MelderError) {
  2014. Melder_throw (me, U": not learned from partial adult output ", partialAdultOutput, U".");
  2015. }
  2016. }
  2017. static autoOTHistory OTGrammar_createHistory (OTGrammar me, integer storeHistoryEvery, integer numberOfData) {
  2018. try {
  2019. integer numberOfSamplingPoints = numberOfData / storeHistoryEvery; // e.g. 0, 20, 40, ...
  2020. autoOTHistory thee = Thing_new (OTHistory);
  2021. TableOfReal_init (thee.get(), 2 + numberOfSamplingPoints * 2, 1 + my numberOfConstraints);
  2022. TableOfReal_setColumnLabel (thee.get(), 1, U"Datum");
  2023. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2024. TableOfReal_setColumnLabel (thee.get(), icons + 1, my constraints [icons]. name.get());
  2025. }
  2026. TableOfReal_setRowLabel (thee.get(), 1, U"Initial state");
  2027. thy data [1] [1] = 0.0;
  2028. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2029. thy data [1] [icons + 1] = my constraints [icons]. ranking;
  2030. }
  2031. return thee;
  2032. } catch (MelderError) {
  2033. Melder_throw (me, U": history not created.");
  2034. }
  2035. }
  2036. static void OTGrammar_updateHistory (OTGrammar me, OTHistory thee, integer storeHistoryEvery, integer datumNumber, conststring32 input) {
  2037. try {
  2038. if (datumNumber % storeHistoryEvery == 0) {
  2039. integer rowNumber = 2 * datumNumber / storeHistoryEvery;
  2040. TableOfReal_setRowLabel (thee, rowNumber, input);
  2041. thy data [rowNumber] [1] = datumNumber;
  2042. thy data [rowNumber + 1] [1] = datumNumber;
  2043. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2044. thy data [rowNumber] [icons + 1] = my constraints [icons]. disharmony;
  2045. thy data [rowNumber + 1] [icons + 1] = my constraints [icons]. ranking;
  2046. }
  2047. }
  2048. } catch (MelderError) {
  2049. Melder_throw (me, U": history not updated.");
  2050. }
  2051. }
  2052. static void OTGrammar_finalizeHistory (OTGrammar me, OTHistory thee, integer datumNumber) {
  2053. try {
  2054. TableOfReal_setRowLabel (thee, thy numberOfRows, U"Final state");
  2055. thy data [thy numberOfRows] [1] = datumNumber;
  2056. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2057. thy data [thy numberOfRows] [icons + 1] = my constraints [icons]. ranking;
  2058. }
  2059. } catch (MelderError) {
  2060. Melder_throw (me, U": history not finalized.");
  2061. }
  2062. }
  2063. void OTGrammar_learnFromPartialOutputs (OTGrammar me, Strings partialOutputs,
  2064. double evaluationNoise, enum kOTGrammar_rerankingStrategy updateRule, bool honourLocalRankings,
  2065. double plasticity, double relativePlasticityNoise, integer numberOfChews,
  2066. integer storeHistoryEvery, autoOTHistory *history_out)
  2067. {
  2068. try {
  2069. autoOTHistory history;
  2070. if (storeHistoryEvery) {
  2071. history = OTGrammar_createHistory (me, storeHistoryEvery, partialOutputs -> numberOfStrings);
  2072. }
  2073. try {
  2074. for (integer idatum = 1; idatum <= partialOutputs -> numberOfStrings; idatum ++) {
  2075. try {
  2076. OTGrammar_learnOneFromPartialOutput (me, partialOutputs -> strings [idatum].get(),
  2077. evaluationNoise, updateRule, honourLocalRankings,
  2078. plasticity, relativePlasticityNoise, numberOfChews, false);
  2079. } catch (MelderError) {
  2080. if (history) {
  2081. OTGrammar_updateHistory (me, history.get(), storeHistoryEvery, idatum, partialOutputs -> strings [idatum].get()); // so that we can inspect
  2082. }
  2083. throw;
  2084. }
  2085. if (history) {
  2086. OTGrammar_updateHistory (me, history.get(), storeHistoryEvery, idatum, partialOutputs -> strings [idatum].get());
  2087. }
  2088. }
  2089. if (history) {
  2090. OTGrammar_finalizeHistory (me, history.get(), partialOutputs -> numberOfStrings);
  2091. }
  2092. *history_out = history.move();
  2093. } catch (MelderError) {
  2094. *history_out = history.move(); // so that we can inspect
  2095. throw;
  2096. }
  2097. } catch (MelderError) {
  2098. Melder_throw (me, U": not learned from partial outputs ", partialOutputs, U".");
  2099. }
  2100. }
  2101. static void OTGrammar_opt_deleteOutputMatching (OTGrammar me) {
  2102. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  2103. OTGrammarTableau tab = & my tableaus [itab];
  2104. for (integer icand = 1; icand <= tab -> numberOfCandidates; icand ++) {
  2105. OTGrammarCandidate cand = & tab -> candidates [icand];
  2106. cand -> numberOfPotentialPartialOutputsMatching = 0;
  2107. NUMvector_free (cand -> partialOutputMatches, 1);
  2108. cand -> partialOutputMatches = nullptr;
  2109. }
  2110. }
  2111. }
  2112. static void OTGrammar_Distributions_opt_createOutputMatching (OTGrammar me, Distributions thee, integer columnNumber) {
  2113. try {
  2114. if (columnNumber > thy numberOfColumns)
  2115. Melder_throw (U"No column ", columnNumber, U" in Distributions.");
  2116. if (thy numberOfRows < 1)
  2117. Melder_throw (U"No candidates in Distributions.");
  2118. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  2119. OTGrammarTableau tab = & my tableaus [itab];
  2120. for (integer icand = 1; icand <= tab -> numberOfCandidates; icand ++) {
  2121. OTGrammarCandidate cand = & tab -> candidates [icand];
  2122. cand -> numberOfPotentialPartialOutputsMatching = thy numberOfRows;
  2123. cand -> partialOutputMatches = NUMvector <bool> (1, thy numberOfRows);
  2124. }
  2125. }
  2126. for (integer ipartialOutput = 1; ipartialOutput <= thy numberOfRows; ipartialOutput ++) {
  2127. if (thy data [ipartialOutput] [columnNumber] > 0.0) {
  2128. conststring32 partialOutput = thy rowLabels [ipartialOutput].get();
  2129. bool foundPartialOutput = false;
  2130. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  2131. OTGrammarTableau tab = & my tableaus [itab];
  2132. for (integer icand = 1; icand <= tab -> numberOfCandidates; icand ++) {
  2133. OTGrammarCandidate cand = & tab -> candidates [icand];
  2134. if (str32str (cand -> output.get(), partialOutput)) {
  2135. foundPartialOutput = true;
  2136. cand -> partialOutputMatches [ipartialOutput] = true;
  2137. }
  2138. }
  2139. }
  2140. if (! foundPartialOutput)
  2141. Melder_throw (U"The partial output \"", partialOutput, U"\" does not match any candidate for any input form.");
  2142. }
  2143. }
  2144. } catch (MelderError) {
  2145. OTGrammar_opt_deleteOutputMatching (me);
  2146. throw;
  2147. }
  2148. }
  2149. void OTGrammar_Distributions_learnFromPartialOutputs (OTGrammar me, Distributions thee, integer columnNumber,
  2150. double evaluationNoise, enum kOTGrammar_rerankingStrategy updateRule, bool honourLocalRankings,
  2151. double initialPlasticity, integer replicationsPerPlasticity, double plasticityDecrement,
  2152. integer numberOfPlasticities, double relativePlasticityNoise, integer numberOfChews,
  2153. integer storeHistoryEvery, autoOTHistory *history_out,
  2154. bool resampleForVirtualProduction, bool compareOnlyPartialOutput, integer resampleForCorrectForm)
  2155. {
  2156. integer idatum = 0;
  2157. const integer numberOfData = numberOfPlasticities * replicationsPerPlasticity;
  2158. try {
  2159. autoOTHistory history;
  2160. OTGrammar_Distributions_opt_createOutputMatching (me, thee, columnNumber);
  2161. autoMelderMonitor monitor (U"Learning with limited knowledge...");
  2162. if (monitor.graphics())
  2163. Graphics_clearWs (monitor.graphics());
  2164. if (storeHistoryEvery)
  2165. history = OTGrammar_createHistory (me, storeHistoryEvery, numberOfData);
  2166. try {
  2167. double plasticity = initialPlasticity;
  2168. for (integer iplasticity = 1; iplasticity <= numberOfPlasticities; iplasticity ++) {
  2169. for (integer ireplication = 1; ireplication <= replicationsPerPlasticity; ireplication ++) {
  2170. conststring32 partialOutput;
  2171. integer ipartialOutput;
  2172. Distributions_peek (thee, columnNumber, & partialOutput, & ipartialOutput);
  2173. ++ idatum;
  2174. if (monitor.graphics() && idatum % (numberOfData / 400 + 1) == 0) {
  2175. Graphics_beginMovieFrame (monitor.graphics(), nullptr);
  2176. Graphics_setWindow (monitor.graphics(), 0, numberOfData, 50, 150);
  2177. for (integer icons = 1; icons <= 14 && icons <= my numberOfConstraints; icons ++) {
  2178. Graphics_setGrey (monitor.graphics(), (double) icons / 14);
  2179. Graphics_line (monitor.graphics(), idatum, my constraints [icons]. ranking,
  2180. idatum, my constraints [icons]. ranking+1);
  2181. }
  2182. Graphics_endMovieFrame (monitor.graphics(), 0.0);
  2183. }
  2184. Melder_monitor ((double) idatum / numberOfData,
  2185. U"Processing partial output ", idatum, U" out of ", numberOfData, U": ",
  2186. thy rowLabels [ipartialOutput].get());
  2187. try {
  2188. OTGrammar_learnOneFromPartialOutput_opt (me, partialOutput, ipartialOutput,
  2189. evaluationNoise, updateRule, honourLocalRankings,
  2190. plasticity, relativePlasticityNoise, numberOfChews, false,
  2191. resampleForVirtualProduction, compareOnlyPartialOutput, resampleForCorrectForm); // no warning if stalled: RIP form is allowed to be harmonically bounded
  2192. } catch (MelderError) {
  2193. if (history)
  2194. OTGrammar_updateHistory (me, history.get(), storeHistoryEvery, idatum, thy rowLabels [ipartialOutput].get());
  2195. throw;
  2196. }
  2197. if (history)
  2198. OTGrammar_updateHistory (me, history.get(), storeHistoryEvery, idatum, thy rowLabels [ipartialOutput].get());
  2199. }
  2200. plasticity *= plasticityDecrement;
  2201. }
  2202. if (history) {
  2203. OTGrammar_finalizeHistory (me, history.get(), numberOfData);
  2204. }
  2205. OTGrammar_opt_deleteOutputMatching (me);
  2206. if (history_out)
  2207. *history_out = history.move();
  2208. } catch (MelderError) {
  2209. OTGrammar_opt_deleteOutputMatching (me);
  2210. if (history_out)
  2211. *history_out = history.move(); // so that we can inspect
  2212. throw;
  2213. }
  2214. } catch (MelderError) {
  2215. if (idatum > 1)
  2216. Melder_appendError (U"Only ", idatum - 1, U" input-output pairs out of ", numberOfData, U" were processed.");
  2217. Melder_throw (me, U" & ", thee, U": not learned from partial outputs.");
  2218. }
  2219. }
  2220. double OTGrammar_PairDistribution_getFractionCorrect (OTGrammar me, PairDistribution thee,
  2221. double evaluationNoise, integer numberOfInputs)
  2222. {
  2223. try {
  2224. integer numberOfCorrect = 0;
  2225. for (integer ireplication = 1; ireplication <= numberOfInputs; ireplication ++) {
  2226. conststring32 input, adultOutput;
  2227. PairDistribution_peekPair (thee, & input, & adultOutput);
  2228. OTGrammar_newDisharmonies (me, evaluationNoise);
  2229. integer inputTableau = OTGrammar_getTableau (me, input);
  2230. OTGrammarCandidate learnerCandidate = & my tableaus [inputTableau]. candidates [OTGrammar_getWinner (me, inputTableau)];
  2231. if (str32equ (learnerCandidate -> output.get(), adultOutput))
  2232. numberOfCorrect ++;
  2233. }
  2234. return (double) numberOfCorrect / numberOfInputs;
  2235. } catch (MelderError) {
  2236. Melder_throw (me, U" & ", thee, U": fraction correct not computed.");
  2237. }
  2238. }
  2239. integer OTGrammar_PairDistribution_getMinimumNumberCorrect (OTGrammar me, PairDistribution thee,
  2240. double evaluationNoise, integer numberOfReplications)
  2241. {
  2242. try {
  2243. integer minimumNumberCorrect = numberOfReplications;
  2244. for (integer ipair = 1; ipair <= thy pairs.size; ipair ++) {
  2245. PairProbability prob = thy pairs.at [ipair];
  2246. if (prob -> weight > 0.0) {
  2247. integer numberOfCorrect = 0;
  2248. conststring32 input = prob -> string1.get(), adultOutput = prob -> string2.get();
  2249. integer inputTableau = OTGrammar_getTableau (me, input);
  2250. for (integer ireplication = 1; ireplication <= numberOfReplications; ireplication ++) {
  2251. OTGrammar_newDisharmonies (me, evaluationNoise);
  2252. OTGrammarCandidate learnerCandidate = & my tableaus [inputTableau]. candidates [OTGrammar_getWinner (me, inputTableau)];
  2253. if (str32equ (learnerCandidate -> output.get(), adultOutput))
  2254. numberOfCorrect ++;
  2255. }
  2256. if (numberOfCorrect < minimumNumberCorrect)
  2257. minimumNumberCorrect = numberOfCorrect;
  2258. }
  2259. }
  2260. return minimumNumberCorrect;
  2261. } catch (MelderError) {
  2262. Melder_throw (me, U" & ", thee, U": minimum number correct not computed.");
  2263. }
  2264. }
  2265. double OTGrammar_Distributions_getFractionCorrect (OTGrammar me, Distributions thee, integer columnNumber,
  2266. double evaluationNoise, integer numberOfInputs)
  2267. {
  2268. try {
  2269. integer numberOfCorrect = 0;
  2270. OTGrammar_Distributions_opt_createOutputMatching (me, thee, columnNumber);
  2271. for (integer ireplication = 1; ireplication <= numberOfInputs; ireplication ++) {
  2272. integer ipartialOutput;
  2273. Distributions_peek (thee, columnNumber, nullptr, & ipartialOutput);
  2274. OTGrammar_newDisharmonies (me, evaluationNoise);
  2275. integer assumedAdultInputTableau, assumedAdultCandidate;
  2276. OTGrammar_getInterpretiveParse_opt (me, ipartialOutput, & assumedAdultInputTableau, & assumedAdultCandidate);
  2277. OTGrammarCandidate learnerCandidate = & my tableaus [assumedAdultInputTableau]. candidates [OTGrammar_getWinner (me, assumedAdultInputTableau)];
  2278. if (str32equ (learnerCandidate -> output.get(), my tableaus [assumedAdultInputTableau]. candidates [assumedAdultCandidate]. output.get()))
  2279. numberOfCorrect ++;
  2280. }
  2281. OTGrammar_opt_deleteOutputMatching (me);
  2282. return (double) numberOfCorrect / numberOfInputs;
  2283. } catch (MelderError) {
  2284. Melder_throw (me, U" & ", thee, U": fraction correct not computed.");
  2285. }
  2286. }
  2287. void OTGrammar_removeConstraint (OTGrammar me, conststring32 constraintName) {
  2288. try {
  2289. integer removed = 0;
  2290. Melder_require (my numberOfConstraints > 1,
  2291. U"Cannot remove last remaining constraint.");
  2292. /*
  2293. Look for the constraint to be removed.
  2294. */
  2295. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2296. OTGrammarConstraint constraint = & my constraints [icons];
  2297. if (str32equ (constraint -> name.get(), constraintName)) {
  2298. removed = icons;
  2299. break;
  2300. }
  2301. }
  2302. if (removed == 0)
  2303. Melder_throw (U"No such constraint.");
  2304. /*
  2305. Remove the constraint while reusing the memory space.
  2306. */
  2307. for (integer icons = removed; icons < my numberOfConstraints; icons ++) {
  2308. my constraints [icons] = std::move (my constraints [icons + 1]);
  2309. }
  2310. my constraints [my numberOfConstraints]. destroy (); // this will do nothing except if the removed constraint is the last one
  2311. my numberOfConstraints -= 1;
  2312. /*
  2313. Remove or shift fixed rankings.
  2314. */
  2315. for (integer ifixed = my numberOfFixedRankings; ifixed > 0; ifixed --) {
  2316. OTGrammarFixedRanking fixed = & my fixedRankings [ifixed];
  2317. if (fixed -> higher == removed || fixed -> lower == removed) {
  2318. /*
  2319. Remove fixed ranking.
  2320. */
  2321. my numberOfFixedRankings -= 1;
  2322. if (my numberOfFixedRankings == 0) {
  2323. NUMvector_free <structOTGrammarFixedRanking> (my fixedRankings, 1);
  2324. my fixedRankings = nullptr;
  2325. }
  2326. for (integer jfixed = ifixed; jfixed <= my numberOfFixedRankings; jfixed ++)
  2327. my fixedRankings [jfixed] = my fixedRankings [jfixed + 1];
  2328. } else {
  2329. /*
  2330. Shift fixed ranking.
  2331. */
  2332. if (fixed -> higher > removed)
  2333. fixed -> higher -= 1;
  2334. if (fixed -> lower > removed)
  2335. fixed -> lower -= 1;
  2336. }
  2337. }
  2338. /*
  2339. Shift tableau rows.
  2340. */
  2341. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  2342. OTGrammarTableau tableau = & my tableaus [itab];
  2343. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  2344. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  2345. candidate -> numberOfConstraints -= 1;
  2346. for (integer icons = removed; icons <= my numberOfConstraints; icons ++)
  2347. candidate -> marks [icons] = candidate -> marks [icons + 1];
  2348. }
  2349. }
  2350. /*
  2351. Rebuild index.
  2352. */
  2353. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) my index [icons] = icons;
  2354. OTGrammar_sort (me);
  2355. } catch (MelderError) {
  2356. Melder_throw (me, U": constraint \"", constraintName, U"\" not removed.");
  2357. }
  2358. }
  2359. static void OTGrammarTableau_removeCandidate_unstripped (OTGrammarTableau me, integer candidateNumber) {
  2360. Melder_assert (candidateNumber >= 1);
  2361. if (candidateNumber > my numberOfCandidates)
  2362. Melder_fatal (U"icand ", candidateNumber, U", ncand ", my numberOfCandidates);
  2363. for (integer jcand = candidateNumber; jcand < my numberOfCandidates; jcand ++) {
  2364. OTGrammarCandidate candj = & my candidates [jcand];
  2365. OTGrammarCandidate candj1 = & my candidates [jcand + 1];
  2366. candj -> output = candj1 -> output. move();
  2367. candj -> marks = candj1 -> marks. move();
  2368. }
  2369. my candidates [my numberOfCandidates]. output. reset();
  2370. my candidates [my numberOfCandidates]. marks. reset();
  2371. my numberOfCandidates -= 1;
  2372. }
  2373. static bool OTGrammarTableau_isHarmonicallyBounded (OTGrammarTableau me, integer icand, integer jcand) {
  2374. OTGrammarCandidate candi = & my candidates [icand], candj = & my candidates [jcand];
  2375. bool equal = true;
  2376. if (icand == jcand)
  2377. return false;
  2378. for (integer icons = 1; icons <= candi -> numberOfConstraints; icons ++) {
  2379. if (candi -> marks [icons] < candj -> marks [icons])
  2380. return false;
  2381. if (candi -> marks [icons] > candj -> marks [icons])
  2382. equal = false;
  2383. }
  2384. return ! equal;
  2385. }
  2386. static bool OTGrammarTableau_candidateIsPossibleWinner (OTGrammar me, integer itab, integer icand) {
  2387. OTGrammar_save (me);
  2388. OTGrammar_reset (me, 100.0);
  2389. for (;;) {
  2390. bool grammarHasChanged = false;
  2391. OTGrammar_learnOne (me, my tableaus [itab]. input.get(), my tableaus [itab]. candidates [icand]. output.get(),
  2392. 1e-3, kOTGrammar_rerankingStrategy::EDCD, false, 1.0, 0.0, true, true, & grammarHasChanged);
  2393. if (! grammarHasChanged) {
  2394. OTGrammar_restore (me);
  2395. return true;
  2396. }
  2397. double previousStratum = 101.0;
  2398. OTGrammar_newDisharmonies (me, 0.0);
  2399. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2400. double stratum = my constraints [my index [icons]]. ranking;
  2401. #if 0
  2402. if (stratum < 50.0 - my numberOfConstraints) {
  2403. OTGrammar_restore (me);
  2404. return false; // we detected a tumble
  2405. }
  2406. #else
  2407. if (stratum < previousStratum) {
  2408. if (stratum < previousStratum - 1.0) {
  2409. OTGrammar_restore (me);
  2410. return false; // we detected a vacated stratum
  2411. }
  2412. previousStratum = stratum;
  2413. }
  2414. #endif
  2415. }
  2416. }
  2417. return false; // cannot occur
  2418. }
  2419. void OTGrammar_removeHarmonicallyBoundedCandidates (OTGrammar me, bool singly) {
  2420. try {
  2421. /*
  2422. First, the candidates that are harmonically bounded by one or more single other candidates have to be removed;
  2423. otherwise, EDCD will stall.
  2424. */
  2425. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  2426. OTGrammarTableau tab = & my tableaus [itab];
  2427. for (integer icand = tab -> numberOfCandidates; icand >= 1; icand --) {
  2428. for (integer jcand = 1; jcand <= tab -> numberOfCandidates; jcand ++) {
  2429. if (OTGrammarTableau_isHarmonicallyBounded (tab, icand, jcand)) {
  2430. OTGrammarTableau_removeCandidate_unstripped (tab, icand);
  2431. break;
  2432. }
  2433. }
  2434. }
  2435. tab -> candidates = (OTGrammarCandidate) realloc (& tab -> candidates [1], sizeof (struct structOTGrammarCandidate) * tab -> numberOfCandidates) - 1;
  2436. }
  2437. if (! singly) {
  2438. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  2439. OTGrammarTableau tab = & my tableaus [itab];
  2440. for (integer icand = tab -> numberOfCandidates; icand >= 1; icand --) {
  2441. if (! OTGrammarTableau_candidateIsPossibleWinner (me, itab, icand))
  2442. OTGrammarTableau_removeCandidate_unstripped (tab, icand);
  2443. }
  2444. tab -> candidates = (OTGrammarCandidate) realloc (& tab -> candidates [1], sizeof (struct structOTGrammarCandidate) * tab -> numberOfCandidates) - 1;
  2445. }
  2446. }
  2447. } catch (MelderError) {
  2448. Melder_throw (me, U": not all harmonically bounded candidates were removed.");
  2449. }
  2450. }
  2451. Thing_define (OTGrammar_List4, Daata) {
  2452. // new data:
  2453. integer hi1, lo1, hi2, lo2;
  2454. };
  2455. Thing_implement (OTGrammar_List4, Daata, 0);
  2456. void OTGrammar_PairDistribution_listObligatoryRankings (OTGrammar me, PairDistribution thee) {
  2457. /*
  2458. Save.
  2459. */
  2460. OTGrammarFixedRanking savedFixedRankings = my fixedRankings; // dangle...
  2461. my fixedRankings = nullptr; // ...undangle
  2462. integer savedNumberOfFixedRankings = my numberOfFixedRankings;
  2463. OTGrammar_save (me);
  2464. try {
  2465. integer ipair = 0, npair = my numberOfConstraints * (my numberOfConstraints - 1);
  2466. integer itrial;
  2467. const double evaluationNoise = 1e-9;
  2468. /*
  2469. Add room for two more fixed rankings.
  2470. */
  2471. my fixedRankings = NUMvector <structOTGrammarFixedRanking> (1, my numberOfFixedRankings + 2);
  2472. for (integer ifixedRanking = 1; ifixedRanking <= my numberOfFixedRankings; ifixedRanking ++) {
  2473. my fixedRankings [ifixedRanking]. higher = savedFixedRankings [ifixedRanking]. higher;
  2474. my fixedRankings [ifixedRanking]. lower = savedFixedRankings [ifixedRanking]. lower;
  2475. }
  2476. /*
  2477. Test whether there are rankings at all for these output data.
  2478. */
  2479. OTGrammar_reset (me, 100.0);
  2480. for (itrial = 1; itrial <= 40; itrial ++) {
  2481. bool grammarHasChangedDuringCycle = false;
  2482. OTGrammar_honourLocalRankings (me, 1.0, 0.0, & grammarHasChangedDuringCycle);
  2483. OTGrammar_newDisharmonies (me, evaluationNoise);
  2484. for (integer iform = 1; iform <= thy pairs.size; iform ++) {
  2485. PairProbability prob = thy pairs.at [iform];
  2486. if (prob -> weight > 0.0) {
  2487. bool grammarHasChanged = false;
  2488. OTGrammar_learnOne (me, prob -> string1.get(), prob -> string2.get(),
  2489. evaluationNoise, kOTGrammar_rerankingStrategy::EDCD, true /* honour fixed rankings; very important */,
  2490. 1.0, 0.0, false, true, & grammarHasChanged
  2491. );
  2492. if (grammarHasChanged)
  2493. OTGrammar_newDisharmonies (me, evaluationNoise);
  2494. grammarHasChangedDuringCycle |= grammarHasChanged;
  2495. }
  2496. }
  2497. if (! grammarHasChangedDuringCycle) break;
  2498. }
  2499. if (itrial > 40) {
  2500. MelderInfo_writeLine (U"There are no total rankings that generate these input-output pairs.");
  2501. throw MelderError ();
  2502. }
  2503. /*
  2504. Test learnability of every possible ranked pair.
  2505. */
  2506. my numberOfFixedRankings ++;
  2507. autoNUMmatrix <bool> obligatory (1, my numberOfConstraints, 1, my numberOfConstraints);
  2508. MelderInfo_open ();
  2509. autoMelderProgress progress (U"Finding obligatory rankings.");
  2510. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2511. for (integer jcons = 1; jcons <= my numberOfConstraints; jcons ++) if (icons != jcons) {
  2512. my fixedRankings [my numberOfFixedRankings]. higher = icons;
  2513. my fixedRankings [my numberOfFixedRankings]. lower = jcons;
  2514. OTGrammar_reset (me, 100.0);
  2515. Melder_progress ((double) ipair / npair, ipair + 1, U"/", npair, U": Trying ranking ",
  2516. my constraints [icons]. name.get(), U" >> ", my constraints [jcons]. name.get());
  2517. ipair ++;
  2518. for (itrial = 1; itrial <= 40; itrial ++) {
  2519. bool grammarHasChangedDuringCycle = false;
  2520. OTGrammar_honourLocalRankings (me, 1.0, 0.0, & grammarHasChangedDuringCycle);
  2521. OTGrammar_newDisharmonies (me, evaluationNoise);
  2522. for (integer iform = 1; iform <= thy pairs.size; iform ++) {
  2523. PairProbability prob = thy pairs.at [iform];
  2524. if (prob -> weight > 0.0) {
  2525. bool grammarHasChanged = false;
  2526. OTGrammar_learnOne (me, prob -> string1.get(), prob -> string2.get(),
  2527. evaluationNoise, kOTGrammar_rerankingStrategy::EDCD, true /* honour fixed rankings; very important */,
  2528. 1.0, 0.0, false, true, & grammarHasChanged);
  2529. if (grammarHasChanged)
  2530. OTGrammar_newDisharmonies (me, evaluationNoise);
  2531. grammarHasChangedDuringCycle |= grammarHasChanged;
  2532. }
  2533. }
  2534. if (! grammarHasChangedDuringCycle)
  2535. break;
  2536. }
  2537. if (itrial > 40) {
  2538. obligatory [jcons] [icons] = true;
  2539. MelderInfo_writeLine (my constraints [jcons]. name.get(), U" >> ", my constraints [icons]. name.get());
  2540. MelderInfo_close ();
  2541. }
  2542. }
  2543. }
  2544. my numberOfFixedRankings ++;
  2545. Melder_progress (0.0, U"");
  2546. npair = npair * npair;
  2547. OrderedOf<structOTGrammar_List4> list;
  2548. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2549. for (integer jcons = 1; jcons <= my numberOfConstraints; jcons ++) if (icons != jcons && ! obligatory [jcons] [icons]) {
  2550. my fixedRankings [my numberOfFixedRankings - 1]. higher = icons;
  2551. my fixedRankings [my numberOfFixedRankings - 1]. lower = jcons;
  2552. for (integer kcons = icons; kcons <= my numberOfConstraints; kcons ++) {
  2553. for (integer lcons = 1; lcons <= my numberOfConstraints; lcons ++) if (kcons != lcons && ! obligatory [lcons] [kcons]) {
  2554. if (icons == kcons && jcons >= lcons) continue;
  2555. if (icons == lcons && jcons == kcons) continue;
  2556. if (jcons == kcons && obligatory [lcons] [icons]) continue;
  2557. if (icons == lcons && obligatory [jcons] [kcons]) continue;
  2558. if (obligatory [lcons] [icons] && obligatory [jcons] [kcons]) continue;
  2559. my fixedRankings [my numberOfFixedRankings]. higher = kcons;
  2560. my fixedRankings [my numberOfFixedRankings]. lower = lcons;
  2561. OTGrammar_reset (me, 100.0);
  2562. Melder_progress ((double) ipair / npair, ipair + 1, U"/", npair);
  2563. ipair ++;
  2564. for (itrial = 1; itrial <= 40; itrial ++) {
  2565. bool grammarHasChangedDuringCycle = false;
  2566. OTGrammar_honourLocalRankings (me, 1.0, 0.0, & grammarHasChangedDuringCycle);
  2567. OTGrammar_newDisharmonies (me, evaluationNoise);
  2568. for (integer iform = 1; iform <= thy pairs.size; iform ++) {
  2569. PairProbability prob = thy pairs.at [iform];
  2570. if (prob -> weight > 0.0) {
  2571. bool grammarHasChanged = false;
  2572. OTGrammar_learnOne (me, prob -> string1.get(), prob -> string2.get(),
  2573. evaluationNoise, kOTGrammar_rerankingStrategy::EDCD, true /* honour fixed rankings; very important */,
  2574. 1.0, 0.0, false, true, & grammarHasChanged);
  2575. if (grammarHasChanged)
  2576. OTGrammar_newDisharmonies (me, evaluationNoise);
  2577. grammarHasChangedDuringCycle |= grammarHasChanged;
  2578. }
  2579. }
  2580. if (! grammarHasChangedDuringCycle) break;
  2581. }
  2582. if (itrial > 40) {
  2583. autoOTGrammar_List4 listElement = Thing_new (OTGrammar_List4);
  2584. listElement -> hi1 = jcons;
  2585. listElement -> lo1 = icons;
  2586. listElement -> hi2 = lcons;
  2587. listElement -> lo2 = kcons;
  2588. list. addItem_move (listElement.move());
  2589. }
  2590. }
  2591. }
  2592. }
  2593. }
  2594. Melder_progress (1.0);
  2595. /*
  2596. Improve list.
  2597. */
  2598. bool improved = true;
  2599. while (improved) {
  2600. improved = false;
  2601. for (integer ilist = 1; ilist <= list.size; ilist ++) {
  2602. for (integer jlist = 1; jlist <= list.size; jlist ++) if (ilist != jlist) {
  2603. OTGrammar_List4 elA = list.at [ilist];
  2604. OTGrammar_List4 elB = list.at [jlist];
  2605. integer ahi1 = elA -> hi1, alo1 = elA -> lo1, ahi2 = elA -> hi2, alo2 = elA -> lo2;
  2606. integer bhi1 = elB -> hi1, blo1 = elB -> lo1, bhi2 = elB -> hi2, blo2 = elB -> lo2;
  2607. improved |= (ahi1 == bhi1 || obligatory [bhi1] [ahi1]) && (ahi2 == bhi2 || obligatory [bhi2] [ahi2]) &&
  2608. (alo1 == blo1 || obligatory [alo1] [blo1]) && (alo2 == blo2 || obligatory [alo2] [blo2]);
  2609. improved |= (ahi1 == bhi2 || obligatory [bhi2] [ahi1]) && (ahi2 == bhi1 || obligatory [bhi1] [ahi2]) &&
  2610. (alo1 == blo2 || obligatory [alo1] [blo2]) && (alo2 == blo1 || obligatory [alo2] [blo1]);
  2611. if (improved) {
  2612. list. removeItem (jlist);
  2613. break;
  2614. }
  2615. }
  2616. if (improved) break;
  2617. }
  2618. }
  2619. improved = true;
  2620. while (improved) {
  2621. improved = false;
  2622. for (integer ilist = 1; ilist <= list.size; ilist ++) {
  2623. for (integer jlist = 1; jlist <= list.size; jlist ++) if (ilist != jlist) {
  2624. OTGrammar_List4 elA = list.at [ilist];
  2625. OTGrammar_List4 elB = list.at [jlist];
  2626. integer ahi1 = elA -> hi1, alo1 = elA -> lo1, ahi2 = elA -> hi2, alo2 = elA -> lo2;
  2627. integer bhi1 = elB -> hi1, blo1 = elB -> lo1, bhi2 = elB -> hi2, blo2 = elB -> lo2;
  2628. improved |= ahi1 == bhi1 && alo1 == blo1 && ahi2 == bhi2 && blo2 == bhi1 && alo2 == alo1;
  2629. improved |= ahi1 == bhi2 && alo1 == blo2 && ahi2 == bhi1 && blo1 == bhi2 && alo2 == alo1;
  2630. improved |= ahi2 == bhi1 && alo2 == blo1 && ahi1 == bhi2 && blo2 == bhi1 && alo1 == alo2;
  2631. improved |= ahi2 == bhi2 && alo2 == blo2 && ahi1 == bhi1 && blo1 == bhi2 && alo1 == alo2;
  2632. if (improved) {
  2633. list. removeItem (jlist);
  2634. break;
  2635. }
  2636. }
  2637. if (improved) break;
  2638. }
  2639. }
  2640. for (integer ilist = 1; ilist <= list.size; ilist ++) {
  2641. OTGrammar_List4 el = list.at [ilist];
  2642. MelderInfo_write (my constraints [el -> hi1]. name.get(), U" >> ", my constraints [el -> lo1]. name.get(), U" OR ");
  2643. MelderInfo_writeLine (my constraints [el -> hi2]. name.get(), U" >> ", my constraints [el -> lo2]. name.get());
  2644. MelderInfo_close ();
  2645. }
  2646. MelderInfo_close ();
  2647. /*
  2648. Remove room.
  2649. */
  2650. NUMvector_free <structOTGrammarFixedRanking> (my fixedRankings, 1); // dangle
  2651. /*
  2652. Restore.
  2653. */
  2654. my numberOfFixedRankings = savedNumberOfFixedRankings;
  2655. my fixedRankings = savedFixedRankings; // undangle
  2656. OTGrammar_restore (me);
  2657. } catch (MelderError) {
  2658. MelderInfo_close ();
  2659. /*
  2660. Remove room.
  2661. */
  2662. NUMvector_free <structOTGrammarFixedRanking> (my fixedRankings, 1); // dangle
  2663. /*
  2664. Restore.
  2665. */
  2666. my numberOfFixedRankings = savedNumberOfFixedRankings;
  2667. my fixedRankings = savedFixedRankings; // undangle
  2668. OTGrammar_restore (me);
  2669. Melder_throw (me, U": obligatory rankings not listed.");
  2670. }
  2671. }
  2672. void OTGrammar_Distributions_listObligatoryRankings (OTGrammar me, Distributions thee, integer columnNumber) {
  2673. /*
  2674. Save.
  2675. */
  2676. OTGrammarFixedRanking savedFixedRankings = my fixedRankings;
  2677. my fixedRankings = nullptr;
  2678. OTGrammar_save (me);
  2679. try {
  2680. integer ipair = 0, npair = my numberOfConstraints * (my numberOfConstraints - 1);
  2681. /*
  2682. Add room for one more fixed ranking.
  2683. */
  2684. my numberOfFixedRankings ++;
  2685. my fixedRankings = NUMvector <structOTGrammarFixedRanking> (1, my numberOfFixedRankings);
  2686. for (integer ifixedRanking = 1; ifixedRanking < my numberOfFixedRankings; ifixedRanking ++) {
  2687. my fixedRankings [ifixedRanking]. higher = savedFixedRankings [ifixedRanking]. higher;
  2688. my fixedRankings [ifixedRanking]. lower = savedFixedRankings [ifixedRanking]. lower;
  2689. }
  2690. /*
  2691. Test learnability of every possible ranked pair.
  2692. */
  2693. MelderInfo_open ();
  2694. autoMelderProgress progress (U"Finding obligatory rankings.");
  2695. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2696. for (integer jcons = 1; jcons <= my numberOfConstraints; jcons ++) if (icons != jcons) {
  2697. my fixedRankings [my numberOfFixedRankings]. higher = icons;
  2698. my fixedRankings [my numberOfFixedRankings]. lower = jcons;
  2699. OTGrammar_reset (me, 100.0);
  2700. Melder_progress ((double) ipair / npair, ipair + 1, U"/", npair, U": Trying ranking ",
  2701. my constraints [icons]. name.get(), U" >> ", my constraints [jcons]. name.get());
  2702. ipair ++;
  2703. Melder_progressOff ();
  2704. OTGrammar_Distributions_learnFromPartialOutputs (me, thee, columnNumber,
  2705. 1e-9, kOTGrammar_rerankingStrategy::EDCD, true /* honour fixed rankings; very important */,
  2706. 1.0, 1000, 0.0, 1, 0.0, 1, 0, nullptr, false, false, 0);
  2707. Melder_progressOn ();
  2708. for (integer kcons = 1; kcons <= my numberOfConstraints; kcons ++) {
  2709. if (my constraints [kcons]. ranking < 0.0) {
  2710. MelderInfo_writeLine (my constraints [jcons]. name.get(), U" >> ", my constraints [icons]. name.get());
  2711. break;
  2712. }
  2713. }
  2714. }
  2715. }
  2716. MelderInfo_close ();
  2717. /*
  2718. Remove room.
  2719. */
  2720. NUMvector_free <structOTGrammarFixedRanking> (my fixedRankings, 1); // dangle
  2721. /*
  2722. * Restore.
  2723. */
  2724. my numberOfFixedRankings --;
  2725. my fixedRankings = savedFixedRankings; // undangle
  2726. OTGrammar_restore (me);
  2727. } catch (MelderError) {
  2728. MelderInfo_close ();
  2729. /*
  2730. Remove room.
  2731. */
  2732. NUMvector_free <structOTGrammarFixedRanking> (my fixedRankings, 1); // dangle
  2733. /*
  2734. Restore.
  2735. */
  2736. my numberOfFixedRankings --;
  2737. my fixedRankings = savedFixedRankings; // undangle
  2738. OTGrammar_restore (me);
  2739. Melder_throw (me, U": obligatory rankings not listed.");
  2740. }
  2741. }
  2742. static void printConstraintNames (OTGrammar me, MelderString *buffer) {
  2743. char32 text [200];
  2744. bool secondLine = false;
  2745. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2746. OTGrammarConstraint constraint = & my constraints [my index [icons]];
  2747. if (str32chr (constraint -> name.get(), U'\n')) {
  2748. char32 *newLine;
  2749. str32cpy (text, constraint -> name.get());
  2750. newLine = str32chr (text, U'\n');
  2751. *newLine = U'\0';
  2752. MelderString_append (buffer, U"\t", text);
  2753. secondLine = true;
  2754. } else {
  2755. MelderString_append (buffer, U"\t", constraint -> name.get());
  2756. }
  2757. }
  2758. MelderString_appendCharacter (buffer, U'\n');
  2759. if (secondLine) {
  2760. MelderString_appendCharacter (buffer, U'\t');
  2761. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2762. OTGrammarConstraint constraint = & my constraints [my index [icons]];
  2763. char32 *newLine = str32chr (constraint -> name.get(), U'\n');
  2764. MelderString_append (buffer, U"\t", newLine ? newLine + 1 : U"");
  2765. }
  2766. MelderString_appendCharacter (buffer, U'\n');
  2767. }
  2768. }
  2769. void OTGrammar_writeToHeaderlessSpreadsheetFile (OTGrammar me, MelderFile file) {
  2770. try {
  2771. autoMelderString buffer;
  2772. MelderString_copy (& buffer, U"CONSTRAINTS\t");
  2773. printConstraintNames (me, & buffer);
  2774. MelderString_append (& buffer, U"rankings\t");
  2775. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2776. OTGrammarConstraint constraint = & my constraints [my index [icons]];
  2777. MelderString_append (& buffer, U"\t", constraint -> ranking);
  2778. }
  2779. MelderString_append (& buffer, U"\ndisharmonies\t");
  2780. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2781. OTGrammarConstraint constraint = & my constraints [my index [icons]];
  2782. MelderString_append (& buffer, U"\t", constraint -> disharmony);
  2783. }
  2784. MelderString_appendCharacter (& buffer, U'\n');
  2785. for (integer itab = 1; itab <= my numberOfTableaus; itab ++) {
  2786. OTGrammarTableau tableau = & my tableaus [itab];
  2787. integer winner = OTGrammar_getWinner (me, itab), numberOfOptimalCandidates = 0;
  2788. for (integer icons = 1; icons <= my numberOfConstraints + 1; icons ++)
  2789. MelderString_appendCharacter (& buffer, U'\t');
  2790. MelderString_append (& buffer, U"\nINPUT\t", tableau -> input.get());
  2791. printConstraintNames (me, & buffer);
  2792. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  2793. if (OTGrammar_compareCandidates (me, itab, icand, itab, winner) == 0)
  2794. numberOfOptimalCandidates ++;
  2795. }
  2796. for (integer icand = 1; icand <= tableau -> numberOfCandidates; icand ++) {
  2797. OTGrammarCandidate candidate = & tableau -> candidates [icand];
  2798. bool candidateIsOptimal = OTGrammar_compareCandidates (me, itab, icand, itab, winner) == 0;
  2799. integer crucialCell = OTGrammar_crucialCell (me, itab, icand, winner, numberOfOptimalCandidates);
  2800. MelderString_append (& buffer,
  2801. candidateIsOptimal == false ? U"loser" : numberOfOptimalCandidates > 1 ? U"co-winner" : U"winner",
  2802. U"\t",
  2803. candidate -> output.get());
  2804. for (integer icons = 1; icons <= my numberOfConstraints; icons ++) {
  2805. integer index = my index [icons];
  2806. OTGrammarConstraint constraint = & my constraints [index];
  2807. static MelderString markString;
  2808. MelderString_empty (& markString);
  2809. /*
  2810. An exclamation mark can be drawn in this cell only if all of the following conditions are met:
  2811. 1. the candidate is not optimal;
  2812. 2. the constraint is not tied;
  2813. 3. this is the crucial cell, i.e. the cells after it are drawn in grey.
  2814. */
  2815. if (icons == crucialCell && ! candidateIsOptimal && ! constraint -> tiedToTheLeft && ! constraint -> tiedToTheRight) {
  2816. const integer winnerMarks = tableau -> candidates [winner]. marks [index];
  2817. for (integer imark = 1; imark <= winnerMarks + 1; imark ++)
  2818. MelderString_appendCharacter (& markString, U'*');
  2819. MelderString_appendCharacter (& markString, U'!');
  2820. for (integer imark = winnerMarks + 2; imark <= candidate -> marks [index]; imark ++)
  2821. MelderString_appendCharacter (& markString, U'*');
  2822. } else {
  2823. if (! candidateIsOptimal && (constraint -> tiedToTheLeft || constraint -> tiedToTheRight) &&
  2824. crucialCell >= 1 && constraint -> disharmony == my constraints [my index [crucialCell]]. disharmony)
  2825. {
  2826. MelderString_appendCharacter (& markString, U'=');
  2827. }
  2828. for (integer imark = 1; imark <= candidate -> marks [index]; imark ++)
  2829. MelderString_appendCharacter (& markString, U'*');
  2830. }
  2831. MelderString_append (& buffer, U"\t", markString.string);
  2832. }
  2833. MelderString_appendCharacter (& buffer, U'\n');
  2834. }
  2835. }
  2836. MelderFile_writeText (file, buffer.string, Melder_getOutputEncoding ());
  2837. } catch (MelderError) {
  2838. Melder_throw (me, U": not saved to tab-separated file ", file, U".");
  2839. }
  2840. }
  2841. /* End of file OTGrammar.cpp */