Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
436 views
1
# -*- coding: utf-8 -*-
2
"""
3
GSC Software: Grammar class
4
Author: Nick Becker
5
Department of Cognitive Science, Johns Hopkins University
6
Summer 2015
7
"""
8
9
import os.path
10
import re
11
import sys
12
import numpy as np
13
from collections import OrderedDict
14
from itertools import product
15
from tree import Tree
16
17
class Grammar(object):
18
"""
19
The last grammar class you will ever need. Currently under development.
20
"""
21
22
# -------------------------------------------------------------------------
23
# Constructor and supporting functions
24
25
def __init__(self, rules, isPcfg=False, isHnf=False):
26
"""
27
Create a grammar object from a grammar specification.
28
29
If grammar rules are being passed in a file, they must be written in the form
30
A -> B C | D E
31
F -> G H
32
... etc. Separate rules must be written on separate lines.
33
34
If grammar rules are being passed as an argument, they must be written in the form
35
"A -> B C | D E; F -> G H"
36
... etc. Separate rules must be separated by a semicolon (;).
37
38
In both cases, rules with multiple right-hand sides can use a vertical bar ('|') to
39
separate different right-hand sides, or every rule can just be written on a separate line.
40
All sister symbols must be separated by a space (' '). The first node of the first
41
rule will be treated as the root node internally. For now, all rules must be binary
42
branching at most; this is enforced within the program.
43
44
By default, this function accepts rules in (approximately) CNF form and converts them to HNF.
45
If you want to enter HNF rules using the standard bracketing practice, you must set the hnf
46
parameter to True by passing this as a value to the function.
47
48
Rules can be entered in HNF; if they are, the flag 'isHnf' must be set to 'True.'
49
PCFG functionality is on its way. For now, best not to use this functionality.
50
Some features (i.e. 'toString' methods) may not work if you do.
51
52
This function is based on one originally written by Colin Wilson.
53
"""
54
self.isPcfg = isPcfg
55
56
listOfRules = []
57
58
if os.path.isfile(rules):
59
f = open(rules, 'r')
60
for line in f:
61
listOfRules.append(line)
62
f.close()
63
else:
64
listOfRules = rules.split(';')
65
66
# check that all rules approximately conform to prescribed format
67
ruleForm = re.compile(r'.+->.+')
68
for i in range(len(listOfRules)):
69
listOfRules[i] = listOfRules[i].strip()
70
if re.match(ruleForm, listOfRules[i]) == None:
71
sys.exit('Error: input ' + listOfRules[i] + ' is not formatted correctly.')
72
73
# this is a bit sloppy right now, but the order in which these lines are executed does matter;
74
# there is a different form depending on whether we received HNF rules (and need to convert
75
# to CNF) or received CNF (and need to convert to HNF)
76
if not isHnf: # default
77
self.cnfRules = self.createRules(listOfRules)
78
self.hnfRules, self.branchSyms = self.cnfToHnf()
79
else:
80
self.hnfRules = self.createRules(listOfRules)
81
self.cnfRules, self.branchSyms = self.hnfToCnf()
82
83
if self.isPcfg:
84
self.cnfRules, self.hnfRules = self.setRemainingProbabilities()
85
86
self.hgRulesAreSet = False
87
self.networkInfoIsSet = False
88
self.allGridpointsAreSet = False
89
self.zIsSet = False
90
self.allHarmoniesAreSet = False
91
92
def createRules(self, listOfRules):
93
"""
94
Create the internal representation of the CFG passed as parameter listOfRules.
95
"""
96
maxN = 2
97
98
fl = re.compile(r'^0*1?\.?\d+?$')
99
100
rules = OrderedDict()
101
for rule in listOfRules:
102
splitRule = rule.split('->') # split ruleInput into rhs and lhs
103
lhs = splitRule[0].strip() # rule[0] is the first piece (lhs)
104
wholeRhs = splitRule[1].split('|') # rule[1] is the second piece (rhs)
105
106
for rhs in wholeRhs:
107
rhsList = rhs.split()
108
nChildren = len(rhsList)
109
if self.isPcfg and re.match(fl, rhsList[0]) != None:
110
rhsList[0] = float(rhsList[0]) # this is a probability
111
nChildren -= 1
112
if nChildren > maxN:
113
sys.exit('Seriously? Do you really need ' + str(len(rhsList)) + \
114
'-ary trees? Chomsky was satisfied with binary trees; ' + \
115
'they should be sufficient for you too.')
116
rules.setdefault(lhs, []).append(rhsList)
117
118
return rules
119
120
def cnfToHnf(self):
121
"""
122
Convert rules in "Conventional Normal Form" to Harmonic Normal Form. (In other
123
words, add intermediate bracketed symbols.) Must be called after self.cnfRules has
124
been created.
125
126
This function is based on one originally written by Colin Wilson.
127
"""
128
hnfRules = OrderedDict()
129
branchSyms = []
130
131
# for each left hand side
132
b = re.compile(r'.*\[\d+\]')
133
goodInput = True
134
for lhs in self.cnfRules.keys():
135
if re.match(b, lhs) != None:
136
goodInput = False
137
bracketIndex = 1
138
for rhs in self.cnfRules[lhs]:
139
# get degree of of rule (# of nonterminals)
140
degree = len(rhs)
141
# if there is more than one nonterminal
142
if degree >= 1:
143
# create bracket symbol
144
newSym = lhs +'['+ str(bracketIndex) +']'
145
# add unique branching lhs to list
146
if not newSym in branchSyms:
147
branchSyms.append(newSym)
148
149
# add rules (lhs --> new and new --> rhs)
150
if self.isPcfg and isinstance(rhs[0], float):
151
hnfRules.setdefault(lhs, []).append([rhs[0], newSym])
152
hnfRules.setdefault(newSym, []).append(rhs[1:])
153
else:
154
hnfRules.setdefault(lhs, []).append([newSym])
155
hnfRules.setdefault(newSym, []).append(rhs[:])
156
157
bracketIndex += 1
158
159
# if it's a terminal, just add original rule
160
else:
161
hnfRules[lhs].append(rhs)
162
163
# issue warning if no bracketed symbols were found
164
if not goodInput:
165
sys.exit('Error: HNF-style bracketing detected. If you meant to enter your rules in HNF, ' + \
166
'you must pass parameter \'True\' to setRules(); otherwise, you should avoid using HNF-style ' + \
167
'bracketing structure in your nodes.')
168
169
# return converted rules
170
return hnfRules, branchSyms
171
172
def hnfToCnf(self):
173
"""
174
Convert rules in Harmonic Normal Form to "Conventional Normal Form." (In other
175
words, remove intermediate bracketed symbols.) Must be called after self.hnfRules
176
has been created.
177
"""
178
cnfRules = OrderedDict()
179
branchSyms = []
180
181
# for each left hand side
182
b = re.compile(r'.*\[\d+\]')
183
goodInput = False
184
for lhs in self.hnfRules.keys():
185
if re.match(b, lhs) != None:
186
branchSyms.append(lhs)
187
newLhs = lhs.split('[')[0]
188
for rhs in self.hnfRules[lhs]:
189
cnfRules.setdefault(newLhs, []).append(rhs[:])
190
goodInput = True
191
192
if not goodInput:
193
sys.exit('Error: HNF not detected. If you did not intend to use HNF, do not pass parameter ' + \
194
'\'True\' to setRules().')
195
196
# return converted rules
197
return cnfRules, branchSyms
198
199
def hnfTreeToState(self, inputString):
200
"""
201
Use grammar to convert a tree in HNF (input as text) to a state based on the current
202
network settings.
203
"""
204
if not self.networkInfoIsSet:
205
self.setNetworkInfo()
206
207
currentTree = Tree(inputString)
208
frBindings = currentTree.getFRbindings()
209
210
byRole = {}
211
for binding in frBindings:
212
if binding[0] not in self.fillerNames:
213
sys.exit('Error: Invalid filler (' + binding[0] + ').')
214
if binding[1] not in self.roleNames:
215
sys.exit('Error: Invalid role (' + binding[1] + ').')
216
byRole[binding[1]] = binding[0]
217
218
state = []
219
for role in self.roleNames:
220
if role not in byRole:
221
if self.padWithNulls:
222
sys.exit('Error: Role ' + role + ' not in tree. Check that \n(a) you entered ' + \
223
'your tree in HNF, \n(b) null elements in the tree are explicitly represented with ' + \
224
'the null symbol (' + self.nullSymbol + '), and \n(b) your tree is licensed by the ' + \
225
'following grammar:\n\n' + self.hnfRulesToString())
226
else:
227
sys.exit('Error: Role ' + role + ' not in tree. Check that \n(a) you entered ' + \
228
'your tree in HNF, and \n(b) your tree is licensed by the following grammar:\n\n' + \
229
self.hnfRulesToString())
230
thisRole = [0] * len(self.fillerNames)
231
thisRole[self.fillerNames.index(byRole[role])] = 1
232
state += thisRole
233
234
return np.transpose(np.array(state))
235
236
def stateToString(self, state):
237
"""
238
Convert state stored as a column vector to a string of 1s and 0s.
239
"""
240
returnString = np.array_str(state) # convert array to string
241
returnString = returnString[1:-1] # strip off leading and trailing brackets
242
returnString = returnString.replace(' ','') # remove spaces...
243
returnString = returnString.replace('.','') # ... and remove periods...
244
returnString = returnString.replace('\n','') # ... and remove newlines so we are left with a binary string
245
246
return returnString
247
248
# -------------------------------------------------------------------------
249
# Probability-related functions (these are only called in the case of a PCFG)
250
251
def setRemainingProbabilities(self):
252
"""
253
Fix rules such that if this is a PCFG, all rules that can expand to more than
254
one right-hand side are given an equal probability to expand to each of those right-hand
255
sides. Make sense?
256
"""
257
rulesSet = [self.cnfRules, self.hnfRules]
258
for rules in rulesSet:
259
for lhs in rules.keys():
260
wholeRhs = rules[lhs] # a list of right-hand sides (list of lists)
261
currentProbValues = []
262
noProbIndices = []
263
probToDistribute = 0
264
for rhs in wholeRhs:
265
if isinstance(rhs[0], float):
266
if rhs[0] < 0 or rhs[0] > 1:
267
sys.exit('Error: Invalid probability (' + str(rules[lhs][0]) + ').')
268
else:
269
currentProbValues.append(rhs[0])
270
else:
271
noProbIndices.append(wholeRhs.index(rhs))
272
if len(noProbIndices) != 0:
273
probToDivide = 1 - sum(currentProbValues)
274
probToDistribute = probToDivide / len(noProbIndices)
275
for index in noProbIndices:
276
wholeRhs[index].insert(0, probToDistribute)
277
if np.abs(1 - (len(noProbIndices) * probToDistribute + sum(currentProbValues))) > 0.02:
278
sys.exit('Error: Probabilities do not sum to 1 (check rules beginning with ' + lhs + ')')
279
280
return rulesSet[0], rulesSet[1]
281
282
def adjustBiases(self):
283
"""
284
Adds values from self.biasAdjustments to self.biasVector to influence probability.
285
Here, the probability difference is simply added to the most likely structure.
286
"""
287
inf = float('Inf')
288
for adjustment in self.biasAdjustments:
289
if adjustment[1] > 0 and adjustment[1] < inf:
290
frBinding = adjustment[0][0] + '/' + adjustment[0][1]
291
self.biasVector[self.allFRbindings.index(frBinding)] += adjustment[1]
292
293
def computeProb(self, tree, T=1):
294
"""
295
Compute probability of tree (input as text), using all gridpoints as the sample
296
space and T = 1 by default.
297
"""
298
if not self.zIsSet:
299
self.computeZ()
300
301
return np.exp(self.getHarmony(tree) / T) / self.z
302
303
def computeZ(self, T=1):
304
"""
305
Computes sum_i (exp(H(tree_i)/T)) for T = 1
306
"""
307
if not self.allHarmoniesAreSet:
308
self.setAllHarmonies()
309
310
allHarmonies = np.array(list(self.allHarmonies.values()))
311
312
self.z = np.exp(allHarmonies / T).sum()
313
self.zIsSet = True
314
315
316
# -------------------------------------------------------------------------
317
# Setters and supporting functions
318
319
def setAllGridpoints(self):
320
"""
321
Generate all gridpoints and evaluate their Harmony.
322
"""
323
if not self.networkInfoIsSet:
324
self.setNetworkInfo()
325
326
nR = len(self.roleNames)
327
nF = len(self.fillerNames)
328
329
# generate a tuple for every possible gridpoint, storing all of them in allGrids
330
allPoints = []
331
for i in range(len(self.fillerNames)):
332
currentPoint = [0] * len(self.fillerNames)
333
currentPoint[i] = 1
334
allPoints.append(currentPoint)
335
336
allGridsList = list(product(allPoints, repeat=len(self.roleNames)))
337
338
allGridsMat = np.zeros(shape=(nR * nF, len(allGridsList)))
339
for i in range(len(allGridsList)):
340
# the following line creates a matrix from the current tuple, reshapes it into a
341
# single row, then transposes it to a single colum that represents this gridpoint
342
gridCol = np.transpose(np.reshape(np.array(allGridsList[i]), (nR * nF, 1)))
343
allGridsMat[:,i] = gridCol
344
345
self.allGridpoints = allGridsMat
346
self.allGridpointsAreSet = True
347
348
def setAllHarmonies(self):
349
"""
350
Store the harmony for every gridpoint because, why not? This may take a while to run,
351
but will only need to be run once per grammar.
352
"""
353
if not self.allGridpointsAreSet:
354
self.setAllGridpoints()
355
356
nGrids = self.allGridpoints.shape[1]
357
allHarmonies = {}
358
for i in range(nGrids):
359
stateKey = self.stateToString(self.allGridpoints[:,i])
360
allHarmonies[stateKey] = self.getHarmony(self.allGridpoints[:,i])
361
362
self.allHarmonies = allHarmonies
363
self.allHarmoniesAreSet = True
364
365
def setHarmonicGrammarRules(self, maxDepth=6, useHnf=True, addNullFillers=True, nullSymbol='_'):
366
"""
367
Create Harmonic Grammar rules based on the CFG passed as parameter
368
ruleDictionary.
369
"""
370
self.maxDepth = maxDepth
371
self.useHnf = useHnf
372
373
if self.useHnf:
374
ruleSet = self.hnfRules
375
else:
376
ruleSet = self.cnfRules
377
378
start = (self.getRootNode(ruleSet), 'r')
379
380
if addNullFillers:
381
self.needNullFiller = addNullFillers
382
self.nullSymbol = nullSymbol
383
self.nullPaddedRules = self.padWithNulls(ruleSet, self.nullSymbol)
384
ruleSet = self.nullPaddedRules
385
386
hgWeights = []
387
hgBiases = []
388
self.biasAdjustments = []
389
390
self.hgWeights, self.hgBiases = self.expandHGrules(start, ruleSet, hgWeights, hgBiases)
391
self.hgWeights, self.hgBiases = self.sortHGrules(self.hgWeights, self.hgBiases)
392
self.hgRulesAreSet = True
393
394
def padWithNulls(self, ruleSet, nullSymbol):
395
"""
396
"Symmetrizes" the CFG grammar in ruleSet by padding projections with null symbols.
397
For example, given the grammar
398
S -> A; S -> B B,
399
this function creates
400
S -> A _; S -> B B
401
Note that after this function is run, all parent nodes have the same number of children.
402
"""
403
# get max number of children (max 'n')
404
maxN = 0
405
for lhs in ruleSet.keys():
406
for rhs in ruleSet[lhs]:
407
currentN = len(rhs)
408
if currentN > maxN:
409
maxN = currentN
410
411
# fill each branching rule up to n children with fillers
412
for lhs in ruleSet.keys():
413
if lhs in self.branchSyms:
414
for rhs in ruleSet[lhs]:
415
while len(rhs) < maxN:
416
rhs.append(self.nullSymbol)
417
418
return ruleSet
419
420
def expandHGrules(self, parent, ruleSet, hgWeights, hgBiases):
421
"""
422
Recursive function to find Harmonic Grammar rules. Stops when all possible
423
paths through the CFG are explored, or the maximum depth is reached.
424
425
Right now, biases are added to roles only, not filler/role bindings.
426
"""
427
if len(parent[1]) < self.maxDepth:
428
if parent[0] in ruleSet.keys():
429
for rhs in ruleSet[parent[0]]:
430
harmonyDiff = 0
431
if self.isPcfg:
432
temp = rhs[1:]
433
# this currently only works for binary-branching trees
434
harmonyDiff = np.log(rhs[0]) - np.log(1 - rhs[0])
435
else:
436
temp = rhs[:]
437
if parent[1] == 'r':
438
hgBiases.append([parent[1], -(len(temp))])
439
else:
440
hgBiases.append([parent[1], -(len(temp) + 1)])
441
childLevel = '0' + parent[1]
442
for childSymbol in temp:
443
# format for hgRules: [[(S, r), (S[1], 0), 2], [...], ...]
444
self.biasAdjustments.append([[childSymbol, childLevel], harmonyDiff])
445
hgWeights.append([(parent[0], parent[1]), (childSymbol, childLevel), 2])
446
hgWeights, hgBiases = \
447
self.expandHGrules((childSymbol, childLevel), ruleSet, hgWeights, hgBiases)
448
childLevel = str(int(childLevel[0]) + 1) + childLevel[1:]
449
else:
450
# handles case where we are at a terminal node (at bottom of tree)
451
hgBiases.append([parent[1], -1])
452
else:
453
# handles case where max depth was reached at a non-terminal symbol (at bottom of tree)
454
hgBiases.append([parent[1], -1])
455
456
return hgWeights, hgBiases
457
458
def sortHGrules(self, hgWeights, hgBiases):
459
"""
460
This is pretty sloppy. So it will remain until we come up with a a more
461
clever data structure to store the HG rules (low priority right now).
462
"""
463
# sort hgWeights
464
needSwapped = True
465
while needSwapped:
466
needSwapped = False
467
for i in range(len(hgWeights) - 1):
468
if len(hgWeights[i][0][1]) > len(hgWeights[i+1][0][1]):
469
# swap
470
temp = hgWeights[i]
471
hgWeights[i] = hgWeights[i+1]
472
hgWeights[i+1] = temp
473
needSwapped = True
474
475
# use sets to remove duplicates
476
hgWeightsNoDuplicates = []
477
seen = set()
478
for weight in hgWeights:
479
# strings are hashable, lists are not, so we must convert to add to set
480
stringWeight = ''.join([weight[0][0], weight[0][1], weight[1][0], weight[1][1]])
481
if stringWeight not in seen:
482
hgWeightsNoDuplicates.append(weight)
483
seen.add(stringWeight)
484
485
# sort hgBiases; remember, hgBiases are currently in the form [role, bias]
486
needSwapped = True
487
while needSwapped:
488
needSwapped = False
489
for i in range(len(hgBiases) - 1):
490
if len(hgBiases[i][0]) > len(hgBiases[i+1][0]):
491
# swap
492
temp = hgBiases[i]
493
hgBiases[i] = hgBiases[i+1]
494
hgBiases[i+1] = temp
495
needSwapped = True
496
497
# use sets to remove duplicates again
498
hgBiasesNoDuplicates = []
499
seen = set()
500
for biasPair in hgBiases:
501
# strings are hashable, lists are not, so we must convert to add to set
502
if biasPair[0] not in seen:
503
hgBiasesNoDuplicates.append(biasPair)
504
seen.add(biasPair[0])
505
506
return hgWeightsNoDuplicates, hgBiasesNoDuplicates
507
508
def setNetworkInfo(self):
509
"""
510
Set the role names, filler names, weight matrix, and bias vector for
511
this HNF grammar.
512
"""
513
if not self.hgRulesAreSet:
514
self.setHarmonicGrammarRules()
515
516
roleNames = []
517
for i in range(len(self.hgBiases)):
518
if self.hgBiases[i][0] not in roleNames:
519
roleNames.append(self.hgBiases[i][0])
520
521
fillerNames = []
522
for i in range(len(self.hgWeights)):
523
if self.hgWeights[i][0][0] not in fillerNames:
524
fillerNames.append(self.hgWeights[i][0][0])
525
if self.hgWeights[i][1][0] not in fillerNames:
526
fillerNames.append(self.hgWeights[i][1][0])
527
528
if self.needNullFiller and self.nullSymbol not in fillerNames:
529
fillerNames.append(self.nullSymbol)
530
531
allFRbindings = []
532
for i in range(len(roleNames)):
533
for j in range(len(fillerNames)):
534
allFRbindings.append(fillerNames[j] + '/'+ roleNames[i])
535
536
weightMatrix = np.zeros((len(allFRbindings), len(allFRbindings)))
537
for i in range(len(self.hgWeights)):
538
index1 = allFRbindings.index(self.hgWeights[i][0][0] + '/' + self.hgWeights[i][0][1])
539
index2 = allFRbindings.index(self.hgWeights[i][1][0] + '/' + self.hgWeights[i][1][1])
540
weightMatrix[index1, index2] = self.hgWeights[i][2]
541
weightMatrix[index2, index1] = self.hgWeights[i][2]
542
543
biasVector = np.zeros((len(allFRbindings), 1))
544
for i in range(len(self.hgBiases)):
545
currentRole = self.hgBiases[i][0]
546
currentBias = self.hgBiases[i][1]
547
for j in range(len(allFRbindings)):
548
if allFRbindings[j][-len(currentRole):] == currentRole:
549
biasVector[j, 0] = currentBias
550
551
self.roleNames = roleNames
552
self.fillerNames = fillerNames
553
self.weightMatrix = weightMatrix
554
self.biasVector = biasVector
555
self.allFRbindings = allFRbindings
556
self.networkInfoIsSet = True
557
558
if self.isPcfg:
559
self.adjustBiases()
560
561
562
# -------------------------------------------------------------------------
563
# Getters and supporting functions
564
565
def getHarmony(self, state):
566
"""
567
Calculate harmony of state input as column vector.
568
"""
569
if isinstance(state, str):
570
stateVector = self.hnfTreeToState(state)
571
else:
572
stateVector = state
573
574
hWeight = np.dot(np.dot(np.transpose(stateVector), self.weightMatrix), stateVector)
575
hBias = np.dot(np.transpose(stateVector), self.biasVector)
576
577
return sum((0.5 * hWeight) + hBias)
578
579
def getRootNode(self, ruleSet):
580
"""
581
Given a dictionary of rules, find the first possible root node.
582
"""
583
return list(ruleSet.keys())[0] # first key in ruleSet is root node
584
585
def getTerminalNodes(self, ruleSet):
586
"""
587
Given a dictionary of rules, find all terminal symbols.
588
"""
589
terminals = []
590
rhSides = []
591
592
rhSides = ruleSet.keys()
593
594
terminals = []
595
for rhs in rhSides:
596
for lhs in ruleSet[rhs]:
597
for node in lhs:
598
if node not in rhSides and node not in terminals:
599
terminals.append(node) # must be a terminal
600
601
return terminals
602
603
def getNetworkInfo(self):
604
"""
605
Get the information needed to create a weight matrix for use in neural network
606
computation.
607
"""
608
if not self.networkInfoIsSet:
609
self.setNetworkInfo()
610
611
return self.roleNames, self.fillerNames, self.weightMatrix, self.biasVector
612
613
def getWeight(self, binding1, binding2):
614
"""
615
Get specific weight from weight matrix.
616
"""
617
binding1_isValid = binding1 in self.allFRbindings
618
binding2_isValid = binding2 in self.allFRbindings
619
if not binding1_isValid and not binding2_isValid:
620
sys.exit('Error: \'' + binding1 + '\' and \'' + binding2 + '\' are not valid filler/role bindings.')
621
elif not binding1_isValid:
622
sys.exit('Error: \'' + binding1 + '\' is not a valid filler/role binding.')
623
elif not binding2_isValid:
624
sys.exit('Error: \'' + binding2 + '\' is not a valid filler/role binding.')
625
else:
626
return self.weightMatrix[self.allFRbindings.index(binding1), self.allFRbindings.index(binding2)]
627
628
def getBias(self, binding):
629
"""
630
Get specific bias from bias vector.
631
"""
632
bindingIsValid = binding in self.allFRbindings
633
if not bindingIsValid:
634
sys.exit('Error: \'' + binding + '\' is not a valid filler/role binding.')
635
else:
636
return self.biasVector[self.allFRbindings.index(binding), 0]
637
638
639
# -------------------------------------------------------------------------
640
# Pretty 'toString' methods
641
642
def cnfRulesToString(self):
643
"""
644
Gets a pretty string for the CNF rules.
645
"""
646
nRules = 0
647
for lhs in self.cnfRules.keys():
648
nRules += len(self.cnfRules[lhs])
649
650
nStringified = 0
651
returnString = '{'
652
for lhs in self.cnfRules.keys():
653
for rhs in self.cnfRules[lhs]:
654
if nStringified != 0:
655
returnString += ' '
656
returnString += lhs + ' -> '
657
for i in range(len(rhs)):
658
if self.isPcfg and i == 0:
659
returnString += str(rhs[i])
660
else:
661
returnString += rhs[i]
662
if i != len(rhs) - 1:
663
returnString += ' '
664
if nStringified != nRules - 1:
665
returnString += '; \n'
666
nStringified += 1
667
returnString += '}'
668
669
return returnString
670
671
def hnfRulesToString(self):
672
"""
673
Gets a pretty string for the HNF rules.
674
"""
675
nRules = 0
676
for lhs in self.hnfRules.keys():
677
nRules += len(self.hnfRules[lhs])
678
679
nStringified = 0
680
returnString = '{'
681
for lhs in self.hnfRules.keys():
682
for rhs in self.hnfRules[lhs]:
683
if nStringified != 0:
684
returnString += ' '
685
returnString += lhs + ' -> '
686
for i in range(len(rhs)):
687
if self.isPcfg and i == 0:
688
returnString += str(rhs[i])
689
else:
690
returnString += rhs[i]
691
if i != len(rhs) - 1:
692
returnString += ' '
693
if nStringified != nRules - 1:
694
returnString += '; \n'
695
nStringified += 1
696
returnString += '}'
697
698
return returnString
699
700
def hgWeightsToString(self):
701
"""
702
Gets a pretty string for the HG weights.
703
"""
704
if not self.hgRulesAreSet:
705
self.setHarmonicGrammarRules()
706
707
returnString = '{'
708
for i in range(len(self.hgWeights)):
709
if i != 0:
710
returnString += ' '
711
returnString += '[(' + self.hgWeights[i][0][0] + '/'+ self.hgWeights[i][0][1] \
712
+ ', ' + self.hgWeights[i][1][0] + '/' + self.hgWeights[i][1][1] \
713
+ '), ' + str(self.hgWeights[i][-1]) + ']'
714
if i != len(self.hgWeights) - 1:
715
returnString += '; \n'
716
returnString += '}'
717
718
return returnString
719
720
def hgBiasesToString(self):
721
"""
722
Gets a pretty string for the HG biases.
723
"""
724
if not self.hgRulesAreSet:
725
self.setHarmonicGrammarRules()
726
727
returnString = '{'
728
for i in range(len(self.hgBiases)):
729
if i != 0:
730
returnString += ' '
731
returnString += '[' + self.hgBiases[i][0] + ', ' + str(self.hgBiases[i][1]) + ']'
732
if i != len(self.hgBiases) - 1:
733
returnString += '; \n'
734
returnString += '}'
735
736
return returnString
737
738
def hgRulesToString(self):
739
"""
740
Concatenates the HG weights and biases and gets a pretty string for them.
741
"""
742
if not self.hgRulesAreSet:
743
self.setHarmonicGrammarRules()
744
745
returnString = '{'
746
for i in range(len(self.hgWeights)):
747
if i != 0:
748
returnString += ' '
749
returnString += '[(' + self.hgWeights[i][0][0] + '/'+ self.hgWeights[i][0][1] \
750
+ ', ' + self.hgWeights[i][1][0] + '/' + self.hgWeights[i][1][1] \
751
+ '), ' + str(self.hgWeights[i][-1]) + '];\n'
752
for i in range(len(self.hgBiases)):
753
returnString += ' [' + self.hgBiases[i][0] + ', ' + str(self.hgBiases[i][1]) + ']'
754
if i != len(self.hgBiases) - 1:
755
returnString += '; \n'
756
returnString += '}'
757
758
return returnString
759
760
def biasVectorToString(self):
761
"""
762
Gets a pretty string for the bias vector.
763
"""
764
returnString = ''
765
for i in range(len(self.allFRbindings)):
766
returnString += self.allFRbindings[i] + ', ' + str(self.biasVector[i]) + '\n'
767
768
return returnString
769
770
771