2
2
3
3
import rwc from 'random-weighted-choice' ;
4
4
import Tokenizer from 'sentence-tokenizer' ;
5
- import { ConceptNetwork , addLink , addNode , incrementBeginning , incrementEnd , incrementMiddle , getLinksFrom , getLinksTo , getNodeIndex } from '@ector/concept-network' ;
6
- import { ConceptNetworkState , activate , propagate , getActivationValue } from '@ector/state' ;
5
+ import {
6
+ ConceptNetwork ,
7
+ addLink ,
8
+ addNode ,
9
+ incrementBeginning ,
10
+ incrementEnd ,
11
+ incrementMiddle ,
12
+ getLinksFrom ,
13
+ getLinksTo ,
14
+ getNodeIndex ,
15
+ } from '@ector/concept-network' ;
16
+ import {
17
+ ConceptNetworkState ,
18
+ activate ,
19
+ propagate ,
20
+ getActivationValue ,
21
+ getActivatedTypedNodes ,
22
+ } from '@ector/state' ;
7
23
8
24
/**
9
25
* @typedef {Object<string, any> } ECTOR
@@ -77,7 +93,10 @@ export function addEntry(ector, entry) {
77
93
cn = addLink ( cn , prevTokenLabel , tokenLabel ) ;
78
94
}
79
95
prevTokenLabel = tokenLabel ;
80
- return { tokenLabels : [ ...tokenLabels , tokenLabel ] , prevTokenLabel } ;
96
+ return {
97
+ tokenLabels : [ ...tokenLabels , tokenLabel ] ,
98
+ prevTokenLabel,
99
+ } ;
81
100
} ,
82
101
{ tokenLabels : [ ] , prevTokenLabel : null } ,
83
102
) ;
@@ -115,23 +134,29 @@ function choseToken(state, temperature) {
115
134
const tokens = Object . keys ( state )
116
135
. filter ( label => label . startsWith ( 'w' ) )
117
136
. filter ( label => state [ label ] . value >= maxActivationValue - 10 ) ;
118
- const toChoose = tokens . map ( label => ( { weight : state [ label ] . value , id : label } ) ) ;
137
+ const toChoose = tokens . map ( label => ( {
138
+ weight : state [ label ] . value ,
139
+ id : label ,
140
+ } ) ) ;
119
141
const chosenNode = rwc ( toChoose , temperature ) ;
120
142
return chosenNode ;
121
143
}
122
144
123
145
/**
124
- * Generate the end of a sentence, adding tokens to the list of token
125
- * nodes in phrase.
126
- *
127
- * @param {ConceptNetwork } cn Network of tokens
128
- * @param {ConceptNetworkState } cns State of the network (activation values)
129
- * @param {{ id: string, weight: number }[] } phraseNodes array of token nodes
130
- * @param {number } temperature
131
- * @returns {{ id: string, weight: number }[] } array of token nodes (end of phrase)
132
- **/
146
+ * Generate the end of a sentence, adding tokens to the list of token
147
+ * nodes in phrase.
148
+ *
149
+ * @param {ConceptNetwork } cn Network of tokens
150
+ * @param {ConceptNetworkState } cns State of the network (activation values)
151
+ * @param {{ id: string, weight: number }[] } phraseNodes array of token nodes
152
+ * @param {number } temperature
153
+ * @returns {{ id: string, weight: number }[] } array of token nodes (end of phrase)
154
+ **/
133
155
function generateForwards ( cn , cns , phraseNodes , temperature ) {
134
- const outgoingLinks = getLinksFrom ( cn , phraseNodes [ phraseNodes . length - 1 ] . id )
156
+ const outgoingLinks = getLinksFrom (
157
+ cn ,
158
+ phraseNodes [ phraseNodes . length - 1 ] . id ,
159
+ ) ;
135
160
136
161
/**
137
162
* @ignore
@@ -140,24 +165,31 @@ function generateForwards(cn, cns, phraseNodes, temperature) {
140
165
const toNode = cn . node [ link . to ] ;
141
166
// When toNode is a word token
142
167
if ( toNode . label . startsWith ( 'w' ) ) {
143
- const activationValue = Math . max ( getActivationValue ( cns , toNode . label ) , 1 ) ;
144
- const repeatNb = phraseNodes . filter ( ( { id } ) => id === toNode . label ) . length ;
168
+ const activationValue = Math . max (
169
+ getActivationValue ( cns , toNode . label ) ,
170
+ 1 ,
171
+ ) ;
172
+ const repeatNb = phraseNodes . filter ( ( { id } ) => id === toNode . label )
173
+ . length ;
145
174
const len = toNode . label . length ;
146
175
// If the node is not present more than ~3 times
147
176
if ( repeatNb * len <= 5 * 3 ) {
148
177
const repetition = 1 + repeatNb * repeatNb * len ;
149
- return [ ...nodes , {
150
- id : toNode . label ,
151
- weight : link . coOcc * activationValue / repetition
152
- } ] ;
178
+ return [
179
+ ...nodes ,
180
+ {
181
+ id : toNode . label ,
182
+ weight : ( link . coOcc * activationValue ) / repetition ,
183
+ } ,
184
+ ] ;
153
185
}
154
186
}
155
187
return [ ...nodes ] ;
156
- } , [ ] )
188
+ } , [ ] ) ;
157
189
158
190
// Stop condition
159
191
if ( nextNodes . length === 0 ) {
160
- return phraseNodes ;
192
+ return phraseNodes ;
161
193
}
162
194
// Choose one node among the tokens following the one at the end of the
163
195
// phrase
@@ -166,46 +198,59 @@ function generateForwards(cn, cns, phraseNodes, temperature) {
166
198
const chosenTokenNode = { id : cn . node [ chosenItemIndex ] . label , weight : - 1 } ;
167
199
168
200
// Recursively generate the remaining of the phrase
169
- return generateForwards ( cn , cns , [ ...phraseNodes , chosenTokenNode ] , temperature ) ;
201
+ return generateForwards (
202
+ cn ,
203
+ cns ,
204
+ [ ...phraseNodes , chosenTokenNode ] ,
205
+ temperature ,
206
+ ) ;
170
207
}
171
208
172
209
/**
173
210
* Generate the begining of a sentence, adding tokens to the list of token
174
211
* nodes in phrase.
175
212
*
176
- * @param {ConceptNetwork } cn Network of tokens
177
- * @param {ConceptNetworkState } cns State of the network (activation values)
178
- * @param {{ id: string, weight: number }[] } phraseNodes array of token nodes
179
- * @param {number } temperature
180
- * @returns {{ id: string, weight: number }[] } array of token nodes (end of phrase)
213
+ * @param {ConceptNetwork } cn Network of tokens
214
+ * @param {ConceptNetworkState } cns State of the network (activation values)
215
+ * @param {{ id: string, weight: number }[] } phraseNodes array of token nodes
216
+ * @param {number } temperature
217
+ * @returns {{ id: string, weight: number }[] } array of token nodes (end of phrase)
181
218
**/
182
219
function generateBackwards ( cn , cns , phraseNodes , temperature ) {
183
- const incomingLinks = getLinksTo ( cn , phraseNodes [ 0 ] . id )
220
+ const incomingLinks = getLinksTo ( cn , phraseNodes [ 0 ] . id ) ;
184
221
/**
185
222
* @ignore
186
223
* @type Array<{ id: string, weight: number }> */
187
224
const previousNodes = incomingLinks . reduce ( ( nodes , link ) => {
188
225
const fromNode = cn . node [ link . from ] ;
189
226
// When fromNode is a word token
190
227
if ( fromNode . label . startsWith ( 'w' ) ) {
191
- const activationValue = Math . max ( getActivationValue ( cns , fromNode . label ) , 1 ) ;
192
- const repeatNb = phraseNodes . filter ( ( { id } ) => id === fromNode . label ) . length ;
228
+ const activationValue = Math . max (
229
+ getActivationValue ( cns , fromNode . label ) ,
230
+ 1 ,
231
+ ) ;
232
+ const repeatNb = phraseNodes . filter (
233
+ ( { id } ) => id === fromNode . label ,
234
+ ) . length ;
193
235
const len = fromNode . label . length ;
194
236
// If the node is not present more than ~3 times
195
237
if ( repeatNb * len <= 5 * 3 ) {
196
238
const repetition = 1 + repeatNb * repeatNb * len ;
197
- return [ ...nodes , {
198
- id : fromNode . label ,
199
- weight : link . coOcc * activationValue / repetition
200
- } ] ;
239
+ return [
240
+ ...nodes ,
241
+ {
242
+ id : fromNode . label ,
243
+ weight : ( link . coOcc * activationValue ) / repetition ,
244
+ } ,
245
+ ] ;
201
246
}
202
247
}
203
248
return [ ...nodes ] ;
204
249
} , [ ] ) ;
205
250
206
251
// Stop condition
207
252
if ( previousNodes . length === 0 ) {
208
- return phraseNodes ;
253
+ return phraseNodes ;
209
254
}
210
255
// Choose one node among the tokens following the one at the end of the
211
256
// phrase
@@ -214,7 +259,12 @@ function generateBackwards(cn, cns, phraseNodes, temperature) {
214
259
const chosenTokenNode = { id : cn . node [ chosenItemIndex ] . label , weight : - 1 } ;
215
260
216
261
// Recursively generate the remaining of the phrase
217
- return generateBackwards ( cn , cns , [ chosenTokenNode , ...phraseNodes ] , temperature ) ;
262
+ return generateBackwards (
263
+ cn ,
264
+ cns ,
265
+ [ chosenTokenNode , ...phraseNodes ] ,
266
+ temperature ,
267
+ ) ;
218
268
}
219
269
220
270
/**
@@ -248,7 +298,7 @@ export function generateResponse(ector) {
248
298
...ector ,
249
299
cns,
250
300
response,
251
- responseLabels : responseItems . map ( ( { id } ) => id )
301
+ responseLabels : responseItems . map ( ( { id } ) => id ) ,
252
302
} ) ;
253
303
}
254
304
@@ -266,8 +316,8 @@ export function linkNodesToLastSentence(ector, nodeLabels = []) {
266
316
const cn = nodeLabels . reduce ( ( cn , nodeLabel ) => {
267
317
// QUESTION: is it the right direction(?)
268
318
return addLink ( cn , nodeLabel , ector . lastSentenceLabel ) ;
269
- } , ector . cn )
270
- return Object . freeze ( { ...ector , cn } )
319
+ } , ector . cn ) ;
320
+ return Object . freeze ( { ...ector , cn } ) ;
271
321
}
272
322
273
323
/**
@@ -279,3 +329,33 @@ export function linkNodesToLastSentence(ector, nodeLabels = []) {
279
329
export function getResponse ( ector ) {
280
330
return ector . response || '' ;
281
331
}
332
+
333
+ /**
334
+ * Activates the nodes of the entry in @about state and returns ECTOR and the
335
+ * array of activated nodes.
336
+ *
337
+ * @param {ECTOR } ector
338
+ * @param {string } entry
339
+ * returns {ECTOR}
340
+ */
341
+ export function about ( ector , entry ) {
342
+ const tokenizer = new Tokenizer ( '@about' , 'ECTOR' ) ;
343
+ tokenizer . setEntry ( entry ) ;
344
+ const sentences = tokenizer . getSentences ( ) ;
345
+ const tokens = tokenizer . getTokens ( 0 ) ;
346
+
347
+ let state = sentences . reduce ( ( cns , sentence ) => {
348
+ return activate ( cns , `s${ sentence } ` ) ;
349
+ } , { } ) ;
350
+ state = tokens . reduce ( ( cns , token ) => {
351
+ return activate ( cns , `w${ token } ` ) ;
352
+ } , state ) ;
353
+ state = propagate ( ector . cn , state ) ;
354
+ state = propagate ( ector . cn , state ) ;
355
+ const newEctor = {
356
+ ...ector ,
357
+ username : '@about' ,
358
+ cns : { ...ector . cns , '@about' : state } ,
359
+ } ;
360
+ return newEctor ;
361
+ }
0 commit comments