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; | |