func TestSimpleDateEncoding(t *testing.T) { p := NewDateEncoderParams() p.SeasonWidth = 3 p.DayOfWeekWidth = 1 p.WeekendWidth = 3 p.TimeOfDayWidth = 5 de := NewDateEncoder(p) // season is aaabbbcccddd (1 bit/month) TODO should be <<3? // should be 000000000111 (centered on month 11 - Nov) seasonExpected := utils.Make1DBool([]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}) // week is SMTWTFS // differs from python implementation dayOfWeekExpected := utils.Make1DBool([]int{0, 0, 0, 0, 1, 0, 0}) // not a weekend, so it should be "False" weekendExpected := utils.Make1DBool([]int{1, 1, 1, 0, 0, 0}) // time of day has radius of 4 hours and w of 5 so each bit = 240/5 min = 48min // 14:55 is minute 14*60 + 55 = 895; 895/48 = bit 18.6 // should be 30 bits total (30 * 48 minutes = 24 hours) timeOfDayExpected := utils.Make1DBool([]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}) d := time.Date(2010, 11, 4, 14, 55, 0, 0, time.UTC) encoded := de.Encode(d) t.Log(utils.Bool2Int(encoded)) expected := append(seasonExpected, dayOfWeekExpected...) expected = append(expected, weekendExpected...) expected = append(expected, timeOfDayExpected...) assert.Equal(t, utils.Bool2Int(expected), utils.Bool2Int(encoded)) }
func TestWideEncoding(t *testing.T) { p := NewScalerEncoderParams(5, 0, 24) p.Periodic = true //p.Verbosity = 5 p.Radius = 4 e := NewScalerEncoder(p) encoded := e.Encode(14.916666666666666, false) t.Log(encoded) expected := utils.Make1DBool([]int{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}) assert.True(t, len(encoded) == 30) assert.Equal(t, utils.Bool2Int(expected), utils.Bool2Int(encoded)) }
func TestNarrowEncoding(t *testing.T) { p := NewScalerEncoderParams(3, 0, 1) p.Periodic = false //p.Verbosity = 5 p.Radius = 1 e := NewScalerEncoder(p) encoded := make([]bool, 6) e.EncodeToSlice(0, false, encoded) t.Log(encoded) expected := utils.Make1DBool([]int{1, 1, 1, 0, 0, 0}) assert.True(t, len(encoded) == 6) assert.Equal(t, utils.Bool2Int(expected), utils.Bool2Int(encoded)) }
/* Decode an encoded sequence. Returns range of values */ func (se *ScalerEncoder) Decode(encoded []bool) []utils.TupleFloat { if !utils.AnyTrue(encoded) { return []utils.TupleFloat{} } tmpOutput := encoded[:se.N] // First, assume the input pool is not sampled 100%, and fill in the // "holes" in the encoded representation (which are likely to be present // if this is a coincidence that was learned by the SP). // Search for portions of the output that have "holes" maxZerosInARow := se.halfWidth for i := 0; i < maxZerosInARow; i++ { searchSeq := make([]bool, i+3) subLen := len(searchSeq) searchSeq[0] = true searchSeq[subLen-1] = true if se.Periodic { for j := 0; j < se.N; j++ { outputIndices := make([]int, subLen) for idx, _ := range outputIndices { outputIndices[idx] = (j + idx) % se.N } if utils.BoolEq(searchSeq, utils.SubsetSliceBool(tmpOutput, outputIndices)) { utils.SetIdxBool(tmpOutput, outputIndices, true) } } } else { for j := 0; j < se.N-subLen+1; j++ { if utils.BoolEq(searchSeq, tmpOutput[j:j+subLen]) { utils.FillSliceRangeBool(tmpOutput, true, j, subLen) } } } } if se.Verbosity >= 2 { fmt.Println("raw output:", utils.Bool2Int(encoded[:se.N])) fmt.Println("filtered output:", utils.Bool2Int(tmpOutput)) } // ------------------------------------------------------------------------ // Find each run of 1's in sequence nz := utils.OnIndices(tmpOutput) //key = start index, value = run length runs := make([]utils.TupleInt, 0, len(nz)) runStart := -1 runLen := 0 for idx, val := range tmpOutput { if val { //increment or new idx if runStart == -1 { runStart = idx runLen = 0 } runLen++ } else { if runStart != -1 { runs = append(runs, utils.TupleInt{runStart, runLen}) runStart = -1 } } } if runStart != -1 { runs = append(runs, utils.TupleInt{runStart, runLen}) runStart = -1 } // If we have a periodic encoder, merge the first and last run if they // both go all the way to the edges if se.Periodic && len(runs) > 1 { if runs[0].A == 0 && runs[len(runs)-1].A+runs[len(runs)-1].B == se.N { runs[len(runs)-1].B += runs[0].B runs = runs[1:] } } // ------------------------------------------------------------------------ // Now, for each group of 1's, determine the "left" and "right" edges, where // the "left" edge is inset by halfwidth and the "right" edge is inset by // halfwidth. // For a group of width w or less, the "left" and "right" edge are both at // the center position of the group. ranges := make([]utils.TupleFloat, 0, len(runs)+2) for _, val := range runs { var left, right int start := val.A length := val.B if length <= se.Width { right = start + length/2 left = right } else { left = start + se.halfWidth right = start + length - 1 - se.halfWidth } var inMin, inMax float64 // Convert to input space. if !se.Periodic { inMin = float64(left-se.padding)*se.Resolution + se.MinVal inMax = float64(right-se.padding)*se.Resolution + se.MinVal } else { inMin = float64(left-se.padding)*se.Range/float64(se.nInternal) + se.MinVal inMax = float64(right-se.padding)*se.Range/float64(se.nInternal) + se.MinVal } // Handle wrap-around if periodic if se.Periodic { if inMin >= se.MaxVal { inMin -= se.Range inMax -= se.Range } } // Clip low end if inMin < se.MinVal { inMin = se.MinVal } if inMax < se.MinVal { inMax = se.MinVal } // If we have a periodic encoder, and the max is past the edge, break into // 2 separate ranges if se.Periodic && inMax >= se.MaxVal { ranges = append(ranges, utils.TupleFloat{inMin, se.MaxVal}) ranges = append(ranges, utils.TupleFloat{se.MinVal, inMax - se.Range}) } else { //clip high end if inMax > se.MaxVal { inMax = se.MaxVal } if inMin > se.MaxVal { inMin = se.MaxVal } ranges = append(ranges, utils.TupleFloat{inMin, inMax}) } } //desc := se.generateRangeDescription(ranges) return ranges }