Spaces:
Sleeping
Sleeping
const {pick} = require("lodash"); | |
const Config = require("./config.js"); | |
class Node { | |
constructor (s_note, c_note) { | |
this.s_note = s_note; | |
this.c_note = c_note; | |
console.assert(this.s_note.softIndex != null, "s_note softIndex is null"); | |
this.offset = this.s_note.softIndex - this.c_note.softIndex; | |
this._prev = null; | |
this._totalCost = 0; | |
this._value = 0; | |
this.cacheDirty = true; | |
//this.evaluatePrev(Node.Zero); | |
} | |
get prev () { | |
return this._prev; | |
} | |
set prev (value) { | |
if (value != this._prev) { | |
this._prev = value; | |
this.cacheDirty = true; | |
} | |
} | |
get si () { | |
return this.s_note.index; | |
} | |
get ci () { | |
return this.c_note.index; | |
} | |
get root () { | |
return this.prev.root || this; | |
} | |
get rootSi () { | |
return !this.prev.zero ? this.prev.rootSi : this.si; | |
} | |
get id () { | |
return `${this.s_note.index},${this.c_note.index}`; | |
} | |
static cost (prev, skip, self) { | |
return prev * Config.CostStepAttenuation + Math.tanh(skip * Config.SkipCost) + Math.tanh(self * 0.5); | |
} | |
updateCache () { | |
if (this.cacheDirty) { | |
this._totalCost = Node.cost(this.prev.totalCost, this.si - this.prev.si - 1, this.selfCost); | |
this._value = this.prev.value + 1 - Math.tanh(this.selfCost * 0.5); | |
this.cacheDirty = false; | |
} | |
} | |
get totalCost () { | |
this.updateCache(); | |
return this._totalCost; | |
} | |
get value () { | |
this.updateCache(); | |
return this._value; | |
} | |
get deep () { | |
return this.prev.deep + 1; | |
} | |
get path () { | |
const path = []; | |
for (let node = this; !node.zero; node = node.prev) { | |
path[node.si] = node.ci; | |
} | |
for (let i = 0; i < path.length; ++i) | |
if (typeof path[i] != "number") | |
path[i] = -1; | |
return path; | |
} | |
dump () { | |
return pick(this, ["id", "si", "ci", "rootSi", "value", "deep", "rootSi", "offset", "prior", "selfCost", "totalCost"]); | |
} | |
evaluatePrev (node) { | |
const cost = this.evaluatePrevCost(node); | |
console.assert(this.si - node.si >= 1, "node index error:", this, node/*, {get [Symbol.toStringTag]() {debugger}}*/); | |
//if (this.si - node.si < 1) | |
// debugger; | |
const totalCost = Node.cost(node.totalCost, this.si - node.si - 1, cost); | |
if (!this.prev || totalCost < this.totalCost) { | |
this.prev = node; | |
this.selfCost = cost; | |
return true; | |
} | |
return false; | |
} | |
evaluatePrevCost (node) { | |
let cost = 0; | |
if (node.offset != null) { | |
const bias = this.offset - node.offset; | |
const costCoeff = node.zero ? Config.ZeroOffsetCost : (bias > 0 ? Config.LagOffsetCost : Config.LeadOffsetCost); | |
cost += (bias * costCoeff) ** 2; | |
} | |
return cost; | |
} | |
priorByOffset (offset) { | |
const distance = Math.abs(this.offset - offset) / 1;//(this.s_note.deltaSi + 0.04); | |
return Math.tanh(this.value * Config.PriorValueSigmoidFactor) - Math.tanh(distance * Config.PriorDistanceSigmoidFactor); | |
//return Math.log(this.value) * Math.tanh(4 / distance); | |
//return this.value - distance; | |
} | |
static zero () { | |
return { | |
zero: true, | |
totalCost: 0, | |
value: 0, | |
si: -1, | |
ci: -1, | |
deep: 0, | |
offset: 0, | |
}; | |
} | |
}; | |
module.exports = Node; | |