/* ---------------------------------------------------------------------
* Numenta Platform for Intelligent Computing (NuPIC)
* Copyright (C) 2014, Numenta, Inc. Unless you have an agreement
* with Numenta, Inc., for a separate license for this software code, the
* following terms and conditions apply:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero Public License for more details.
*
* You should have received a copy of the GNU Affero Public License
* along with this program. If not, see http://www.gnu.org/licenses.
*
* http://numenta.org/licenses/
* ---------------------------------------------------------------------
*/
package org.numenta.nupic.network.sensor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import org.numenta.nupic.FieldMetaType;
import org.numenta.nupic.util.Tuple;
/**
* Meta data describing the fields, types and reset
* rules usually specified by a file header, but may
* be manually or programmatically set.
*
* @author David Ray
*/
public class Header implements ValueList, Serializable {
private static final long serialVersionUID = 1L;
private ValueList rawTupleList;
/** Name of each field */
private List<String> fieldNames;
/** Field data types */
private List<FieldMetaType> fieldMeta;
/** Processing flags and hints */
private List<SensorFlags> sensorFlags;
private boolean isChanged;
private boolean isLearn = true;
private int[] resetIndexes;
private int[] seqIndexes;
@SuppressWarnings("unused")
private int[] tsIndexes;
private int[] learnIndexes;
@SuppressWarnings("unused")
private int[] categoryIndexes;
private List<String> sequenceCache;
/**
* Constructs a new {@code Header} using the specified
* {@link ValueList}
*
* @param input 3 rows of data describing the input
*/
public Header(ValueList input) {
if(input.size() != 3) {
throw new IllegalArgumentException("Input did not have 3 rows");
}
this.rawTupleList = input;
this.fieldNames = input.getRow(0).all().stream().map(o -> o.toString()).collect(Collectors.toList());
this.fieldMeta = input.getRow(1).all().stream().map(FieldMetaType::fromString).collect(Collectors.toList());
this.sensorFlags = input.getRow(2).all().stream().map(SensorFlags::fromString).collect(Collectors.toList());
initIndexes();
}
/**
* Retrieves the header line specified.
*/
@Override
public Tuple getRow(int index) {
return rawTupleList.getRow(index);
}
/**
* Returns the number of configuration lines contained.
* WARNING: Must be size == 3
*/
@Override
public int size() {
return rawTupleList.size();
}
/**
* Returns the header line containing the field names.
* @return
*/
public List<String> getFieldNames() {
return fieldNames;
}
/**
* Returns the header line containing the field types.
* @return
*/
public List<FieldMetaType> getFieldTypes() {
return fieldMeta;
}
/**
* Returns the header line ({@link List}) containing the
* control flags (in the 3rd line) which designate control
* operations such as turning learning on/off and resetting
* the state of an algorithm.
*
* @return
*/
public List<SensorFlags> getFlags() {
return sensorFlags;
}
/**
* Returns a flag indicating whether any watched column
* has changed data.
*
* @return
*/
public boolean isReset() {
return isChanged;
}
/**
* Returns a flag indicating whether the current input state
* is set to learn or not.
*
* @return
*/
public boolean isLearn() {
return isLearn;
}
/**
* Processes the current line of input and sets flags based on
* sensor flags formed by the 3rd line of a given header.
*
* @param input
*/
void process(String[] input) {
isChanged = false;
if(resetIndexes.length > 0) {
for(int i : resetIndexes) {
if(Integer.parseInt(input[i].trim()) == 1) {
isChanged = true; break;
}else{
isChanged = false;
}
}
}
if(learnIndexes.length > 0) {
for(int i : learnIndexes) {
if(Integer.parseInt(input[i].trim()) == 1) {
isLearn = true; break;
}else{
isLearn = false;
}
}
}
// Store lines in cache to detect when current input is a change.
if(seqIndexes.length > 0) {
boolean sequenceChanged = false;
if(sequenceCache.isEmpty()) {
for(int i : seqIndexes) {
sequenceCache.add(input[i]);
}
}else{
int idx = 0;
for(int i : seqIndexes) {
if(!sequenceCache.get(idx).equals(input[i])) {
sequenceCache.set(idx, input[i]);
sequenceChanged = true;
}
}
}
isChanged |= sequenceChanged;
}
}
/**
* Initializes the indexes of {@link SensorFlags} types to aid
* in line processing.
*/
private void initIndexes() {
int idx = 0;
List<Integer> tList = new ArrayList<>();
List<Integer> rList = new ArrayList<>();
List<Integer> cList = new ArrayList<>();
List<Integer> sList = new ArrayList<>();
List<Integer> lList = new ArrayList<>();
for(SensorFlags sf : sensorFlags) {
switch(sf) {
case T: tList.add(idx);break;
case R: rList.add(idx);break;
case C: cList.add(idx);break;
case S: sList.add(idx);break;
case L: lList.add(idx);break;
default:
}
idx++;
}
// Add + 1 to each to offset for Sensor insertion of sequence number in all row headers.
resetIndexes = rList.stream().mapToInt((Integer i) -> i.intValue() + 1).toArray();
seqIndexes = sList.stream().mapToInt((Integer i) -> i.intValue() + 1).toArray();
categoryIndexes = cList.stream().mapToInt((Integer i) -> i.intValue() + 1).toArray();
tsIndexes = tList.stream().mapToInt((Integer i) -> i.intValue() + 1).toArray();
learnIndexes = lList.stream().mapToInt((Integer i) -> i.intValue() + 1).toArray();
if(seqIndexes.length > 0) {
sequenceCache = new ArrayList<>();
}
}
}