/*
 *  ******************************************************************************
 *  *
 *  *
 *  * This program and the accompanying materials are made available under the
 *  * terms of the Apache License, Version 2.0 which is available at
 *  * https://www.apache.org/licenses/LICENSE-2.0.
 *  *
 *  * See the NOTICE file distributed with this work for additional
 *  * information regarding copyright ownership.
 *  * Unless required by applicable law or agreed to in writing, software
 *  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 *  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 *  * License for the specific language governing permissions and limitations
 *  * under the License.
 *  *
 *  * SPDX-License-Identifier: Apache-2.0
 *  *****************************************************************************
 */

include "array.fbs";	//For FlatArray

namespace sd.graph;

/*
An "Event" is any value that may occur multiple times (score vs. iteration, or accuracy for example)
All events have timestamps and iteration/epoch.

Design: Given given FlatBuffers doesn't support polymorphism, each "frame" in our log file will comprise a sequence of (Event,X) pairs
"Event" is common information/header for the frame (i.e., determines type that follows and allows decoding), and can also be used for filtering - i.e., can skip the next entry

Alternatives:
1. Have a large Event class, with all but 1 of the 'type specific' fields being null (inefficient, especially for things like scalars)
2. Use pair of (ubyte,X) and have duplicate fields in every event subtype

Types of entries that can follow an Event:
array:FlatArray;				//Use standard/existing graph FlatArray class. Also used for scalars! (For types and also strings etc)
arrayList:[FlatArray];			//For TensorArray and the like
histogram:Histogram;			//Histogram class
image:Image;					//Could just use array/FlatArray, but idea is to store more efficiently here in compressed format
summaryStat:SummaryStatistics;	//One class for holding stuff like min/mean/max/stdev etc - more efficiently than a whole lot of separate scalar entries...
opTiming:FlatTiming;			//Timing/profiling information about a single op execution. Use existing FlatTiming, but maybe extend if required
hardwareState:HardwareState;	//Information about hardware at a specific point in time: CPU/GPU utilization, etc
*/


enum UIEventType:byte {
	ADD_NAME,					//Used to register a name (essentialy, Map<Integer,String>.put(i,name)), so it can be referred to by index later. Saves us encoding really long names in every single frame...
	SCALAR,
	ARRAY,
	ARRAY_LIST,
	HISTOGRAM,
	IMAGE,						//To be added later
	SUMMARY_STATISTICS,
	OP_TIMING,
	HARDWARE_STATE
}

/*
UIEventSubtype relates to what the metric is about. This determines where the value should be presented in the UI
For example, we can have scalars for: evaluation, tuning (mean magnitudes), performance (op runtime) etc.
But these should be presented in different sections of the UI.
It can also be thought of as the "semantic type of event" whereas UIEventType is the "data type of event"
*/
enum UIEventSubtype:byte {
	NONE,						//Not applicable (for example, ADD_NAME event type)
	EVALUATION,					//Train/test accuracy, etc
	LOSS,						//Value of the loss function (or any sub-component there-of, such as L2)
	LEARNING_RATE,				//Learning rate
	TUNING_METRIC,				//Metrics like: parameter:update ratio, or parameter and gradient histograms, etc
	PERFORMANCE,				//Global performance metrics - batches/sec, epoch time, etc
	PROFILING,					//Op profiling/performance metrics - how long to run each op, etc
	FEATURE_LABEL,				//Feature/input - for visualization, debugging, etc
	PREDICTION,					//Network prediction/output
	USER_CUSTOM					//Custom, user-defined metric or value
}

table UIEvent {
    eventType:UIEventType;		//Type of the event that follows
	eventSubType:UIEventSubtype;//Subtype of event that follows
	nameIdx:int;				//Integer representing the previously registered name of the event - for example, 0=="accuracy", 1=="score", 2=="weights" etc as previously registered
	timestamp:long;
	iteration:int;
	epoch:int;
	variableId:int16;			//Number of the variable. Optional (-1 == not applicable)
	frameIter:FrameIteration;	//Optional - some events have a corresponding frame/iteration
	plugin:uint16;				//An ID number - what UI page/plugin should be used to render this information? (Allows for extensibility, separation of data for different UI components)
}

//Optional, often null. Used for events that have an associated frame/iteration (like array values in a loop)
table FrameIteration {
	frame:string;
	iteration:uint16;
}

//Used to register a name (essentialy, Map<Integer,String>.put(i,name)), so it can be referred to by index later. Saves us encoding really long names in every single frame...
table UIAddName {
	nameIdx:int;
	name:string;
}

//A simple list of arrays
table FlatArrayList {
	list:[FlatArray];
}

enum UIHistogramType:byte {
	DISCRETE,
	EQUAL_SPACING,		//use min/max + num bins to determine where 
	CUSTOM
}

table UIHistogram {
	type:UIHistogramType;
	numbins:uint32;
	binranges:FlatArray;	//Shape [2] for EQUAL_SPACING (min/max), or shape [2,numbins] for custom bin min/max values
	y:FlatArray;			//Shape [numbins] - could be integer or floating point, positive or negative
	binlabels:[string];		//Optional - used for discrete histograms (essentially, bar chart)	//TODO might want to register this value once + reuse?
}

table UISummaryStatistics {
	bitmask:uint32;			//Bit mask that represents which of the primitives are actually present (FB doesn't support null primitives AFAIK)
	min:FlatArray;			//Typed - but it would be more space efficient to use double...
	max:FlatArray;
	mean:double;
	stdev:double;
	countzero:long;
	countpositive:long;
	countnegative:long;
	countnan:long;
	countinf:long;
}

//Standard metrics related to current hardware status
table UIHardwareState {
	//TODO - do we want CPU/GPU utilization statistics and the like?
	gpuMemory:[long];
	hostMemory:long;
}