diff --git a/.gitignore b/.gitignore index 696ea9b..2bd9546 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ PandaVis/guiValues.ini build/* dist/* *.egg-info/* +*.log +PandaVis/screenshots/* +HotgymExample/bakedDatabase/*/*.dump +HotgymExample/bakedDatabase/*.db \ No newline at end of file diff --git a/PandaVis/pandaComm/__init__.py b/HotgymExample/bakedDatabase/dummy.txt similarity index 100% rename from PandaVis/pandaComm/__init__.py rename to HotgymExample/bakedDatabase/dummy.txt diff --git a/HotgymExample/hotgym.py b/HotgymExample/hotgym.py index 9e3b080..c935712 100644 --- a/HotgymExample/hotgym.py +++ b/HotgymExample/hotgym.py @@ -2,12 +2,12 @@ import datetime import os import numpy as np -import random import math # Panda vis -from PandaVis.pandaComm.server import PandaServer -from PandaVis.pandaComm.dataExchange import ServerData, dataHTMObject, dataLayer, dataInput +from pandaBaker.pandaBaker import PandaBaker +from pandaBaker.pandaBaker import cLayer, cInput, cDataStream + from htm.bindings.sdr import SDR, Metrics from htm.encoders.rdse import RDSE, RDSE_Parameters @@ -23,6 +23,8 @@ _EXAMPLE_DIR = os.path.dirname(os.path.abspath(__file__)) _INPUT_FILE_PATH = os.path.join(_EXAMPLE_DIR, "gymdata.csv") +BAKE_DATABASE_FILE_PATH = os.path.join(os.getcwd(),'bakedDatabase','hotgym.db') + default_parameters = { # there are 2 (3) encoders: "value" (RDSE) & "time" (DateTime weekend, timeOfDay) 'enc': { @@ -57,7 +59,7 @@ } } -pandaServer = PandaServer() +pandaBaker = PandaBaker(BAKE_DATABASE_FILE_PATH) def main(parameters=default_parameters, argv=None, verbose=True): if verbose: @@ -133,12 +135,18 @@ def main(parameters=default_parameters, argv=None, verbose=True): predictor = Predictor(steps=[1, 5], alpha=parameters["predictor"]['sdrc_alpha']) predictor_resolution = 1 + BuildPandaSystem(sp,tm, parameters["enc"]["value"]["size"],dateEncoder.size) + # Iterate through every datum in the dataset, record the inputs & outputs. inputs = [] anomaly = [] anomalyProb = [] predictions = {1: [], 5: []} iterationNo = 0 + + dateBits_last = None + consBits_last = None + for count, record in enumerate(records): # Convert date string into Python date object. @@ -168,45 +176,10 @@ def main(parameters=default_parameters, argv=None, verbose=True): tm.activateDendrites(True) predictiveCellsSDR = tm.getPredictiveCells() - pandaServer.currentIteration = iterationNo # update server's iteration number - # do not update if we are running GOTO iteration command - if (not pandaServer.cmdGotoIteration or ( - pandaServer.cmdGotoIteration and pandaServer.gotoIteration == pandaServer.currentIteration)): - # ------------------HTMpandaVis---------------------- - # fill up values - serverData.iterationNo = pandaServer.currentIteration - - serverData.HTMObjects["HTM1"].inputs["SL_Consumption"].stringValue = "consumption: {:.2f}".format(consumption) - serverData.HTMObjects["HTM1"].inputs["SL_Consumption"].bits = consumptionBits.sparse - serverData.HTMObjects["HTM1"].inputs["SL_Consumption"].count = consumptionBits.size - - serverData.HTMObjects["HTM1"].inputs["SL_TimeOfDay"].stringValue = record[0] - serverData.HTMObjects["HTM1"].inputs["SL_TimeOfDay"].bits = dateBits.sparse - serverData.HTMObjects["HTM1"].inputs["SL_TimeOfDay"].count = dateBits.size - - serverData.HTMObjects["HTM1"].layers["SensoryLayer"].activeColumns = activeColumns.sparse - serverData.HTMObjects["HTM1"].layers["SensoryLayer"].winnerCells = tm.getWinnerCells().sparse - serverData.HTMObjects["HTM1"].layers["SensoryLayer"].predictiveCells = predictiveCellsSDR.sparse - - pandaServer.serverData = serverData - - pandaServer.spatialPoolers["HTM1"] = sp - pandaServer.temporalMemories["HTM1"] = tm - pandaServer.NewStateDataReady() - - print("One step finished") - pandaServer.BlockExecution() - print("Proceeding one step...") - - # ------------------HTMpandaVis---------------------- - tm.activateCells(activeColumns, True) tm_info.addData(tm.getActiveCells().flatten()) - activeCells = tm.getActiveCells() - print("ACTIVE" + str(len(activeCells.sparse))) - # Predict what will happen, and then train the predictor based on what just happened. pdf = predictor.infer(tm.getActiveCells()) for n in (1, 5): @@ -217,15 +190,63 @@ def main(parameters=default_parameters, argv=None, verbose=True): rawAnomaly = Anomaly.calculateRawAnomaly(activeColumns, tm.cellsToColumns(predictiveCellsSDR)) - print("rawAnomaly:" + str(rawAnomaly)) + anomalyLikelihood = anomaly_history.anomalyProbability(consumption, rawAnomaly) # need to use different calculation as we are not calling sp.compute(..) anomaly.append(rawAnomaly) anomalyProb.append(anomalyLikelihood) predictor.learn(count, tm.getActiveCells(), int(consumption / predictor_resolution)) + # ------------------HTMpandaVis---------------------- + # fill up values + + pandaBaker.inputs["Consumption"].stringValue = "consumption: {:.2f}".format(consumption) + pandaBaker.inputs["Consumption"].bits = consumptionBits.sparse + + pandaBaker.inputs["TimeOfDay"].stringValue = record[0] + pandaBaker.inputs["TimeOfDay"].bits = dateBits.sparse + + pandaBaker.layers["Layer1"].activeColumns = activeColumns.sparse + pandaBaker.layers["Layer1"].winnerCells = tm.getWinnerCells().sparse + pandaBaker.layers["Layer1"].predictiveCells = predictiveCellsSDR.sparse + pandaBaker.layers["Layer1"].activeCells = tm.getActiveCells().sparse + + # customizable datastreams to be show on the DASH PLOTS + pandaBaker.dataStreams["rawAnomaly"].value = rawAnomaly + pandaBaker.dataStreams["powerConsumption"].value = consumption + pandaBaker.dataStreams["numberOfWinnerCells"].value = len(tm.getWinnerCells().sparse) + pandaBaker.dataStreams["numberOfPredictiveCells"].value = len(predictiveCellsSDR.sparse) + pandaBaker.dataStreams["consumptionInput_sparsity"].value = consumptionBits.getSparsity() + pandaBaker.dataStreams["dateInput_sparsity"].value = dateBits.getSparsity() + pandaBaker.dataStreams["consumptionInput_overlap_with_prev_step"].value = 0 if consBits_last is None else consumptionBits.getOverlap(consBits_last) + consBits_last = consumptionBits + pandaBaker.dataStreams["dateInput_overlap_with_prev_step"].value = 0 if dateBits_last is None else dateBits.getOverlap(dateBits_last) + dateBits_last = dateBits + + pandaBaker.dataStreams["Layer1_SP_overlap_metric"].value = sp_info.overlap.overlap + pandaBaker.dataStreams["Layer1_TM_overlap_metric"].value = sp_info.overlap.overlap + pandaBaker.dataStreams["Layer1_SP_activation_frequency"].value = sp_info.activationFrequency.mean() + pandaBaker.dataStreams["Layer1_TM_activation_frequency"].value = tm_info.activationFrequency.mean() + pandaBaker.dataStreams["Layer1_SP_entropy"].value = sp_info.activationFrequency.mean() + pandaBaker.dataStreams["Layer1_TM_entropy"].value = tm_info.activationFrequency.mean() + + + pandaBaker.StoreIteration(iterationNo) + + print("ITERATION: "+str(iterationNo)) + + # ------------------HTMpandaVis---------------------- + + + iterationNo = iterationNo + 1 + #pandaBaker.CommitBatch() + if iterationNo == 1000: + break + + pandaBaker.CommitBatch() + # Print information & statistics about the state of the HTM. print("Encoded Input", enc_info) print("") @@ -253,8 +274,11 @@ def main(parameters=default_parameters, argv=None, verbose=True): accuracy[n] += (inp - val) ** 2 accuracy_samples[n] += 1 for n in sorted(predictions): - accuracy[n] = (accuracy[n] / accuracy_samples[n]) ** .5 - print("Predictive Error (RMS)", n, "steps ahead:", accuracy[n]) + if accuracy_samples[n]!=0: + accuracy[n] = (accuracy[n] / accuracy_samples[n]) ** .5 + print("Predictive Error (RMS)", n, "steps ahead:", accuracy[n]) + else: + print("Unable to calculate RMS error!") # Show info about the anomaly (mean & std) print("Anomaly Mean", np.mean(anomaly)) @@ -290,32 +314,37 @@ def main(parameters=default_parameters, argv=None, verbose=True): return -accuracy[5] -def BuildPandaSystem(): - global serverData - serverData = ServerData() - serverData.HTMObjects["HTM1"] = dataHTMObject() - serverData.HTMObjects["HTM1"].inputs["SL_Consumption"] = dataInput() - serverData.HTMObjects["HTM1"].inputs["SL_TimeOfDay"] = dataInput() +#with this method, the structure for visualisation is defined +def BuildPandaSystem(sp,tm,consumptionBits_size,dateBits_size): - serverData.HTMObjects["HTM1"].layers["SensoryLayer"] = dataLayer( - default_parameters["sp"]["columnCount"], - default_parameters["tm"]["cellsPerColumn"], - ) - serverData.HTMObjects["HTM1"].layers["SensoryLayer"].proximalInputs = [ - "SL_Consumption", - "SL_TimeOfDay", + #we have two inputs connected to proximal synapses of Layer1 + pandaBaker.inputs["Consumption"] = cInput(consumptionBits_size) + pandaBaker.inputs["TimeOfDay"] = cInput(dateBits_size) + + pandaBaker.layers["Layer1"] = cLayer(sp,tm) # Layer1 has Spatial Pooler & Temporal Memory + pandaBaker.layers["Layer1"].proximalInputs = [ + "Consumption", + "TimeOfDay", ] + #data for dash plots + streams = ["rawAnomaly","powerConsumption","numberOfWinnerCells","numberOfPredictiveCells", + "consumptionInput_sparsity","dateInput_sparsity","consumptionInput_overlap_with_prev_step", + "dateInput_overlap_with_prev_step","Layer1_SP_overlap_metric","Layer1_TM_overlap_metric", + "Layer1_SP_activation_frequency","Layer1_TM_activation_frequency","Layer1_SP_entropy", + "Layer1_TM_entropy" + ] + + pandaBaker.dataStreams = dict((name,cDataStream()) for name in streams)# create dicts for more comfortable code + #could be also written like: pandaBaker.dataStreams["myStreamName"] = cDataStream() + + pandaBaker.PrepareDatabase() if __name__ == "__main__": try: - pandaServer.Start() - BuildPandaSystem() - #while True: # run infinitely main() except KeyboardInterrupt: print("Keyboard interrupt") - pandaServer.MainThreadQuitted() print("Script finished") diff --git a/HotgymExample/logs/dummy.txt b/HotgymExample/logs/dummy.txt new file mode 100644 index 0000000..e69de29 diff --git a/HotgymExample/test.py b/HotgymExample/test.py deleted file mode 100644 index e4c6273..0000000 --- a/HotgymExample/test.py +++ /dev/null @@ -1,360 +0,0 @@ -import csv -import datetime -import os -import numpy as np -import math - -from htm.bindings.sdr import SDR, Metrics -from htm.encoders.rdse import RDSE, RDSE_Parameters -from htm.encoders.date import DateEncoder -from htm.bindings.algorithms import SpatialPooler -from htm.bindings.algorithms import TemporalMemory -from htm.algorithms.anomaly_likelihood import AnomalyLikelihood -from htm.bindings.algorithms import Predictor - -_EXAMPLE_DIR = os.path.dirname(os.path.abspath(__file__)) -_INPUT_FILE_PATH = os.path.join(_EXAMPLE_DIR, "gymdata.csv") - - -default_parameters = { - # there are 2 (3) encoders: "value" (RDSE) & "time" (DateTime weekend, timeOfDay) - 'enc': { - "value" : - {'resolution': 0.88, 'size': 700, 'sparsity': 0.02}, - "time": - {'timeOfDay': (30, 1), 'weekend': 21} - }, - 'predictor': {'sdrc_alpha': 0.1}, - 'sp': {'boostStrength': 3.0, - 'columnCount': 400,#1638 - 'localAreaDensity': 0.04395604395604396, - 'potentialPct': 0.85, - 'synPermActiveInc': 0.04, - 'synPermConnected': 0.13999999999999999, - 'synPermInactiveDec': 0.006}, - 'tm': {'activationThreshold': 17, - 'cellsPerColumn': 13, - 'initialPerm': 0.21, - 'maxSegmentsPerCell': 128, - 'maxSynapsesPerSegment': 64, - 'minThreshold': 10, - 'newSynapseCount': 32, - 'permanenceDec': 0.1, - 'permanenceInc': 0.1}, - 'anomaly': { - 'likelihood': - {#'learningPeriod': int(math.floor(self.probationaryPeriod / 2.0)), - #'probationaryPeriod': self.probationaryPeriod-default_parameters["anomaly"]["likelihood"]["learningPeriod"], - 'probationaryPct': 0.1, - 'reestimationPeriod': 100} #These settings are copied from NAB - } -} -def getAllPresynapticCellsForCell(tm,cellID): - segments = tm.connections.segmentsForCell(cellID) - - synapses = [] - for seg in segments: - for syn in tm.connections.synapsesForSegment(seg): - synapses += [syn] - - presynapticCells = [] - for syn in synapses: - presynapticCells += [tm.connections.presynapticCellForSynapse(syn)] - - return presynapticCells - -def getPresynapticCellsForCell(tm, cellID): - segments = tm.connections.segmentsForCell(cellID) - - synapses = [] - res = [] - for seg in segments: - for syn in tm.connections.synapsesForSegment(seg): - synapses += [syn] - - presynapticCells = [] - for syn in synapses: - presynapticCells += [tm.connections.presynapticCellForSynapse(syn)] - - res += [presynapticCells] - return res - -def main(parameters=default_parameters, argv=None, verbose=True): - - modelParams = parameters - - if verbose: - import pprint - - print("Parameters:") - pprint.pprint(parameters, indent=4) - print("") - - # Read the input file. - records = [] - with open(_INPUT_FILE_PATH, "r") as fin: - reader = csv.reader(fin) - headers = next(reader) - next(reader) - next(reader) - for record in reader: - records.append(record) - - # Make the Encoders. These will convert input data into binary representations. - dateEncoder = DateEncoder( - timeOfDay=parameters["enc"]["time"]["timeOfDay"], - weekend=parameters["enc"]["time"]["weekend"], - ) - - scalarEncoderParams = RDSE_Parameters() - scalarEncoderParams.size = parameters["enc"]["value"]["size"] - scalarEncoderParams.sparsity = parameters["enc"]["value"]["sparsity"] - scalarEncoderParams.resolution = parameters["enc"]["value"]["resolution"] - scalarEncoder = RDSE(scalarEncoderParams) - encodingWidth = dateEncoder.size + scalarEncoder.size - enc_info = Metrics([encodingWidth], 999999999) - - # Make the HTM. SpatialPooler & TemporalMemory & associated tools. - spParams = parameters["sp"] - sp = SpatialPooler( - inputDimensions=(encodingWidth,), - columnDimensions=(spParams["columnCount"],), - potentialPct=spParams["potentialPct"], - potentialRadius=encodingWidth, - globalInhibition=True, - localAreaDensity=spParams["localAreaDensity"], - synPermInactiveDec=spParams["synPermInactiveDec"], - synPermActiveInc=spParams["synPermActiveInc"], - synPermConnected=spParams["synPermConnected"], - boostStrength=spParams["boostStrength"], - wrapAround=True, - ) - - sp_info = Metrics(sp.getColumnDimensions(), 999999999) - - tmParams = parameters["tm"] - tm = TemporalMemory( - columnDimensions=(spParams["columnCount"],), - cellsPerColumn=tmParams["cellsPerColumn"], - activationThreshold=tmParams["activationThreshold"], - initialPermanence=tmParams["initialPerm"], - connectedPermanence=spParams["synPermConnected"], - minThreshold=tmParams["minThreshold"], - maxNewSynapseCount=tmParams["newSynapseCount"], - permanenceIncrement=tmParams["permanenceInc"], - permanenceDecrement=tmParams["permanenceDec"], - predictedSegmentDecrement=0.0, - maxSegmentsPerCell=tmParams["maxSegmentsPerCell"], - maxSynapsesPerSegment=tmParams["maxSynapsesPerSegment"], - ) - tm_info = Metrics([tm.numberOfCells()], 999999999) - - # setup likelihood, these settings are used in NAB - anParams = parameters["anomaly"]["likelihood"] - probationaryPeriod = int( - math.floor(float(anParams["probationaryPct"]) * len(records)) - ) - learningPeriod = int(math.floor(probationaryPeriod / 2.0)) - anomaly_history = AnomalyLikelihood( - learningPeriod=learningPeriod, - estimationSamples=probationaryPeriod - learningPeriod, - reestimationPeriod=anParams["reestimationPeriod"], - ) - - predictor = Predictor(steps=[1, 5], alpha=parameters["predictor"]["sdrc_alpha"]) - predictor_resolution = 1 - - # Iterate through every datum in the dataset, record the inputs & outputs. - inputs = [] - anomaly = [] - anomalyProb = [] - predictions = {1: [], 5: []} - iteration = 0 - for count, record in enumerate(records): - - # Convert date string into Python date object. - dateString = datetime.datetime.strptime(record[0], "%m/%d/%y %H:%M") - # Convert data value string into float. - consumption = float(record[1]) - inputs.append(consumption) - - # Call the encoders to create bit representations for each value. These are SDR objects. - dateBits = dateEncoder.encode(dateString) - consumptionBits = scalarEncoder.encode(consumption) - - # Concatenate all these encodings into one large encoding for Spatial Pooling. - encoding = SDR(encodingWidth).concatenate([consumptionBits, dateBits]) - enc_info.addData(encoding) - - # Create an SDR to represent active columns, This will be populated by the - # compute method below. It must have the same dimensions as the Spatial Pooler. - activeColumns = SDR(sp.getColumnDimensions()) - - # Execute Spatial Pooling algorithm over input space. - sp.compute(encoding, True, activeColumns) - sp_info.addData(activeColumns) - - # Execute Temporal Memory algorithm over active mini-columns. - #tm.compute(activeColumns, learn=True) - tm.activateDendrites(True) - predictiveCellsSDR = tm.getPredictiveCells() - - tm.activateCells(activeColumns,True) - - tm_info.addData(tm.getActiveCells().flatten()) - - activeCells = tm.getActiveCells() - #print("ACTIVE"+str(len(activeCells.sparse))) - - # Predict what will happen, and then train the predictor based on what just happened. - pdf = predictor.infer(count, tm.getActiveCells()) - for n in (1, 5): - if pdf[n]: - predictions[n].append(np.argmax(pdf[n]) * predictor_resolution) - else: - predictions[n].append(float("nan")) - predictor.learn( - count, tm.getActiveCells(), int(consumption / predictor_resolution) - ) - - anomalyLikelihood = anomaly_history.anomalyProbability(consumption, tm.anomaly) - anomaly.append(tm.anomaly) - anomalyProb.append(anomalyLikelihood) - - # connectedSynapses = np.zeros(sp.getNumInputs(), dtype=np.int32) - # sp.getConnectedSynapses(1, connectedSynapses) - - # pandaServer.serverData.connectedSynapses = connectedSynapses - - # print("CONNECTED:") - # print("len:"+str(len(connectedSynapses))) - # print(connectedSynapses) - - iteration += 1 - - if iteration > 50: - - segments = [] - for i in range(tmParams["cellsPerColumn"]*spParams["columnCount"]): - segments = tm.connections.segmentsForCell(i) - - if len(segments)>0: - print("cell:"+str(i)) - break - - - presynapticCells = getPresynapticCellsBySegmentsForCell(tm,i) - - -# if len(segments)>0: -# print("Segments:"+str(segments)) -# -# synapses = [] -# for seg in segments: -# for syn in tm.connections.synapsesForSegment(seg): -# synapses += [syn] -# -# #print("Cell for segment:"+str(tm.connections.cellForSegment(segments[0]))) -# -# print("Synapses:"+str(synapses)) -# presynapticCells = [] -# for syn in synapses: -# presynapticCells += [tm.connections.presynapticCellForSynapse(syn)] - - print("Presynaptic cells for that cell:"+str(presynapticCells)) - - - - # Print information & statistics about the state of the HTM. - print("Encoded Input", enc_info) - print("") - print("Spatial Pooler Mini-Columns", sp_info) - print(str(sp)) - print("") - print("Temporal Memory Cells", tm_info) - print(str(tm)) - print("") - - # Shift the predictions so that they are aligned with the input they predict. - for n_steps, pred_list in predictions.items(): - for x in range(n_steps): - pred_list.insert(0, float("nan")) - pred_list.pop() - - # Calculate the predictive accuracy, Root-Mean-Squared - accuracy = {1: 0, 5: 0} - accuracy_samples = {1: 0, 5: 0} - for idx, inp in enumerate(inputs): - for ( - n - ) in predictions: # For each [N]umber of time steps ahead which was predicted. - val = predictions[n][idx] - if not math.isnan(val): - accuracy[n] += (inp - val) ** 2 - accuracy_samples[n] += 1 - for n in sorted(predictions): - accuracy[n] = (accuracy[n] / accuracy_samples[n]) ** 0.5 - print("Predictive Error (RMS)", n, "steps ahead:", accuracy[n]) - - # Show info about the anomaly (mean & std) - print("Anomaly Mean", np.mean(anomaly)) - print("Anomaly Std ", np.std(anomaly)) - - # Plot the Predictions and Anomalies. - if verbose: - try: - import matplotlib.pyplot as plt - except: - print("WARNING: failed to import matplotlib, plots cannot be shown.") - return -accuracy[5] - - plt.subplot(2, 1, 1) - plt.title("Predictions") - plt.xlabel("Time") - plt.ylabel("Power Consumption") - plt.plot( - np.arange(len(inputs)), - inputs, - "red", - np.arange(len(inputs)), - predictions[1], - "blue", - np.arange(len(inputs)), - predictions[5], - "green", - ) - plt.legend( - labels=( - "Input", - "1 Step Prediction, Shifted 1 step", - "5 Step Prediction, Shifted 5 steps", - ) - ) - - plt.subplot(2, 1, 2) - plt.title("Anomaly Score") - plt.xlabel("Time") - plt.ylabel("Power Consumption") - inputs = np.array(inputs) / max(inputs) - plt.plot( - np.arange(len(inputs)), - inputs, - "red", - np.arange(len(inputs)), - anomaly, - "blue", - ) - plt.legend(labels=("Input", "Anomaly Score")) - plt.show() - - return -accuracy[5] - -if __name__ == "__main__": - - try: - #while True: # run infinitely - main() - - except KeyboardInterrupt: - print("Keyboard interrupt") - print("Script finished") diff --git a/PandaVis/Explorer3D.py b/PandaVis/Explorer3D.py new file mode 100644 index 0000000..030ddd8 --- /dev/null +++ b/PandaVis/Explorer3D.py @@ -0,0 +1,296 @@ +# -*- coding: utf-8 -*- + +from direct.showbase.ShowBase import ShowBase + +from bakeReader.bakeReader import BakeReader +import math +import os +import time + +from objects.htmObject import cHTM +from gui import cGUI # Graphical user interface +from environment import cEnvironment # handles everything about the environment +from interaction import cInteraction # handles keys, user interaction etc.. +from direct.stdpy import threading +from panda3d.core import loadPrcFileData, GraphicsWindow + +loadPrcFileData('', 'win-size 1600 900') + +#import faulthandler; faulthandler.enable() + +verbosityLow = 0 +verbosityMedium = 1 +verbosityHigh = 2 +FILE_VERBOSITY = ( + verbosityHigh +) # change this to change printing verbosity of this file + + +def printLog(txt, verbosity=verbosityLow): + if FILE_VERBOSITY >= verbosity: + print(txt) + + +class cExplorer3D(ShowBase): + + def __init__(self, databaseFilePath): + ShowBase.__init__(self) + self.speed = 40 + + # Mouse and camera movement init + self.mouseX_last = 0 + self.mouseY_last = 0 + self.rotateCamera = False + self.move_z = 50 + + self.env = cEnvironment(self) + + self.env.CreateBasement() # to not be lost if there is nothing around + self.env.SetupLights() + self.env.SetupCamera() + + width = self.win.getProperties().getXSize() + height = self.win.getProperties().getYSize() + + self.gui = cGUI( + width, + height, + self.loader, + visApp = self + ) + + self.databaseFilePath = databaseFilePath + + self.bakeReader = BakeReader(self.databaseFilePath) + + self.interaction = cInteraction(self) + + self.interaction.SetupKeys() + self.interaction.SetupOnClick() + + self.taskMgr.add(self.update, "main loop") + self.accept(self.win.getWindowEvent(), self.interaction.onWindowEvent) + + self.HTMObjects = {} + self.allHTMobjectsCreated = False + self.oneOfObjectsCreationFinished = False + + self.gfxCreationThread= threading.Thread(target=self.gfxCreationWorker, args=()) + + #---- + self.iterationDataUpdate = False + + self.BuildStructure() + + # hand over this value to the gui to be able to validate user inputs + # -1 because predictive cells are + 1 + self.gui.cntIterations = self.bakeReader.cntIterations-1 + + self.iteration = 0 + self.initIterationLoaded = False + + self.autoRunIteration = 0 + + def BuildStructure(self): + + self.bakeReader.OpenDatabase() + self.bakeReader.BuildStructure() + + obj = "HTM1" + if obj not in self.HTMObjects: + + printLog("HTM object creation! Name:" + str(obj)) + # create HTM object + newObj = cHTM(self, self.loader, obj) + newObj.getNode().reparentTo(self.render) + + # create inputs + for inp in self.bakeReader.inputs: + printLog("Creating input: " + str(inp), verbosityHigh) + + newObj.CreateInput( + name=inp, + count=self.bakeReader.inputs[inp].size, + rows=int(math.sqrt(self.bakeReader.inputs[inp].size)), + ) + # create layers + for lay in self.bakeReader.layers: + printLog("Creating layer: " + str(lay), verbosityHigh) + newObj.CreateLayer( + name=lay, + nOfColumnsPerLayer=int(self.bakeReader.layers[lay].params['sp_columnCount']), + nOfCellsPerColumn=int(self.bakeReader.layers[lay].params['tm_cellsPerColumn']), + ) + + self.HTMObjects[obj] = newObj + + self.gfxCreationThread.start() + + + def LoadIteration(self, iteration): + self.iteration = iteration + + for obj in self.HTMObjects: + for inp in self.HTMObjects[obj].inputs: + self.bakeReader.LoadInput(inp, iteration) + + for ly in self.HTMObjects[obj].layers: + self.bakeReader.LoadActiveColumns(ly, iteration) + self.bakeReader.LoadWinnerCells(ly, iteration) + self.bakeReader.LoadPredictiveCells(ly, iteration+1)#take predictions for t+1 + if self.gui.showPredictionCorrectness: + self.bakeReader.LoadPredictiveCells(ly, iteration, loadPrevious=True)#take also predictions for t to be able to calculate correctness + self.bakeReader.LoadActiveCells(ly, iteration) + + self.bakeReader.LoadProximalSynapses(ly,[self.gui.columnID,], iteration) + #can't load distal synapses here, because they are a big size + # loading just for one cell per - user click + + + self.updateHTMstate() # update state of the HTM objects and connections + + + def updateHTMstate(self): + printLog("Data change! Updating HTM state", verbosityMedium) + + self.gui.setIteration(self.iteration) + obj = "HTM1" + # go through all incoming inputs + for i in self.bakeReader.inputs: # dict + printLog("Updating state of input: " + str(i), verbosityHigh) + # find matching input in local structure + + self.HTMObjects[obj].inputs[i].UpdateState( + self.bakeReader.inputs[i].bits, + self.bakeReader.inputs[i].stringValue, + ) + + # go through all incoming layers + for l in self.bakeReader.layers: # dict + if self.HTMObjects[obj].layers[l].gfxCreationFinished: + printLog("Updating state of layer: " + str(l), verbosityHigh) + # find matching layer in local structure + self.HTMObjects[obj].layers[l].UpdateState( + self.bakeReader.layers[l].activeColumns, + self.bakeReader.layers[l].activeCells, + self.bakeReader.layers[l].winnerCells, + self.bakeReader.layers[l].predictiveCells, + self.bakeReader.layers[l].prev_predictiveCells, + showPredictionCorrectness=self.gui.showPredictionCorrectness, + showBursting = self.gui.showBursting + ) + + self.oneOfObjectsCreationFinished = False + + self.UpdateProximalAndDistalData() + self.gui.UpdateCellDescription() + + def UpdateProximalAndDistalData(self): + if self.gui.focusedCell is None: + return + # -------- proximal and distal synapses ----------------------- + if self.gui.showProximalSynapses: + self.ShowProximalSynapses(self.gui.focusedPath[0],self.gui.focusedPath[1],self.gui.columnID, self.gui.showOnlyActiveProximalSynapses) + + if self.gui.showDistalSynapses: + self.ShowDistalSynapses(self.gui.focusedPath[0], self.gui.focusedPath[1], self.gui.columnID, self.gui.cellID) + + # if self.gui.showProximalSynapses and self.gui.focusedCell is not None: + # self.client.reqProximalData() + # else: + # for obj in self.base.HTMObjects.values(): + # obj.DestroyProximalSynapses() + # + # #do not request distal data if we don't want to show them or if this layer doesn't have TM + # if self.gui.showDistalSynapses and self.gui.focusedCell is not None: + # self.client.reqDistalData() + # else: + # for obj in self.base.HTMObjects.values(): # destroy synapses if they not to be shown + # obj.DestroyDistalSynapses() + # ----------------------------------------------------------- + + def ShowProximalSynapses(self, obj, layerName, column, showOnlyActive):# reads the synapses from the database and show them + + layer = self.bakeReader.layers[layerName] + self.bakeReader.LoadProximalSynapses(layerName, [column], self.iteration) # load it + + if column not in layer.proximalSynapses: + printLog("Don't have proximal data for requested column:"+str(column) + " of layer:"+str(layerName)) + self.HTMObjects[obj].layers[layerName].DestroyProximalSynapses() + return + self.HTMObjects[obj].layers[layerName].ShowProximalSynapses(column, layer.proximalSynapses[column], + layer.proximalInputs,#names of inputs + self.HTMObjects[obj].inputs, + layer.params['sp_synPermConnected'], + showOnlyActive) + def ShowDistalSynapses(self, obj, layerName, column, cell): + + layer = self.bakeReader.layers[layerName] + + gotSomeData = self.bakeReader.LoadDistalSynapses(layerName, column, cell, self.iteration) # load it + + if not gotSomeData: + printLog("Don't have any distal synapses to show for this cell.") + self.HTMObjects[obj].layers[layerName].minicolumns[ + column + ].cells[cell].DestroyDistalSynapses() + return + + self.HTMObjects[obj].layers[layerName].minicolumns[ + column + ].cells[cell].CreateDistalSynapses( + self.HTMObjects[obj], + self.HTMObjects[obj].layers[layerName], + layer.distalSynapses[cell], + layer.distalInputs + ) + + + def update(self, task): + + self.gui.update() + self.interaction.Update() + + if self.allHTMobjectsCreated and not self.initIterationLoaded: # wait till the objects are created, then load iteration 0 + self.LoadIteration(0) + self.initIterationLoaded = True + + if self.gui.gotoReq >= 0: + self.LoadIteration(self.gui.gotoReq) + self.gui.gotoReq = -1 + + + if self.gui.capture: + self.LoadIteration(self.autoRunIteration) + self.autoRunIteration += 1 + if self.autoRunIteration > 997: + self.gui.capture =False + os.system("ffmpeg -y -framerate 10 -i screenshots/%01d.jpg -codec copy screenshots/recording.mkv") + self.win.saveScreenshot('screenshots/'+str(self.autoRunIteration)+'.jpg') + + + return task.cont + + def gfxCreationWorker(self): + + time.sleep(5) # need to delay this, there was SIGSEG faults, probably during creation of objects thread collision happens + printLog("Starting GFX worker thread") + while True: + # finishing HTM objects creation on the run + if not self.allHTMobjectsCreated: + allFinished = True + for obj in self.HTMObjects: + if not self.HTMObjects[obj].gfxCreationFinished: + allFinished = False + self.HTMObjects[obj].CreateGfxProgressively() + + if self.HTMObjects[obj].gfxCreationFinished: # it just finished GFX creation + self.oneOfObjectsCreationFinished = True + if allFinished: + self.allHTMobjectsCreated = True + printLog("GFX worker: all objects finished") + break + if self.gui.terminating: + break + printLog("GFX worker: quit") + diff --git a/PandaVis/FreeMonoBold.ttf b/PandaVis/FreeMonoBold.ttf deleted file mode 100644 index cf8d291..0000000 Binary files a/PandaVis/FreeMonoBold.ttf and /dev/null differ diff --git a/PandaVis/FreeSans.ttf b/PandaVis/FreeSans.ttf deleted file mode 100644 index 137fcbc..0000000 Binary files a/PandaVis/FreeSans.ttf and /dev/null differ diff --git a/PandaVis/Ubuntu-B.ttf b/PandaVis/Ubuntu-B.ttf deleted file mode 100644 index b173da2..0000000 Binary files a/PandaVis/Ubuntu-B.ttf and /dev/null differ diff --git a/PandaVis/bakeReader/__init__.py b/PandaVis/bakeReader/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/PandaVis/bakeReader/bakeReader.py b/PandaVis/bakeReader/bakeReader.py new file mode 100644 index 0000000..784a721 --- /dev/null +++ b/PandaVis/bakeReader/bakeReader.py @@ -0,0 +1,209 @@ +# -*- coding: utf-8 -*- + +from bakeReader.bakeReaderDatabase import Database +from bakeReader.dataStructs import cLayer,cInput, cDataStream +import numpy as np +import os +import time + +#needed to gather distal synapses from dump +from htm.bindings.algorithms import TemporalMemory + +class BakeReader(object): + def __init__(self, databaseFilePath): + self.databaseFilePath = databaseFilePath + self.db = None + + self.layers = {} # can contain cLayer instances + self.inputs = {} # can contain cInput instances + self.dataStreams = {} # can contain cDataStream instances + + self._reqProximalData = False + self._reqDistalData = False + + self.cntIterations = 0 + + def OpenDatabase(self): + self.db = Database(self.databaseFilePath) # connect to the database + + def BuildStructure(self): + tableNames = self.db.getTableNames() + + #build the structure, tables that start with par_ + parTables = [q for q in tableNames if q.startswith("par_") and q!='par_inputs'] + + #each layer has own par table + for t in parTables: + ly = t.split('_')[2] + self.layers[ly]=cLayer() + Log("Loaded layer: " + ly) + + pars = self.db.SelectAll(t) + # create dict from rows + dictPars = {} + for p in pars: + dictPars[p['name']] = p['value'] + + self.layers[ly].params = dictPars # assign parameter dict to the layer + Log("Layer parameters loaded:"+str(dictPars)) + + #fill all inputs + rows = self.db.SelectAll('par_inputs') + for row in rows: + self.inputs[row['name']] = cInput(row['size']) + Log("Loaded input : " + row['name']) + + # load connections + connections = self.db.SelectAll("connections") + + for con in connections: + if con["type"] == "proximal": + self.layers[con["layer"]].proximalInputs.append(con["input"]) + Log("Loaded proximal input for layer: " + str(con["layer"])) + elif con["type"] == "distal": + self.layers[con["layer"]].distalInputs.append(con["input"]) + Log("Loaded distal input for layer: " + str(con["layer"])) + + # figure out how many iterations there are + self.cntIterations = self.LoadMaxIteration("inputs_"+next(iter(self.inputs)))# get first input + Log("Database contains data for "+str(self.cntIterations)+" iterations.") + + def LoadDataStreams(self): + # DATA STREAMS - loaded all at once + tableNames = self.db.getTableNames() + parStreamTables = [q for q in tableNames if q.startswith("dataStream_")] + for streamTable in parStreamTables: + streamName = streamTable.replace("dataStream_","") + self.dataStreams[streamName] = cDataStream() + Log("Loaded stream: " + streamName) + + data = self.db.SelectAll(streamTable) + + arr = np.empty(shape=(2,len(data))) + i=0 + for d in data:#convert to numpy two dim array + arr[0,i] = d['iteration'] + arr[1,i] = d['value'] + i=i+1 + + + self.dataStreams[streamName].allData = arr + + def LoadMaxIteration(self, tableName): + row = self.db.SelectMaxIteration(tableName) + return row + + def LoadInput(self, inp, iteration): + tableName = "inputs_"+inp + + row = self.db.SelectByIteration(tableName,iteration) + + self.inputs[inp].bits = row['data'] + self.inputs[inp].stringValue = row['value'] + + def LoadActiveColumns(self, layer, iteration): + tableName = "layer_activeColumns_"+layer + row = self.db.SelectByIteration(tableName,iteration) + self.layers[layer].activeColumns = row['data'] if row['data'] is not None else np.empty(0) + + def LoadWinnerCells(self, layer, iteration): + tableName = "layer_winnerCells_"+layer + row = self.db.SelectByIteration(tableName,iteration) + self.layers[layer].winnerCells = row['data'] if row['data'] is not None else np.empty(0) + + def LoadPredictiveCells(self, layer, iteration, loadPrevious=False): + tableName = "layer_predictiveCells_"+layer + row = self.db.SelectByIteration(tableName,iteration) + if not loadPrevious: + self.layers[layer].predictiveCells = row['data'] if row['data'] is not None else np.empty(0) + else: + self.layers[layer].prev_predictiveCells = row['data'] if row['data'] is not None else np.empty(0) + + def LoadActiveCells(self, layer, iteration): + tableName = "layer_activeCells_"+layer + row = self.db.SelectByIteration(tableName,iteration) + self.layers[layer].activeCells = row['data'] if row['data'] is not None else np.empty(0) + + def LoadProximalSynapses(self, layer, columns, iteration): + tableName = "layer_proximalSynapses_"+layer + columnCount = self.layers[layer].params["sp_columnCount"] + self.layers[layer].proximalSynapses = {} # erase dict + + for col in columns: # what specific columns we want to get + # we need to select by rowid, because of speed + rowId = iteration * columnCount + col + 1 + row = self.db.SelectByRowId(tableName, rowId) + + #now check for sure if the data fits + if col != row['column'] or iteration != row['iteration']: + raise RuntimeError("Data are not valid! Not sorted!") + self.layers[layer].proximalSynapses[col] = (row['data'] if row['data'] is not None else np.empty(0))# add to the dict + + + def LoadDistalSynapses(self, layer, column, cell, iteration): + timeStartDistalSynapsesCalc = time.time() + tm = TemporalMemory() + tm.loadFromFile(os.path.join(os.path.splitext(self.databaseFilePath)[0] + "_distalDump", + "" + str(layer) + "_" + str(iteration) + ".dump")) + + reqCellID = int(column * self.layers[layer].params['tm_cellsPerColumn'] + cell) + + print("requesting distals for cell:"+str(reqCellID)) + segments = self.getPresynapticCellsForCell(tm, reqCellID) + + segNo = 0 + for seg in segments: # for each segment + if cell not in self.layers[layer].distalSynapses.keys(): + self.layers[layer].distalSynapses[cell] = {} + self.layers[layer].distalSynapses[cell][segNo] = seg # add numpy array to the dict + segNo += 1 + + return len(segments) > 0 # true if we got something for this cell + + def getPresynapticCellsForCell(self, tm, cellID): + start_time = time.time() + segments = tm.connections.segmentsForCell(cellID) + + # synapses = [] + res = [] + + # if len(segments) > 0: + # Log("segments len:"+str(len(segments))) + for seg in segments: + time1 = time.time() + synapsesForSegment = tm.connections.synapsesForSegment(seg) + # Log("synapsesForSegment() took %s ms " % ((time.time() - time1)*1000)) + # Log("synapses for segment len:" + str(len(segments))) + # Log("datatype:" + str(type(synapsesForSegment))) + # Log("synapsesForSegment len:" + str(len(synapsesForSegment))) + + # for syn in synapsesForSegment: + # synapses += [syn] + + presynapticCells = [] + for syn in synapsesForSegment: + # time2 = time.time() + presynapticCells += [tm.connections.presynapticCellForSynapse(syn)] + # Log("presynapticCellForSynapse() took %s ms " % ((time.time() - time2)*1000)) + res += [np.array(presynapticCells)] + + # if len(segments) > 0: + # Log("getPresynapticCellsForCell() took %s ms " % ((time.time() - start_time)*1000)) + return res + + def reqProximalData(self): + self._reqProximalData = True + + def reqDistalData(self): + self._reqDistalData = True + + def Close(self): + self.db.Close() + +def Log(s): + print(str(s)) + from datetime import datetime + dateStr=datetime.now().strftime('%Y-%m-%d %H:%M:%S') + with open(os.path.join(os.getcwd(),"logs","pandaBaker.log"),"a") as file: + file.write(dateStr+" >> "+str(s)+"\n") + diff --git a/PandaVis/bakeReader/bakeReaderDatabase.py b/PandaVis/bakeReader/bakeReaderDatabase.py new file mode 100644 index 0000000..a948b8d --- /dev/null +++ b/PandaVis/bakeReader/bakeReaderDatabase.py @@ -0,0 +1,140 @@ +# -*- coding: utf-8 -*- + +import sqlite3 +import numpy as np +import os +# defining new SQLITE datatype to be able to store numpy array types +#--------------------------------------------------------------------FLOAT ARRAY +def adapt_array(arr): + return arr.tobytes() + +def convert_array(text): + return np.frombuffer(text,dtype=np.float32) + +sqlite3.register_adapter(np.array, adapt_array) +sqlite3.register_converter("array", convert_array) +#--------------------------------------------------------------------SDR (SPARSE, contains active indicies) +def adapt_sdr(arr): + return arr.tobytes() + +def convert_sdr(text): + return np.frombuffer(text,dtype=np.uint32) + +sqlite3.register_adapter(np.array, adapt_sdr) +sqlite3.register_converter("sdr", convert_sdr) + + +class Database(object): + def __init__(self,filePath): + + self.conn = sqlite3.connect(filePath,detect_types=sqlite3.PARSE_DECLTYPES) + self.conn.row_factory = sqlite3.Row + self.curs = self.conn.cursor() + + def CreateTable(self, tableName, columns): + + query = "CREATE TABLE " + tableName + " (%s);"%columns + + self.curs.execute(query) + self.conn.commit() + + @staticmethod + def AddParanthesis(s): + if type(s)==str: + return "'"+s+"'" + return s + + # inserts array of values into table + def Insert(self,tableName, values): + values = [Database.AddParanthesis(v) for v in values] + + query = "INSERT INTO " + tableName + " VALUES (%s);"%(",".join(values)) + + self.curs.execute(query) + + + # inserts dictionary items into table with "name" and "value" column + def InsertParameters(self,tableName, _dict): + + for i in _dict: + query = "INSERT INTO " + tableName + " (name,value) VALUES (%s,%s);"%(Database.AddParanthesis(i),_dict[i]) + + self.curs.execute(query) + + + # insert into two column table + def InsertDataArray(self, tableName,a, b): + self.curs.execute("INSERT INTO " + tableName + " VALUES (?,?);",(a,b,)) + + + # insert into three column table + def InsertDataArray2(self, tableName, a, b, c): + + self.curs.execute("INSERT INTO " + tableName + " VALUES (?,?,?);", (a, b, c,)) + + + + + def getTableNames(self): + + result = self.curs.execute("SELECT name FROM sqlite_master WHERE type='table';").fetchall() + if(len(result)==0): + raise RuntimeError("The database is empty!") + table_names = sorted(list(zip(*result))[0]) + + return table_names + + def SelectAll(self, tableName): + + self.curs.execute("SELECT * FROM " + tableName + ";") + self.conn.commit() + data = self.curs.fetchall() + + return data + + def SelectMaxIteration(self, tableName): + self.curs.execute("SELECT MAX(iteration) FROM " + tableName + ";") + self.conn.commit() + data = self.curs.fetchone() # returns single row + + return data[0] + + # used on tables where iteration is unique value + def SelectByIteration(self, tableName, iteration): + + self.curs.execute("SELECT * FROM " + tableName + " WHERE iteration=(?);",(iteration,)) + self.conn.commit() + data = self.curs.fetchone() # returns single row + + return data + + def SelectByRowId(self, tableName, rowId): + + self.curs.execute("SELECT * FROM " + tableName + " WHERE rowid=(?);",(rowId,)) + self.conn.commit() + data = self.curs.fetchone()#row id is unique + + return data + + def SelectDistalData(self, tableName, iteration, column, cell): + + self.curs.execute("SELECT * FROM " + tableName + " WHERE iteration = (?) AND column = (?) AND cell = (?);", (iteration,column,cell,)) + self.conn.commit() + data = self.curs.fetchall() + + return data + + + def Close(self): + self.conn.close() + + + + +def Log(s): + s = "SQLite:" + str(s) + print(str(s)) + from datetime import datetime + dateStr=datetime.now().strftime('%Y-%m-%d %H:%M:%S') + with open(os.path.join(os.getcwd(), "logs", "bakerDatabase.log"), "a") as file: + file.write(dateStr+" >> "+str(s)+"\n") diff --git a/PandaVis/bakeReader/dataStructs.py b/PandaVis/bakeReader/dataStructs.py new file mode 100644 index 0000000..2065e2f --- /dev/null +++ b/PandaVis/bakeReader/dataStructs.py @@ -0,0 +1,87 @@ +# -*- coding: utf-8 -*- +import numpy as np + +def Params(sp, tm): + + spPars={} + tmPars={} + if sp is not None: + + sp_inputDims_x = sp.getInputDimensions()[0] + sp_inputDims_y = sp.getInputDimensions()[1] if len(sp.getInputDimensions()) > 1 else 1 + sp_columnDimensions_x = sp.getColumnDimensions()[0] + sp_columnDimensions_y = sp.getColumnDimensions()[1] if len(sp.getColumnDimensions()) > 1 else 1 + + sp_columnCount = sp_columnDimensions_x * sp_columnDimensions_y; + + spPars = {'sp_inputDimensions_x': sp_inputDims_x,'sp_inputDimensions_y': sp_inputDims_y, + 'sp_columnCount' : sp_columnCount, + 'sp_columnDimensions_x': sp_columnDimensions_x, 'sp_columnDimensions_y': sp_columnDimensions_y, + 'sp_potentialPct': sp.getPotentialPct(), 'sp_potentialRadius': sp.getPotentialRadius(), + 'sp_globalInhibition': sp.getGlobalInhibition(), 'sp_localAreaDensity': sp.getLocalAreaDensity(), + 'sp_synPermInactiveDec': sp.getSynPermInactiveDec(), 'sp_synPermActiveInc': sp.getSynPermActiveInc(), + 'sp_synPermConnected': sp.getSynPermConnected(), 'sp_boostStrength': sp.getBoostStrength(), + 'sp_wrapAround': sp.getWrapAround()} + + if tm is not None: + + tmPars = {'tm_activationThreshold': tm.getActivationThreshold(), + 'tm_cellsPerColumn': tm.getCellsPerColumn(), + 'tm_initialPerm': tm.getInitialPermanence(), + 'tm_maxSegmentsPerCell': tm.getMaxSegmentsPerCell(), + 'tm_maxSynapsesPerSegment': tm.getMaxSynapsesPerSegment(), + 'tm_minThreshold': tm.getMinThreshold(), + 'tm_newSynapseCount': tm.getMaxNewSynapseCount(), + 'tm_permanenceDec': tm.getPermanenceDecrement(), + 'tm_permanenceInc': tm.getPermanenceIncrement() + } + + return {**spPars, **tmPars} + +class cLayer(object): + def __init__(self, sp=None, tm=None): + # static vars ---------------------------------------------------------------------- + self.sp = sp + self.tm = tm + + self.params = Params(self.sp, self.tm) + + # to what inputs are the synapses connected + self.proximalInputs = [] # [inputName1,inputName2,...] + # to what distal connections are connected (exluding itself, so these are external distal) + self.distalInputs = [] # [inputName1, inputName2] - can be input or layer + + + + # dynamic vars ---------------------------------------------------------------------- + self.activeColumns = np.empty(0) # currently active columns (sparse) in this layer + self.winnerCells = np.empty(0) + self.activeCells = np.empty(0) + self.predictiveCells = np.empty(0) + self.prev_predictiveCells = np.empty(0) # filled in only when we want to see prediction correctness + + # proximal synapses - contains values of permanences + # dict of numpy arrays, e.g. proximalSynapses[0] is numpy array for column ID 0 + self.proximalSynapses = {} + + # synapses - contains values of permanences + # dict of numpy arrays, e.g. distalSynapses[0] is numpy array for cell ID 0 + self.distalSynapses = {} + + +class cInput(object): + def __init__(self, size): + #static vars ----------- + self.size = size + + #dynamic vars ---------- + self.bits = np.empty(0) # input SDRs (just indicies of active bits) + self.stringValue = "" # ordinary expressed value that is represented by input SDRs + + +class cDataStream(object): + def __init__(self, dataType="REAL"): + self.dataType = dataType + self.value = None + + diff --git a/PandaVis/entryWindow.py b/PandaVis/entryWindow.py new file mode 100644 index 0000000..7968557 --- /dev/null +++ b/PandaVis/entryWindow.py @@ -0,0 +1,85 @@ +import PySimpleGUI as sg +import json +import os + +class cEntryWindow: + def __init__(self): + sg.ChangeLookAndFeel('NeutralBlue')#NeutralBlue + + self.command = "None" + + try: + with open('guiValues.ini', 'r') as file: + self.defaults = json.loads(file.read()) + except: + # guiValues doesn't exist, probably is this the first time run + with open('guiDefaults.ini', 'r') as file: + self.defaults = json.loads(file.read()) + + self.databaseFilePath = self.getDefault("databaseFileLocation") + logoPath = os.path.join('..','images','HTMpandaVis.png') + + imageColumn = [[sg.Image(filename=logoPath)]] + + # find all txt files in layouts folder + dashLayouts = () # tuple + for file in os.listdir(os.path.join(os.getcwd(),"..","dashVis","layouts")): + if file.endswith(".txt"): + dashLayouts += (file.replace('.txt',''),) + + defaultDashLayout = self.getDefault("defaultDashLayout") + + layout = [[sg.Column(imageColumn, justification='center')], + [sg.Text('Database file location:'), sg.Text(size=(15,1))], + [sg.In(default_text=self.databaseFilePath,key='-databaseFilePath-') ,sg.FileBrowse(file_types=(("SQLite3 database", "*.db"),))], + [sg.Button('Run pandaVis 3D explorer',size=(30,5),key='-run3Dexplorer-')], + [sg.Button('Run dash visualisation in web browser',size=(30,5),key='-runDash-'), sg.InputCombo(dashLayouts, default_value=defaultDashLayout, size=(20, 1),key='-dashLayout-')], + [sg.Button('Run both', size=(30, 5), key='-runBoth-')], + [sg.Button('Exit')] + ] + + self.window = sg.Window('HTMpandaVis', keep_on_top=True).Layout(layout) + + self.dashLayout = None + + + def Show(self): + while True: # Event Loop + event, values = self.window.read() + + if event == sg.WIN_CLOSED or event == 'Exit': + self.command = "terminate" + else: + self.databaseFilePath = values['-databaseFilePath-'] + self.defaults["defaultDashLayout"] = values['-dashLayout-'] + self.dashLayout = values['-dashLayout-'] + self.command = event + + break + + self.Close() + + def getDefault(self, key): + try: + return self.defaults[key] + except KeyError as e: + print("Can't load default value:" + str(e)) + return False + + def Close(self): + + # retrieve defaults + self.defaults["databaseFileLocation"] = self.databaseFilePath + self.SaveDefaults() + + self.window.close() + + def SaveDefaults(self): + + try: + with open('guiValues.ini', 'w') as file: + file.write(json.dumps(self.defaults)) + except: + self.defaults = {} + print("Wasn't able to save defaults into file guiValues.ini !!") + diff --git a/PandaVis/environment.py b/PandaVis/environment.py index 3329a63..4664416 100644 --- a/PandaVis/environment.py +++ b/PandaVis/environment.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- - +import os from panda3d.core import DirectionalLight, AmbientLight, AntialiasAttrib, TransparencyAttrib from panda3d.core import LColor, CollisionBox, CollisionNode from panda3d.core import ( @@ -78,9 +78,9 @@ def SetupLights(self): def CreateBasement( self ): # it will create basement object, just for case that nothing is drawn to be not lost - # Load the environment model. - self.cube = self.base.loader.loadModel("models/cube") # /media/Data/Data/Panda3d/ + + self.cube = self.base.loader.loadModel(os.path.join(os.getcwd(),"models/cube")) # /media/Data/Data/Panda3d/ # Reparent the model to render. self.cube.reparentTo(self.render) diff --git a/PandaVis/gui.py b/PandaVis/gui.py index 8df4051..3edcd02 100644 --- a/PandaVis/gui.py +++ b/PandaVis/gui.py @@ -26,6 +26,7 @@ def __init__(self, defaultWidth, defaultHeight, loader, visApp): self.focusedCell = None self.focusedPath = None self.columnID = 0 + self.cellID = 0 self.visApp = visApp self.loader = loader @@ -46,6 +47,7 @@ def __init__(self, defaultWidth, defaultHeight, loader, visApp): self.showProximalSynapses = self.getDefault("proximalSynapses") + self.showOnlyActiveProximalSynapses = self.getDefault("showOnlyProximalSynapses") self.showDistalSynapses = self.getDefault("distalSynapses") self.showInputOverlapWithPrevStep = self.getDefault("inputPrevStepOverlap") self.showPredictionCorrectness = self.getDefault("predictionCorrectness") @@ -67,13 +69,19 @@ def __init__(self, defaultWidth, defaultHeight, loader, visApp): self.showLegend = self.getDefault("legend") self.showDescription = self.getDefault("desc") + self.capture = False + + self.iteration = 0 + self.cntIterations = 0 + layout = [[sg.Text('Iteration no. 0 ', key = 'iteration')], - [sg.Button('ONE STEP')], + [sg.Button('STEP -1'), sg.Button('STEP +1')], [sg.Button('RUN'), sg.Button('STOP')], [sg.InputText(self.getDefault("iterationGoto"), key="iterationGoto"), sg.Button('GOTO step')], [sg.Checkbox('Show proximal synapes', key="proximalSynapses", enable_events=True)], + [sg.Checkbox('Show only active proximal synapes', key="onlyActiveProximalSynapses", enable_events=True)], [sg.Checkbox('Show distal synapes', key="distalSynapses", enable_events=True)], [sg.Checkbox('Show input overlap with prev.step', key="inputPrevStepOverlap", enable_events=True)], [sg.Checkbox('Show prediction correctness', key="predictionCorrectness", enable_events=True)], @@ -88,13 +96,16 @@ def __init__(self, defaultWidth, defaultHeight, loader, visApp): sg.Slider(key='LODSlider1', range=(1, 1000), orientation='h', size=(20, 10), default_value=100, enable_events=True)], [sg.Slider(key='LODSlider2', range=(1, 10000), orientation='h', size=(20, 10), - default_value=100, enable_events=True)]])] + default_value=100, enable_events=True)]])], + [sg.Button('capture')], ] self.window = sg.Window('Main panel', keep_on_top=True, location=self.getDefault("mainWinPos")).Layout(layout) def setIteration(self, iterationNo): + self.iteration = iterationNo self.window["iteration"].update("Iteration no.:"+str(iterationNo)) + def getDefault(self, key): try: return self.defaults[key] @@ -164,18 +175,27 @@ def update(self): if event != '__TIMEOUT__': - if event == "ONE STEP": - self.cmdStepForward = True - print("one step") + if event == "STEP +1": + print("step +1") + if(self.iteration + 1 <= self.cntIterations): + self.gotoReq = self.iteration + 1 + self.iteration= self.iteration + 1 + if event == "STEP -1": + print("step -1") + if self.iteration > 0: + self.gotoReq = self.iteration - 1 + self.iteration= self.iteration - 1 elif event == "RUN": self.cmdRun = True elif event == "STOP": self.cmdStop = True elif event == "GOTO step": try: - self.gotoReq = int(values["iterationGoto"]) - print("GOTO") - print(values["iterationGoto"]) + if (int(values["iterationGoto"]) > self.cntIterations): + print("Larger than allowed!") + else: + self.gotoReq = int(values["iterationGoto"]) + print("GOTO") except: print("It is not a number!") elif event == "transparencySlider": @@ -202,12 +222,15 @@ def update(self): else: self.description.window.close() self.description = None + elif event == 'capture': + self.capture = True self.wireframe = values["wireFrame"] self.wireframeChanged = event == "wireFrame" self.showProximalSynapses = values["proximalSynapses"] + self.showOnlyActiveProximalSynapses = values["onlyActiveProximalSynapses"] self.showDistalSynapses = values["distalSynapses"] self.showInputOverlapWithPrevStep = values["inputPrevStepOverlap"] self.showPredictionCorrectness = values["predictionCorrectness"] @@ -222,6 +245,7 @@ def update(self): def UpdateDescription(self, txt): if self.description is not None: self.description.updateText(txt) + def UpdateCellDescription(self): if self.focusedCell is None: @@ -250,4 +274,4 @@ def Terminate(self): # event when app exit file.write(json.dumps(self.defaults)) except: self.defaults = {} - print("Wasn't able to save defaults into file guiValues.ini !!") \ No newline at end of file + print("Wasn't able to save defaults into file guiValues.ini !!") diff --git a/PandaVis/interaction.py b/PandaVis/interaction.py index 70dcfef..81fc221 100644 --- a/PandaVis/interaction.py +++ b/PandaVis/interaction.py @@ -27,7 +27,7 @@ def __init__(self,base): self.speedBoost = False self.gui = base.gui - self.client = base.client + self.bakeReader = base.bakeReader """Updates the camera based on the keyboard input. Once this is done, then the CellManager's update function is called.""" @@ -132,13 +132,16 @@ def onKey(self, key, value): def onEscape(self): """Event when escape button is pressed.""" + if self.gui.focusedCell is None: + return # unfocus all for obj in self.base.HTMObjects.values(): obj.DestroyProximalSynapses() for obj in self.base.HTMObjects.values(): obj.DestroyDistalSynapses() - self.focusedCell.resetFocus() # reset previous + self.gui.focusedCell.resetFocus() # reset previous + self.gui.focusedCell = None def onMouseEvent(self, event, press): printLog("Mouse event:" + str(event), verbosityHigh) @@ -199,7 +202,7 @@ def SetupOnClick(self): def CloseApp(self): printLog("CLOSE app event") - self.client.terminateClientThread = True + self.bakeReader.Close() self.base.gui.Terminate() # terminate GUI windows __import__("sys").exit(0) @@ -228,13 +231,13 @@ def HandlePickedObject(self, obj): HTMObj = self.base.HTMObjects[focusedHTMObject] Layer = HTMObj.layers[focusedLayer] newCellFocus = Layer.minicolumns[parentId].cells[thisId] - self.focusedPath = [focusedHTMObject, focusedLayer] + focusedPath = [focusedHTMObject, focusedLayer] - if self.focusedCell is not None: - self.focusedCell.resetFocus() # reset previous - self.focusedCell = newCellFocus - self.focusedCell.setFocus() + if self.gui.focusedCell is not None: + self.gui.focusedCell.resetFocus() # reset previous + self.gui.focusedCell = newCellFocus + self.gui.focusedCell.setFocus() # unfocus all for obj in self.base.HTMObjects.values(): @@ -242,34 +245,19 @@ def HandlePickedObject(self, obj): for obj in self.base.HTMObjects.values(): obj.DestroyDistalSynapses() - self.gui.focusedCell = self.focusedCell - self.gui.focusedPath = self.focusedPath + + self.gui.focusedPath = focusedPath self.gui.columnID = Layer.minicolumns.index(self.gui.focusedCell.column) self.gui.cellID = Layer.minicolumns[self.gui.columnID].cells.index(self.gui.focusedCell) - self.UpdateProximalAndDistalData() + self.base.UpdateProximalAndDistalData() self.gui.UpdateCellDescription() elif obj.getName() == "basement": self.testRoutine() - def UpdateProximalAndDistalData(self): - # -------- proximal and distal synapses ----------------------- - if self.gui.showProximalSynapses and self.gui.focusedCell is not None: - self.client.reqProximalData() - else: - for obj in self.base.HTMObjects.values(): - obj.DestroyProximalSynapses() - - #do not request distal data if we don't want to show them or if this layer doesn't have TM - if self.gui.showDistalSynapses and self.gui.focusedCell is not None: - self.client.reqDistalData() - else: - for obj in self.base.HTMObjects.values(): # destroy synapses if they not to be shown - obj.DestroyDistalSynapses() - # ----------------------------------------------------------- def Update(self): diff --git a/PandaVis/logs/dummy.txt b/PandaVis/logs/dummy.txt new file mode 100644 index 0000000..e69de29 diff --git a/PandaVis/main.py b/PandaVis/main.py deleted file mode 100644 index 28007d8..0000000 --- a/PandaVis/main.py +++ /dev/null @@ -1,251 +0,0 @@ -# -*- coding: utf-8 -*- - -from direct.showbase.ShowBase import ShowBase - -from pandaComm.client import SocketClient -import math - -import time -from objects.htmObject import cHTM -from gui import cGUI # Graphical user interface -from environment import cEnvironment # handles everything about the environment -from interaction import cInteraction # handles keys, user interaction etc.. -from direct.stdpy import threading -from panda3d.core import loadPrcFileData - -loadPrcFileData('', 'win-size 1600 900') - -import faulthandler; faulthandler.enable() - - -verbosityLow = 0 -verbosityMedium = 1 -verbosityHigh = 2 -FILE_VERBOSITY = ( - verbosityHigh -) # change this to change printing verbosity of this file - - -def printLog(txt, verbosity=verbosityLow): - if FILE_VERBOSITY >= verbosity: - print(txt) - - -class cApp(ShowBase): - - def __init__(self): - ShowBase.__init__(self) - self.speed = 40 - - # Mouse and camera movement init - self.mouseX_last = 0 - self.mouseY_last = 0 - self.rotateCamera = False - self.move_z = 50 - - self.env = cEnvironment(self) - - self.env.CreateBasement() # to not be lost if there is nothing around - self.env.SetupLights() - self.env.SetupCamera() - - width = self.win.getProperties().getXSize() - height = self.win.getProperties().getYSize() - - self.gui = cGUI( - width, - height, - self.loader, - visApp = self - ) - - self.client = SocketClient() - self.client.setGui(self.gui) - - self.interaction = cInteraction(self) - - self.interaction.SetupKeys() - self.interaction.SetupOnClick() - - self.taskMgr.add(self.update, "main loop") - self.accept(self.win.getWindowEvent(), self.interaction.onWindowEvent) - - self.HTMObjects = {} - self.allHTMobjectsCreated = False - self.oneOfObjectsCreationFinished = False - - self.gfxCreationThread= threading.Thread(target=self.gfxCreationWorker, args=()) - - - def updateHTMstate(self): - - cellDataWasUpdated = False - - if self.client.stateDataArrived or self.oneOfObjectsCreationFinished: - - printLog("Data change! Updating HTM state", verbosityMedium) - - self.gui.setIteration(self.client.serverData.iterationNo) - serverObjs = self.client.serverData.HTMObjects - - # if we get HTM data objects, iterate over received data - for obj in serverObjs: - - if not obj in self.HTMObjects: # its new HTM object - printLog("New HTM object arrived! Name:" + str(obj)) - # create HTM object - newObj = cHTM(self, self.loader, obj) - newObj.getNode().reparentTo(self.render) - - # create inputs - for inp in serverObjs[obj].inputs: - printLog("Creating input: " + str(inp), verbosityHigh) - newObj.CreateInput( - name=inp, - count=serverObjs[obj].inputs[inp].count, - rows=int(math.sqrt(serverObjs[obj].inputs[inp].count)), - ) - # create layers - for lay in serverObjs[obj].layers: - printLog("Creating layer: " + str(lay), verbosityHigh) - newObj.CreateLayer( - name=lay, - nOfColumnsPerLayer=serverObjs[obj].layers[lay].columnCount, - nOfCellsPerColumn=serverObjs[obj] - .layers[lay] - .cellsPerColumn, - ) - - self.HTMObjects[obj] = newObj - - self.gfxCreationThread.start() - # update HTM object - - # go through all incoming inputs - for i in serverObjs[obj].inputs: # dict - printLog("Updating state of input: " + str(i), verbosityHigh) - # find matching input in local structure - - self.HTMObjects[obj].inputs[i].UpdateState( - serverObjs[obj].inputs[i].bits, - serverObjs[obj].inputs[i].stringValue, - ) - - # go through all incoming layers - for l in serverObjs[obj].layers: # dict - if self.HTMObjects[obj].layers[l].gfxCreationFinished: - printLog("Updating state of layer: " + str(l), verbosityHigh) - # find matching layer in local structure - self.HTMObjects[obj].layers[l].UpdateState( - serverObjs[obj].layers[l].activeColumns, - serverObjs[obj].layers[l].activeCells, - serverObjs[obj].layers[l].winnerCells, - serverObjs[obj].layers[l].predictiveCells, - newStep = True, - showPredictionCorrectness=self.gui.showPredictionCorrectness, - showBursting = self.gui.showBursting - ) - - self.client.stateDataArrived = False - self.oneOfObjectsCreationFinished = False - cellDataWasUpdated = True - - if self.client.proximalDataArrived: - printLog("Proximal data arrived, updating synapses!", verbosityMedium) - - serverObjs = self.client.serverData.HTMObjects - - for obj in serverObjs: - - self.HTMObjects[obj].DestroyProximalSynapses() - - for l in serverObjs[obj].layers: # dict - printLog("data len:"+str(len(serverObjs[obj].layers[l].proximalSynapses)), verbosityHigh) - for syn in serverObjs[obj].layers[l].proximalSynapses: # array - - printLog("Layer:" + str(l), verbosityMedium) - printLog("proximalSynapses len:" + str(len(syn)), verbosityHigh) - - columnID = syn[0] - proximalSynapses = syn[1] - - # update columns with proximal Synapses - self.HTMObjects[obj].layers[l].minicolumns[ - columnID - ].CreateProximalSynapses( - serverObjs[obj].layers[l].proximalInputs, - self.HTMObjects[obj].inputs, - proximalSynapses, - ) - self.client.proximalDataArrived = False - - if self.client.distalDataArrived: - printLog("Distal data arrived, updating synapses!", verbosityMedium) - serverObjs = self.client.serverData.HTMObjects - - for obj in serverObjs: - - self.HTMObjects[obj].DestroyDistalSynapses() - - for l in serverObjs[obj].layers: # dict - printLog("len:"+str(len(serverObjs[obj].layers[l].distalSynapses)), verbosityHigh) - for syn in serverObjs[obj].layers[l].distalSynapses: # array - - printLog("Layer:" + str(l), verbosityMedium) - printLog("distalSynapses len:" + str(len(syn)), verbosityHigh) - - columnID = syn[0] - cellID = syn[1] - distalSynapses = syn[2] - - # update columns with distal Synapses - self.HTMObjects[obj].layers[l].minicolumns[ - columnID - ].cells[cellID].CreateDistalSynapses( - self.HTMObjects[obj], - self.HTMObjects[obj].layers[l], - distalSynapses, - serverObjs[obj].layers[l].distalInputs - ) - - self.client.distalDataArrived = False - - if cellDataWasUpdated: - self.interaction.UpdateProximalAndDistalData() - self.gui.UpdateCellDescription() - - - def update(self, task): - - self.gui.update() - self.interaction.Update() - self.updateHTMstate() - - return task.cont - - def gfxCreationWorker(self): - - time.sleep(5) # need to delay this, there was SIGSEG faults, probably during creation of objects thread collision happens - printLog("Starting GFX worker thread") - while(True): - # finishing HTM objects creation on the run - if not self.allHTMobjectsCreated: - allFinished = True - for obj in self.HTMObjects: - if not self.HTMObjects[obj].gfxCreationFinished: - allFinished = False - self.HTMObjects[obj].CreateGfxProgressively() - - if self.HTMObjects[obj].gfxCreationFinished: # it just finished GFX creation - self.oneOfObjectsCreationFinished = True - if allFinished: - self.allHTMobjectsCreated = True - printLog("GFX worker: all objects finished") - break - if self.gui.terminating: - break - printLog("GFX worker: quit") - -if __name__ == "__main__": - app = cApp() - app.run() diff --git a/PandaVis/objects/cell.py b/PandaVis/objects/cell.py index d381e2f..ced1b2c 100644 --- a/PandaVis/objects/cell.py +++ b/PandaVis/objects/cell.py @@ -5,6 +5,7 @@ @author: osboxes """ +import os from panda3d.core import LColor, CollisionBox, CollisionNode from panda3d.core import ( GeomVertexFormat, @@ -14,7 +15,7 @@ GeomLines, GeomNode, ) -import random + from Colors import * verbosityLow = 0 @@ -38,13 +39,14 @@ def __init__(self, column): self.transparency = 1.0 self.column = column # to be able to track column that this cell belongs to self.idx = -1 + self.showPredictionCorrectness = False def CreateGfx( self, loader, idx ): # idx is neccesary to be able to track it down for mouse picking self.idx = idx - self.__node = loader.loadModel("models/cube") + self.__node = loader.loadModel(os.path.join(os.getcwd(),"models/cube")) self.__node.setPos(0, 0, 0) self.__node.setScale(0.5, 0.5, 0.5) self.__node.setTag("clickable", str(idx)) # to be able to click on it @@ -57,20 +59,24 @@ def CreateGfx( self.UpdateState(False, False, False) - def UpdateState(self, active, predictive, winner, focused=False, presynapticFocus=False, newStep = False, showPredictionCorrectness = True): + def UpdateState(self, active, predictive, winner, focused=False, presynapticFocus=False, showPredictionCorrectness = False, prev_predictive = False): # determine if previous prediction was correct or not - if newStep: - if self.predictive and showPredictionCorrectness:#was predicted last step - if active:#now active - self.correctlyPredicted = True - self.falselyPredicted = False - else: - self.correctlyPredicted = False - self.falselyPredicted = True - else: # wasn't predictive previous step, so can't be correct or false + + if showPredictionCorrectness:#was predicted last step + if prev_predictive and active:#was predictive and now is active + self.correctlyPredicted = True + self.falselyPredicted = False + elif prev_predictive and not active: + self.correctlyPredicted = False + self.falselyPredicted = True + else:#wasn't predictive previous step, so can't be correct or false self.correctlyPredicted = False self.falselyPredicted = False + else: # we don't want to see correctness + self.correctlyPredicted = False + self.falselyPredicted = False + self.showPredictionCorrectness = showPredictionCorrectness # store it for purposes of description self.active = active self.predictive = predictive @@ -139,15 +145,13 @@ def getNode(self): def CreateDistalSynapses(self, HTMObject, layer, data, inputObjects): - for child in self.__node.getChildren(): - if child.getName() == "DistalSynapseLine": - child.removeNode() + self.DestroyDistalSynapses() printLog("Creating distal synapses", verbosityMedium) - printLog("EXTERNAL DISTAL:"+str(inputObjects)) - printLog("HTM inputs:"+str(HTMObject.inputs)) - printLog("HTM layers:" + str(HTMObject.layers)) + #printLog("EXTERNAL DISTAL:"+str(inputObjects)) + #printLog("HTM inputs:"+str(HTMObject.inputs)) + #printLog("HTM layers:" + str(HTMObject.layers)) #input object could be either input or layer instance for inputObj in inputObjects: @@ -160,7 +164,7 @@ def CreateDistalSynapses(self, HTMObject, layer, data, inputObjects): for segment in data: - for presynCellID in segment: + for presynCellID in data[segment]: cellID = presynCellID % layer.nOfCellsPerColumn colID = (int)(presynCellID / layer.nOfCellsPerColumn) @@ -227,8 +231,8 @@ def getDescription(self): txt += "Active:" + str(self.active)+"\n" txt += "Winner:" + str(self.winner) + "\n" txt += "Predictive:" + str(self.predictive)+"\n" - txt += "Correctly predicted:" + str(self.correctlyPredicted)+"\n" - txt += "Falsely predicted:" + str(self.falselyPredicted)+"\n" + txt += "Correctly predicted:" + 'N/A' if not self.showPredictionCorrectness else str(self.correctlyPredicted)+"\n" + txt += "Falsely predicted:" + 'N/A' if not self.showPredictionCorrectness else str(self.falselyPredicted)+"\n" return txt diff --git a/PandaVis/objects/input.py b/PandaVis/objects/input.py index 8085e3e..1276ab6 100644 --- a/PandaVis/objects/input.py +++ b/PandaVis/objects/input.py @@ -78,12 +78,6 @@ def CreateGfx(self, loader): def UpdateState(self, data, text): - # if len(data)!=self.count: - # print("Given data for input does not match number of bits in input!") - # print("A:"+str(self.count)+" B:"+str(len(data))) - # return - printLog("In UpdateState(): " + str(self.name), verbosityHigh) - self.__textValNodePath.getNode(0).setText(text) for i in range(len(self.inputBits)): @@ -100,8 +94,6 @@ def UpdateState(self, data, text): for i in range(len(self.inputBits)): # update all states self.inputBits[i].UpdateState() - printLog("Leaving UpdateState()") - def getNode(self): return self.__node diff --git a/PandaVis/objects/inputBit.py b/PandaVis/objects/inputBit.py index 07cad30..7d54eaa 100644 --- a/PandaVis/objects/inputBit.py +++ b/PandaVis/objects/inputBit.py @@ -5,7 +5,7 @@ @author: zz """ - +import os from panda3d.core import LColor, CollisionNode, CollisionBox from Colors import * # import random @@ -23,7 +23,7 @@ def __init__(self, parentObj): def CreateGfx(self, loader, idx): - self.__node = loader.loadModel("models/cube") + self.__node = loader.loadModel(os.path.join(os.getcwd(),"models/cube")) #self.__node.setRenderModeFilledWireframe(LColor(0, 0, 0, 1.0)) self.__node.setPos(0, 0, 0) self.__node.setScale(0.5, 0.5, 0.5) diff --git a/PandaVis/objects/layer.py b/PandaVis/objects/layer.py index c1a4aca..4d4ad61 100644 --- a/PandaVis/objects/layer.py +++ b/PandaVis/objects/layer.py @@ -78,7 +78,7 @@ def CreateGfxProgressively(self): else: self.text.setText(self.name+"(creating:"+str(int(100*createdCols/len(self.minicolumns)))+" %)") - def UpdateState(self, activeColumns, activeCells, winnerCells, predictiveCells, newStep = False, showPredictionCorrectness = False, showBursting = False): + def UpdateState(self, activeColumns, activeCells, winnerCells, predictiveCells, prevPredictiveCells, showPredictionCorrectness = False, showBursting = False): # print("COLUMNS SIZE:"+str(len(self.minicolumns))) #print("winners:"+str(winnerCells)) @@ -94,6 +94,10 @@ def UpdateState(self, activeColumns, activeCells, winnerCells, predictiveCells, isActive = cellID+(colID*self.nOfCellsPerColumn) in activeCells isWinner = cellID + (colID * self.nOfCellsPerColumn) in winnerCells isPredictive = cellID+(colID*self.nOfCellsPerColumn) in predictiveCells + if showPredictionCorrectness: + wasPredictive = cellID+(colID*self.nOfCellsPerColumn) in prevPredictiveCells + else: + wasPredictive = False if isPredictive: oneOfCellPredictive=True @@ -102,7 +106,7 @@ def UpdateState(self, activeColumns, activeCells, winnerCells, predictiveCells, nOfActiveCells = nOfActiveCells + 1 - self.minicolumns[colID].cells[cellID].UpdateState(active = isActive, predictive = isPredictive,winner = isWinner, newStep=newStep, showPredictionCorrectness = showPredictionCorrectness) + self.minicolumns[colID].cells[cellID].UpdateState(active = isActive, predictive = isPredictive,winner = isWinner, showPredictionCorrectness = showPredictionCorrectness, prev_predictive = wasPredictive) if showPredictionCorrectness: #get correct/false prediction info @@ -136,6 +140,18 @@ def updateWireframe(self, value): def getNode(self): return self.__node + def ShowProximalSynapses(self, column, permanences, inputNames, inputObj, thresholdConnected, showOnlyActive): + # update columns with proximal Synapses + self.minicolumns[ + column + ].CreateProximalSynapses( + inputNames, + inputObj, + permanences, + thresholdConnected, + createOnlyActive=showOnlyActive + ) + def DestroyProximalSynapses(self): for col in self.minicolumns: col.DestroyProximalSynapses() diff --git a/PandaVis/objects/minicolumn.py b/PandaVis/objects/minicolumn.py index 916ffb6..dac7907 100644 --- a/PandaVis/objects/minicolumn.py +++ b/PandaVis/objects/minicolumn.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import os from objects.cell import cCell from panda3d.core import NodePath, PandaNode, LODNode, LColor from panda3d.core import ( @@ -61,7 +62,7 @@ def CreateGfx(self, loader, idx): # self.__node.setTag('clickable',str(idx))#to be able to click on it - self.__columnBox = loader.loadModel("models/cube") + self.__columnBox = loader.loadModel(os.path.join(os.getcwd(),"models/cube")) self.__columnBox.setPos( 0, 0, -0.5 + (0 if len(self.cells) == 0 else len(self.cells)*(1+CELL_OFFSET) / 2) ) @@ -166,41 +167,43 @@ def updateWireframe(self, value): self.__columnBox.setRenderModeFilled() # -- Create proximal synapses - # inputObjects - list of names of inputs(areas) - # inputs - panda vis input object - # synapses - list of the second points of synapses (first point is this minicolumn) + # inputNames - list of names of inputs(areas) + # inputObj - panda vis input object + # permanences - list of the second points of synapses (first point is this minicolumn) # NOTE: synapses are now DENSE - def CreateProximalSynapses(self, inputObjects, inputs, synapses): - for child in self.__cellsNodePath.getChildren(): - if child.getName() == "ProximalSynapseLine": - child.removeNode() + def CreateProximalSynapses(self, inputNames, inputObj, permanences, thresholdConnected, createOnlyActive=False): - printLog("Creating proximal synapses", verbosityMedium) - printLog("To inputs called:" + str(inputObjects), verbosityMedium) - printLog("Synapses count:" + str(len(synapses)), verbosityMedium) - printLog("active:" + str(sum([i for i in synapses])), verbosityHigh) + self.DestroyProximalSynapses() - # inputs are divided into separate items in list - [input1,input2,input3] - # synapses are one united array [1,0,0,1,0,1,0...] + printLog("Creating proximal permanences", verbosityMedium) + printLog("To inputObj called:" + str(inputNames), verbosityMedium) + printLog("permanences count:" + str(len(permanences)), verbosityMedium) + printLog("active:" + str(sum([i for i in permanences])), verbosityHigh) + + # inputObj are divided into separate items in list - [input1,input2,input3] + # permanences are one united array [1,0,0,1,0,1,0...] # length is the same - # synapses can be connected to one input or to several inputs + # synapses can be connected to one input or to several inputObj # if to more than one - split synapses array synapsesDiv = [] offset = 0 - for inputObj in inputObjects: - synapsesDiv.append(synapses[offset : offset + inputs[inputObj].count]) - offset += inputs[inputObj].count + for inputName in inputNames: + synapsesDiv.append(permanences[offset : offset + inputObj[inputName].count]) + offset += inputObj[inputName].count for i in range(len(synapsesDiv)): # for each input object - inputs[inputObjects[i]].resetProximalFocus() # clear color highlight + inputObj[inputNames[i]].resetProximalFocus() # clear color highlight for y in range( len(synapsesDiv[i]) ): # go through every synapse and check if its connected (we are comparing permanences) - if synapsesDiv[i][y] != 0.0: + if synapsesDiv[i][y] >= thresholdConnected: + + if createOnlyActive and not inputObj[inputNames[i]].inputBits[y].active:# we want to show only active synapses + continue form = GeomVertexFormat.getV3() vdata = GeomVertexData("ProximalSynapseLine", form, Geom.UHStatic) @@ -208,18 +211,18 @@ def CreateProximalSynapses(self, inputObjects, inputs, synapses): vertex = GeomVertexWriter(vdata, "vertex") vertex.addData3f( - inputs[inputObjects[i]] + inputObj[inputNames[i]] .inputBits[y] .getNode() .getPos(self.__node) ) vertex.addData3f(0, 0, 0) # vertex.addData3f(self.__node.getPos()) - # printLog("Inputs:"+str(i)+"bits:"+str(y)) - # printLog(inputs[i].inputBits[y].getNode().getPos(self.__node)) + # printLog("inputObj:"+str(i)+"bits:"+str(y)) + # printLog(inputObj[i].inputBits[y].getNode().getPos(self.__node)) # highlight - inputs[inputObjects[i]].inputBits[ + inputObj[inputNames[i]].inputBits[ y ].setProximalFocus() # highlight connected bits @@ -238,7 +241,7 @@ def CreateProximalSynapses(self, inputObjects, inputs, synapses): nodePath.setRenderModeThickness(2) # color of the line - if inputs[inputObjects[i]].inputBits[y].active: + if inputObj[inputNames[i]].inputBits[y].active: nodePath.setColor(COL_PROXIMAL_SYNAPSES_ACTIVE) else: nodePath.setColor(COL_PROXIMAL_SYNAPSES_INACTIVE) @@ -277,4 +280,4 @@ def getDescription(self): txt += "One of cell is predictive:" + str(self.oneOfCellPredictive) + "\n" txt += "One of cell correctly predicted:" + str(self.oneOfCellCorrectlyPredicted) + "\n" txt += "One of cell false predicted:" + str(self.oneOfCellFalselyPredicted) + "\n" - return txt \ No newline at end of file + return txt diff --git a/PandaVis/pandaComm/client.py b/PandaVis/pandaComm/client.py deleted file mode 100644 index 7176ef5..0000000 --- a/PandaVis/pandaComm/client.py +++ /dev/null @@ -1,244 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import sys -import os - -sys.path.append( - os.path.dirname(os.path.dirname(os.path.realpath("__file__"))) -) # adds parent directory to path - -import socket, pickle, struct -import _thread -import time -from PandaVis.pandaComm.dataExchange import ServerData, CLIENT_CMD, SERVER_CMD - - -verbosityLow = 0 -verbosityMedium = 1 -verbosityHigh = 2 -FILE_VERBOSITY = ( - verbosityHigh -) # change this to change printing verbosity of this file -SLOW_DEBUG = False - - -def printLog(txt, verbosity=verbosityLow): - if FILE_VERBOSITY >= verbosity: - print(txt) - - -def PackData(cmd, data=[]): - d = [cmd, data] - # Pickle the object and send it to the server - # protocol must be specified to be able to work with py2 on server side - rawData = pickle.dumps(d, protocol=2) - - if len(rawData) % 4096 == 0: # if length of data is multiple of chunk size - # increase the data by 1 byte to prevent it - it causes problems in recv function - # on the client side - because client doesn't know how long data to expect - if isinstance(data, ServerData): - data.compensateSize.append(1) # increase size by some dummy bytes - d = [cmd, data] - rawData = pickle.dumps(d) # , protocol=2) - else: - printLog("Packed data is multiple of chunk size, but not known instance") - - return rawData - - -def send_one_message(sock, data): - length = len(data) - sock.sendall(struct.pack("!I", length)) - sock.sendall(data) - - -def recv_one_message(sock): - buffer = sock.recv(4) - if len(buffer)!=4: - raise BrokenPipeError("Size of sock.recv was not 4 bytes!"); - length, = struct.unpack("!I", buffer) - return sock.recv(length) - - -class SocketClient: - def __init__(self): - - _thread.start_new_thread(self.RunThread, ()) - - self.serverData = None - self.terminateClientThread = False - - self.stateDataArrived = False - self.proximalDataArrived = False - self.distalDataArrived = False - - self._reqProximalData = False - self._reqDistalData = False - - self._gui = None - - self.HOST = "localhost" - self.PORT = 50007 - - self.socket = None - self.connected = False - - self.tmrReq = 0 - - def setGui(self, gui): - self._gui = gui - - def reqProximalData(self): - self._reqProximalData=True - def reqDistalData(self): - self._reqDistalData=True - - def RunThread(self): - - # Create a socket connection, keep trying if no success - self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.socket.settimeout(5) - self.connected = False - - # s.send(SocketClient.PackData(CLIENT_CMD.REQ_DATA)) - while not self.terminateClientThread: - - if not self.connected:#try to reconnect each time (if server fails it can be restarted) - self.ConnectToServer() - - try: - # if time.time() - self.tmrReq > 1: # each 1 sec make request - printLog("Sending REQ", verbosityMedium) - send_one_message(self.socket, PackData(CLIENT_CMD.CMD_GET_STATE_DATA)) - #self.tmrReq = time.time() - - if self._gui.cmdRun: - send_one_message(self.socket, PackData(CLIENT_CMD.CMD_RUN)) - printLog("RUN req", verbosityMedium) - elif self._gui.cmdStop: - send_one_message(self.socket, PackData(CLIENT_CMD.CMD_STOP)) - printLog("STOP req", verbosityMedium) - elif self._gui.gotoReq >= 0: - send_one_message(self.socket, PackData(CLIENT_CMD.CMD_GOTO, self._gui.gotoReq)) - printLog("GOTO req", verbosityMedium) - self._gui.gotoReq = -1 - elif self._gui.cmdStepForward: - send_one_message(self.socket, PackData(CLIENT_CMD.CMD_STEP_FWD)) - printLog("STEP", verbosityMedium) - elif self._reqProximalData: - self._reqProximalData=False - send_one_message( - self.socket, - PackData( - CLIENT_CMD.CMD_GET_PROXIMAL_DATA, - [self._gui.focusedPath, self._gui.columnID], - ), - ) - printLog( - "GET proximal data for col:" + str(self._gui.focusedCell.column), - verbosityMedium, - ) - elif self._reqDistalData: - self._reqDistalData=False - send_one_message( - self.socket, - PackData( - CLIENT_CMD.CMD_GET_DISTAL_DATA, - [self._gui.focusedPath, self._gui.columnID, self._gui.cellID], - ), - ) - printLog( - "GET distal for cell: " + str(self._gui.cellID) + " on column: "+str(self._gui.columnID), - verbosityMedium, - ) - - self._gui.ResetCommands() - - printLog("Data begin receiving", verbosityHigh) - self.ReceiveData(self.socket) - printLog("Data received", verbosityHigh) - - except (ConnectionResetError, BrokenPipeError): - printLog("Connection was reset, probably by server side.") - self.connected = False - continue - - # send that we as a client are quitting - if self.connected: - send_one_message(self.socket, PackData(CLIENT_CMD.QUIT)) - - self.socket.close() - printLog("ClientThread terminated") - - def ConnectToServer(self): - printLog("Continuously trying connect to server...") - while not self.connected: - try: - try: - self.socket.connect((self.HOST, self.PORT)) - self.connected = True - except (TimeoutError,ConnectionRefusedError, ConnectionAbortedError): - time.sleep(1) - continue - except Exception as e: - printLog("Exception while trying connect to server:") - printLog(str(e)) - time.sleep(5) - continue - - printLog("Connected to server:" + self.HOST + ":" + str(self.PORT), verbosityLow) - - def ReceiveData(self, s): - - # wait till the previous data is processed! (in the Update() thread) - while self.stateDataArrived or self.proximalDataArrived or self.distalDataArrived: - continue - - rxRawData = b"" - - try: - rxRawData = recv_one_message(s) - - printLog("lenRaw:" + str(len(rxRawData)), verbosityHigh) - #printLog(rxRawData, verbosityHigh) - - if len(rxRawData) == 0: - printLog("Received data are empty!", verbosityHigh) - return - rxData = pickle.loads(rxRawData, encoding="latin1") - - printLog("lenRx:" + str(len(rxData)), verbosityHigh) - - printLog("RCV ID:" + str(rxData[0]), verbosityHigh) - if len(rxData) > 1: - printLog("RCV data:" + str(rxData[1]), verbosityHigh) - - - - if rxData[0] == SERVER_CMD.SEND_STATE_DATA: - self.serverData = rxData[1] - self.stateDataArrived = True - printLog("Data income", verbosityMedium) - - elif rxData[0] == SERVER_CMD.SEND_PROXIMAL_DATA: - self.serverData = rxData[1] - self.proximalDataArrived = True - printLog("proximal data arrived", verbosityMedium) - - elif rxData[0] == SERVER_CMD.SEND_DISTAL_DATA: - self.serverData = rxData[1] - self.distalDataArrived = True - printLog("distal data arrived", verbosityMedium) - - - elif rxData[0] == SERVER_CMD.NA: - printLog("Server has data not available", verbosityHigh) - if SLOW_DEBUG: - time.sleep(1) - else: - printLog("Unknown command:" + str(rxData[0])) - - except socket.timeout: - printLog("SocketTimeout", verbosityHigh) - return diff --git a/PandaVis/pandaComm/dataExchange.py b/PandaVis/pandaComm/dataExchange.py deleted file mode 100644 index 54114b2..0000000 --- a/PandaVis/pandaComm/dataExchange.py +++ /dev/null @@ -1,91 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Thu Feb 28 02:33:11 2019 - -@author: osboxes -""" -from enum import Enum - - -class ClientData(object): - def __init__(self): - self.a = 0 - self.b = 0 - - -class ServerData(object): - def __init__(self): - - self.HTMObjects = {} # dataHTMObject - - self.iterationNo = 0 - - self.compensateSize = [] # to compensate size by dummy bytes - - -# -------------------DATA OBJECTS --------------------------------------------- -# these data objects can be part of the serverData instance that is sent through -# the socket to the client -# client will determine what he can read by commands - - -class dataHTMObject(object): - def __init__(self): - self.layers = {} # dataLayer - self.inputs = {} # dataInput - - -class dataLayer(object): - def __init__(self, columnCount, cellsPerColumn): - - self.columnCount = columnCount - self.cellsPerColumn = cellsPerColumn - - self.activeColumns = [] # currently active columns (sparse) in this layer - self.winnerCells = [] - self.activeCells = [] - self.predictiveCells = [] - - #contains values of permanences - # array - [[columnID_a,[destinationID_a1,destinationID_a2,...]],[columnID_b,[destinationID_b1,destinationID_b2,...]]] - self.proximalSynapses = ( - [] - ) # first item in array is for what column, second is list of destination input bits - - # to what inputs are the synapses connected - self.proximalInputs = [] # [inputName1,inputName2,...] - - # array - [colID, cellID,[destinationID1,destinationID2,...]] - self.distalSynapses = ( - [] - ) # first item in array is for what column, second is list of destination cells in this layer - - # to what distal connections are connected (exluding itself, so these are external distal) - self.distalInputs = [] # [inputName1, inputName2] - can be input or layer - -class dataInput(object): - def __init__(self): - self.count = 0 - self.bits = [] # input SDRs (just indicies of active bits) - self.stringValue = ( - [] - ) # ordinary expressed value that is represented by input SDRs - - -class CLIENT_CMD(Enum): - QUIT = 0 - CMD_RUN = 1 - CMD_STOP = 2 - CMD_STEP_FWD = 3 - CMD_GET_STATE_DATA = 4 - CMD_GET_PROXIMAL_DATA = 5 - CMD_GET_DISTAL_DATA = 6 - CMD_GOTO = 7 - - -class SERVER_CMD(Enum): - NA = 0 - SEND_STATE_DATA = 1 - SEND_PROXIMAL_DATA = 2 - SEND_DISTAL_DATA = 3 diff --git a/PandaVis/pandaComm/server.py b/PandaVis/pandaComm/server.py deleted file mode 100644 index 7571ab1..0000000 --- a/PandaVis/pandaComm/server.py +++ /dev/null @@ -1,336 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -Created on Mon Aug 26 09:22:19 2019 - -@author: osboxes -""" - -import socket, pickle, struct -from socket import error as SocketError -import threading -from PandaVis.pandaComm.dataExchange import ServerData, CLIENT_CMD, SERVER_CMD -import numpy -import time - -verbosityLow = 0 -verbosityMedium = 1 -verbosityHigh = 2 -FILE_VERBOSITY = ( - verbosityMedium -) # change this to change printing verbosity of this file - - -def printLog(txt, verbosity=verbosityLow): - if FILE_VERBOSITY >= verbosity: - print(txt) - - -def PackData(cmd, data): - d = [cmd, data] - # Pickle the object and send it to the server - # protocol must be specified to be able to work with py2 on server side - rawData = pickle.dumps(d, protocol=2) - - if len(rawData) % 4096 == 0: # if length of data is multiple of chunck size - # increase the data by 1 byte to prevent it - it causes problems in recv function - # on the client side - because client doesn't know how long data to expect - if isinstance(data, ServerData): - data.compensateSize.append(1) # increase size by some dummy bytes - d = [cmd, data] - rawData = pickle.dumps(d) # , protocol=2) - else: - printLog("Packed data is multiple of chunck size, but not known instance") - - return rawData - - -def send_one_message(sock, data): - length = len(data) - sock.sendall(struct.pack("!I", length)) - sock.sendall(data) - - -def recv_one_message(sock): - lengthbuf = sock.recv(4) - length, = struct.unpack("!I", lengthbuf) - return sock.recv(length) - - -class PandaServer: - def __init__(self): - self.serverData = ServerData() - self.cmdRunOneStep = False - self.cmdRunInLoop = False - self.cmdGotoIteration = False - self.gotoIteration = 0 - self.currentIteration = 0 - self.newStateDataReadyForVis = False - self.mainThreadQuitted = False - - self.serverThread = ServerThread(self, 1, "ServerThread-1") - - self.spatialPoolers = {} # two level dict, {'HTMobject' : {'layer' : spatialPoolerInstance}} - self.temporalMemories = {} # two level dict, {'HTMobject' : {'layer' : temporalMemoryInstance}} - - def Start(self): - self.serverThread.start() - - def MainThreadQuitted(self): # if the parent thread is terminated, end this one too - self.mainThreadQuitted = True - self.serverThread.join() - - def NewStateDataReady(self): - self.newStateDataReadyForVis = True - - def RunServer(self): - HOST = "localhost" - PORT = 50007 - - s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - s.setsockopt( - socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 - ) # prevent "adress in use" exception - s.settimeout(5) - s.bind((HOST, PORT)) - s.listen(1) - - printLog("Server listening") - - clientConnected = False - - while not clientConnected and not self.mainThreadQuitted: - try: - conn, addr = s.accept() - conn.settimeout(5) - printLog("Connected by" + str(addr)) - clientConnected = True - newClient = True - except socket.timeout: - continue - - if not clientConnected: - printLog("Client is not connected anymore") - return - - quitServer = False - - while not quitServer and not self.mainThreadQuitted: - try: - rxRawData = recv_one_message(conn) - # rxRawData = conn.recv(4096) - - rxData = pickle.loads(rxRawData) - - # self.serverData = ServerData() # clear server data to start with blank structure - - if rxData[0] == CLIENT_CMD.CMD_GET_STATE_DATA: - # printLog("State data requested", verbosityHigh) - if self.newStateDataReadyForVis or newClient: - newClient = False - - send_one_message( - conn, PackData(SERVER_CMD.SEND_STATE_DATA, self.serverData) - ) - - self.newStateDataReadyForVis = False - else: - send_one_message( - conn, PackData(SERVER_CMD.NA, []) - ) # we dont have any new data for client - # printLog("But no new data available", verbosityHigh) - - elif rxData[0] == CLIENT_CMD.CMD_GET_PROXIMAL_DATA: - printLog("Proximal data req by client", verbosityMedium) - printLog(rxData, verbosityHigh) - - HTMObjectName = rxData[1][0][0] - layerName = rxData[1][0][1] - requestedCol = int(rxData[1][1]) - - printLog( - "HTM object:" - + str(HTMObjectName) - + " layerName:" - + str(layerName) - + " reqCol:" - + str(requestedCol), - verbosityMedium, - ) - # SP - proximal synapses - sp = self.spatialPoolers[HTMObjectName][layerName] - connectedSynapses = numpy.zeros( - sp.getNumInputs(), dtype=numpy.float32 - ) - sp.getPermanence(requestedCol, connectedSynapses, - sp.getSynPermConnected()) # sp.getConnectedSynapses(requestedCol, connectedSynapses) - - self.ClearNonStaticData() # clear previous data (e.g. for other layers) - - self.serverData.HTMObjects[HTMObjectName].layers[ - layerName - ].proximalSynapses = [[requestedCol, connectedSynapses]] - - printLog( - "Sending:" - + str( - self.serverData.HTMObjects[HTMObjectName] - .layers[layerName] - .proximalSynapses - ), - verbosityHigh, - ) - - send_one_message( - conn, PackData(SERVER_CMD.SEND_PROXIMAL_DATA, self.serverData) - ) - - printLog( - "Sent synapses of len:" + str(len(connectedSynapses)), - verbosityHigh, - ) - # printLog("GETTING CELL DATA:") - # printLog(self.serverData.connectedSynapses) - elif rxData[0] == CLIENT_CMD.CMD_GET_DISTAL_DATA: - printLog("Distal data req by client", verbosityMedium) - printLog(rxData, verbosityHigh) - - HTMObjectName = rxData[1][0][0] - layerName = rxData[1][0][1] - requestedColumn = int(rxData[1][1]) - requestedCell = int(rxData[1][2]) - - cellsPerColumn = self.serverData.HTMObjects[HTMObjectName].layers[layerName].cellsPerColumn - reqCellID = requestedColumn * cellsPerColumn + requestedCell - - printLog("Requested cell ID:" + str(reqCellID), verbosityMedium) - - if not layerName in self.temporalMemories[HTMObjectName]: - printLog("This layer doesn't have TM, can't request distal connections.. skipping") - continue - tm = self.temporalMemories[HTMObjectName][layerName] - - presynCells = getPresynapticCellsForCell(tm, reqCellID) - - # printLog("PRESYN CELLS:"+str(presynCells)) - # winners = tm.getWinnerCells() - - # print(winners) - - self.ClearNonStaticData() # clear previous data (e.g. for other layers) - - self.serverData.HTMObjects[HTMObjectName].layers[ - layerName - ].distalSynapses = [ - [requestedColumn, requestedCell, presynCells]] # sending just one pack for one cell - send_one_message( - conn, PackData(SERVER_CMD.SEND_DISTAL_DATA, self.serverData) - ) - - - elif rxData[0] == CLIENT_CMD.CMD_RUN: - self.cmdRunInLoop = True - printLog("RUN", verbosityHigh) - elif rxData[0] == CLIENT_CMD.CMD_STOP: - self.cmdRunInLoop = False - printLog("STOP", verbosityHigh) - elif rxData[0] == CLIENT_CMD.CMD_STEP_FWD: - self.cmdRunOneStep = True - printLog("STEP", verbosityHigh) - elif rxData[0] == CLIENT_CMD.CMD_GOTO: - self.cmdGotoIteration = True - self.gotoIteration = rxData[1] - printLog("GOTO:" + str(self.gotoIteration), verbosityHigh) - - if self.serverData.iterationNo >= self.gotoIteration: # handle when current iteration is above or equal requested - self.cmdGotoIteration = False - - elif rxData[0] == CLIENT_CMD.QUIT: - printLog("Client quitted!") - # quitServer=True - except struct.error as e: - printLog("StructError:") - printLog(e) - clientConnected = False - break - except socket.timeout: - printLog("SocketTimeout") - continue - except SocketError as e: - printLog("SocketError") - printLog(e) - clientConnected = False - break - except EOFError: - printLog("EOFError") - clientConnected = False - break - # except Exception as e: - # printLog("Exception" + str(sys.exc_info()[0])) - # printLog(str(e)) - - # quitServer=True - - printLog("Server quit") - conn.close() - - def ClearNonStaticData( - self): # we need to clean up previous data, for example last time distal connections was requested, but now state data is requested - - for obj in self.serverData.HTMObjects.values(): - for ly in obj.layers.values(): - ly.proximalSynapses = {} - ly.distalSynapses = {} - - ly.activeColumns = [] # currently active columns (sparse) in this layer - ly.activeCells = [] - ly.winnerCells = [] - ly.predictiveCells = [] - - for inp in obj.inputs.values(): - inp.bits = [] - - def BlockExecution(self): - if self.cmdGotoIteration: - if self.gotoIteration <= self.currentIteration: - self.cmdGotoIteration = False - - if not self.cmdGotoIteration: - printLog("One step finished") - while not self.cmdRunInLoop and not self.cmdRunOneStep and not self.cmdGotoIteration: - pass - self.cmdRunOneStep = False - printLog("Proceeding one step...") - - -def getPresynapticCellsForCell(tm, cellID): - start_time = time.time() - segments = tm.connections.segmentsForCell(cellID) - - synapses = [] - res = [] - for seg in segments: - for syn in tm.connections.synapsesForSegment(seg): - synapses += [syn] - - presynapticCells = [] - for syn in synapses: - presynapticCells += [tm.connections.presynapticCellForSynapse(syn)] - - res += [presynapticCells] - - printLog("getPresynapticCellsForCell() took %s seconds " % (time.time() - start_time), verbosityHigh) - return res - - -class ServerThread(threading.Thread): - def __init__(self, serverInstance, threadID, name): - threading.Thread.__init__(self) - self.threadID = threadID - self.name = name - self.serverInstance = serverInstance - - def run(self): - printLog("Starting " + self.name) - self.serverInstance.RunServer() - printLog("Exiting " + self.name) diff --git a/README.md b/README.md index 40c46a8..0e4f944 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,21 @@ +

+ +

+ # HTMpandaVis +**UPDATE 6/2020 - major change - SQLITE3 instead of TCP sockets + Dash plots visualization in web browser !** + **See presentation [video](https://youtu.be/c1aJq0p-9uY)!** Screenshots for visualization of the [2D recognition project](https://discourse.numenta.org/t/2d-object-recognition-project/5465/92) -![img1](img1.png) -![img2](img2.png) -![img2](img3.png) +![img1](images/img1.png) +![img2](images/img2.png) +![img2](images/img3.png) +![dash visualization](images/dashVis.png) This project aspires to create tool that helps **visualize HTM systems in 3D** by using opensource framework for 3D rendering https://www.panda3d.org/ -It should allow to see architecture of the system in 3D, e.g. connection of several layers and inputs and to see input representation, +It allows to see architecture of the system in 3D space, e.g. connection of several layers and inputs and to see input representation, activity of columns and even individual cells in each simulation step. User can observe vast scalable space by moving as "ghost" and interact with objects. It is supposed as tool for educational purpose or as an inspect tool. @@ -18,14 +25,24 @@ I was inspired by following: - [Highbrow](https://github.com/htm-community/highbrow) - [Sanity](https://github.com/htm-community/sanity-nupic) -The visualization is application written in Python3 and strictly separated from "computation script" by TCP sockets. +The visualization is application written purely in Python3. + +# How it works +* Data for visualization are generated by so called "baking". This process generates sqlite3 database file, optionally folder with binary dump files. +* User can open these data in HTMpandaVis and explore them -Currently the "computation script" is hotgym example using [htm.core](https://github.com/htm-community/htm.core) extended by -small amout of code to communicate with visualization. -That is also main idea, anybody can take his own current computation script, slightly modify it and use this vis tool. +You can also browse SQLite3 dabase file (.db) directly with ordinary browser such as [Sqlite Browser](https://sqlitebrowser.org/). -![Diagram](readmeDiagram.png) +## Baking +To bake your simulation, you must use [htm.core](https://github.com/htm-community/htm.core) and extend your script with small amount of code to do the baking. + +## Dash plots visualization + +HTMpandaVis can be used also to record custom dataStreams with pandaBaker and then visualize it in web browser. +[Dash plotly library](https://plotly.com/dash/) is used. These plots are **interactive!**. +For creating layout arrangement, axis and plot labels, there are JSON layout configuration files. They are located in HTMpandaVis\dashVis\layouts. +See hotgym example for more informations. # How to install on Linux @@ -37,7 +54,7 @@ Install htm.core (here building from source, see [repo readme](https://github.co ``` sudo apt-get install cmake git clone https://github.com/htm-community/htm.core.git -python3 setup.py install --user --force +python setup.py install --user --force ``` Install prerequisities & clone pandaVis @@ -46,81 +63,24 @@ sudo apt-get install python3-tk git clone https://github.com/htm-community/HTMpandaVis.git -python3 -m pip install -r requirements.txt +python -m pip install -r requirements.txt - #this installs pandaComm package for use with custom computation script -python3 setup.py + #this installs pandaBaker package for baking process +python setup.py ``` -# How to run +# Run example +There is hotgym example ported from [htm.core](https://github.com/htm-community/htm.core/tree/master/py/htm/examples) modified for baking. -Run server - example "hotgym" +1. To bake database just navigate into /HotgymExample folder and run hotgym.py: ``` cd HTMpandaVis/HotgymExample -python3 hotgym.py -``` - -Run client - pandaVis tool -``` -cd HTMpandaVis/PandaVis -python3 pandaVis.py -``` - -# How to use with your computation script -1. **import packages for pandaVis** -``` -from PandaVis.pandaComm.server import PandaServer -from PandaVis.pandaComm.dataExchange import ServerData, dataHTMObject, dataLayer, dataInput -``` -2. **Create pandaServer intance and start it at init of your script** +python hotgym.py ``` -pandaServer = PandaServer() # globally for example +database will be created inside folder /bakedDatabase -#in init phase of your script -pandaServer.Start() -BuildPandaSystem() -``` -3. **Announce that script finished** - -Call this method when your script is terminated. Needed to ensure that threads in pandaServer and the client itself are quit properly. -``` -pandaServer.MainThreadQuitted() -``` -4. **Build your HTM system** - -There is need to specify what is your HTM system architecture. It is simple done by using python dicts like following: -``` -def BuildPandaSystem(): - global serverData - serverData = ServerData() - serverData.HTMObjects["HTM1"] = dataHTMObject() - serverData.HTMObjects["HTM1"].inputs["SL_Consumption"] = dataInput() - serverData.HTMObjects["HTM1"].inputs["SL_TimeOfDay"] = dataInput() - - serverData.HTMObjects["HTM1"].layers["SensoryLayer"] = dataLayer( - default_parameters["sp"]["columnCount"], - default_parameters["tm"]["cellsPerColumn"], - ) - serverData.HTMObjects["HTM1"].layers["SensoryLayer"].proximalInputs = [ - "SL_Consumption", - "SL_TimeOfDay", - ] -``` -This means that we have 1 *HTM object* containing one *SensoryLayer* and two inputs *SL_Consumption* and *SL_TimeOfDay* connected to layer as proximal inputs. - -5. **Update values each cycle** - -Fill in values that you want to visualize in each execution cycle, like: -``` -serverData.HTMObjects["HTM1"].inputs["SL_Consumption"].stringValue = "consumption: {:.2f}".format(consumption) -serverData.HTMObjects["HTM1"].inputs["SL_Consumption"].bits = consumptionBits.sparse -serverData.HTMObjects["HTM1"].inputs["SL_Consumption"].count = consumptionBits.size -# and so on for other objects.... - -pandaServer.NewStateDataReady()# say to pandaServer that it has new data -``` -6. **Block your execution with following method, if you want to control the run/stepping from pandaVis** +2. Run client - pandaVis tool ``` -pandaServer.BlockExecution() +cd HTMpandaVis +python run.py ``` -See example in "HotgymExample" folder for implementation. -Also see "/pandaComm/dataExchange.py" file for details what can be communicated. +And choose run 3D explorer or "run both" if you want to run dash visualization also. diff --git a/dashVis/__init__.py b/dashVis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/dashVis/assets/bootstrap.min.css b/dashVis/assets/bootstrap.min.css new file mode 100644 index 0000000..5b96335 --- /dev/null +++ b/dashVis/assets/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.4.1 (https://getbootstrap.com/) + * Copyright 2011-2019 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;-moz-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{font-size:2em;margin:.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:"Glyphicons Halflings";src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format("embedded-opentype"),url(../fonts/glyphicons-halflings-regular.woff2) format("woff2"),url(../fonts/glyphicons-halflings-regular.woff) format("woff"),url(../fonts/glyphicons-halflings-regular.ttf) format("truetype"),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format("svg")}.glyphicon{position:relative;top:1px;display:inline-block;font-family:"Glyphicons Halflings";font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:"\2014 \00A0"}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:""}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:"\00A0 \2014"}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.row-no-gutters{margin-right:0;margin-left:0}.row-no-gutters [class*=col-]{padding-right:0;padding-left:0}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-appearance:none;-moz-appearance:none;appearance:none}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=checkbox]:focus,input[type=file]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s,-webkit-box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=datetime-local].form-control,input[type=month].form-control,input[type=time].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],.input-group-sm input[type=time],input[type=date].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm,input[type=time].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],.input-group-lg input[type=time],input[type=date].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg,input[type=time].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;margin-bottom:0;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);opacity:.65;-webkit-box-shadow:none;box-shadow:none}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;background-image:none;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;background-image:none;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;background-image:none;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;background-image:none;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;background-image:none;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;background-image:none;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1);-webkit-overflow-scrolling:touch}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-right:15px;margin-top:8px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-right:-15px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);margin-top:8px;margin-bottom:8px}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0%;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{padding:0;cursor:pointer;background:0 0;border:0;-webkit-appearance:none;-moz-appearance:none;appearance:none}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%);-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out,-o-transform .3s ease-out}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5);outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:12px;filter:alpha(opacity=0);opacity:0}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.42857143;line-break:auto;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;font-size:14px;background-color:#fff;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2)}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover>.arrow{border-width:11px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:-webkit-transform .6s ease-in-out;transition:transform .6s ease-in-out;transition:transform .6s ease-in-out,-webkit-transform .6s ease-in-out,-o-transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);left:0}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);left:0}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0);left:0}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;outline:0;filter:alpha(opacity=90);opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:"\2039"}.carousel-control .icon-next:before{content:"\203a"}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/dashVis/dashVis.py b/dashVis/dashVis.py new file mode 100644 index 0000000..b35b6ad --- /dev/null +++ b/dashVis/dashVis.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- + +import os +import json +import math +import random +import pandas as pd + +import dash +import dash_core_components as dcc +import dash_html_components as html +import plotly.graph_objects as go +from plotly.subplots import make_subplots +import plotly.graph_objects as go + +from bakeReader.bakeReader import BakeReader + + +class cDashVis(object): + def __init__(self): + self.bakeReader = None + + #external_stylesheets = ['https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css'] + self.app = dash.Dash(__name__,) #external_stylesheets=external_stylesheets) + + self.colors = { + 'background': '#111111', + 'text': '#7FDBFF' + } + + number_of_colors = 30 + random.seed(1) + print(random.randint(1,50)) + self.randomColors = ["#" + ''.join([random.choice('0123456789ABCDEF') for j in range(6)]) + for i in range(number_of_colors)] + + + def run(self, databaseFilePath, layout): + + self.bakeReader = BakeReader(databaseFilePath) + self.bakeReader.OpenDatabase() + self.bakeReader.LoadDataStreams() + + + with open(os.path.join(os.getcwd(),'..','dashVis','layouts',layout+'.txt')) as f: + cfgLayout = json.load(f) + + plotsPerRow = cfgLayout['plotsPerRow'] + + + figures = [] + cnt = 0 + for stream in cfgLayout['streams']: + try: + data = self.bakeReader.dataStreams[stream['name']].allData + except KeyError: + print('Requested data stream "'+str(stream['name'])+'" was not found in the database!') + return + + if stream['type'] == 'line': + fig = go.Figure(data=go.Scatter(x=data[0, :], y=data[1, :],line=dict(color=self.randomColors[cnt]))) + figures.append([stream['name'],fig]) + + # Set theme, margin, and annotation in layout + fig.update_layout( + title=stream['name'], + xaxis_title="iteration", + yaxis_title=stream['yaxis'], + template="plotly_dark", + #margin=dict(r=10, t=25, b=40, l=60), + # annotations=[ + # dict( + # text="abc Source: NOAA", + # showarrow=False, + # xref="paper", + # yref="paper", + # x=0, + # y=0) + # ] + ) + cnt+=1 + if cnt>=len(self.randomColors): + cnt=0 + + + + columnClassName = "col-sm-12" + if plotsPerRow == 3: + columnClassName = "col-sm-4" + elif plotsPerRow == 2: + columnClassName = "col-sm-6" + + graphs = [html.Div( + ([html.Div([dcc.Graph( + id='plot_'+x[0], + figure=x[1] + )], className=columnClassName) for x in figures] + ),className="row") + ] + + childs = [ + html.H4( + children='Dash vis', + style={ + 'textAlign': 'center', + 'color': self.colors['text'] + } + ),] + + childs += graphs + + + self.app.layout = html.Div(style={'backgroundColor': self.colors['background']}, children=childs) + + self.app.run_server(debug=True, use_reloader=False) + +if __name__ == '__main__': + dash = cDashVis() + dash.run() + diff --git a/dashVis/layouts/myLayout1.txt b/dashVis/layouts/myLayout1.txt new file mode 100644 index 0000000..67b61e9 --- /dev/null +++ b/dashVis/layouts/myLayout1.txt @@ -0,0 +1,49 @@ +{"plotsPerRow" : 2, +"streams": [{"name" : "rawAnomaly", + "type" : "line", + "yaxis": "anomaly"}, + {"name" : "powerConsumption", + "type" : "line", + "yaxis": "kWh"}, + {"name" : "numberOfWinnerCells", + "type" : "line", + "yaxis": "cells"}, + + {"name" : "numberOfPredictiveCells", + "type" : "line", + "yaxis": "cells"}, + {"name" : "consumptionInput_sparsity", + "type" : "line", + "yaxis": "%"}, + {"name" : "dateInput_sparsity", + "type" : "line", + "yaxis": "%"}, + + {"name" : "consumptionInput_overlap_with_prev_step", + "type" : "line", + "yaxis": "cells"}, + {"name" : "dateInput_overlap_with_prev_step", + "type" : "line", + "yaxis": "cells"}, + {"name" : "Layer1_SP_overlap_metric", + "type" : "line", + "yaxis": "avg cells"}, + + {"name" : "Layer1_TM_overlap_metric", + "type" : "line", + "yaxis": "avg cells"}, + {"name" : "Layer1_SP_activation_frequency", + "type" : "line", + "yaxis": "Hz"}, + {"name" : "Layer1_TM_activation_frequency", + "type" : "line", + "yaxis": "Hz"}, + + {"name" : "Layer1_SP_entropy", + "type" : "line", + "yaxis": "entropy"}, + {"name" : "Layer1_TM_entropy", + "type" : "line", + "yaxis": "entropy"} + ] +} diff --git a/dashVis/layouts/myLayout2.txt b/dashVis/layouts/myLayout2.txt new file mode 100644 index 0000000..06315f0 --- /dev/null +++ b/dashVis/layouts/myLayout2.txt @@ -0,0 +1,13 @@ +{"plotsPerRow" : 3, +"streams": [{"name" : "rawAnomaly", + "type" : "line", + "yaxis": "anomaly"}, + {"name" : "powerConsumption", + "type" : "line", + "yaxis": "kWh"}, + {"name" : "numberOfWinnerCells", + "type" : "line", + "yaxis": "cells"} + ] + +} diff --git a/images/HTMpandaVis.png b/images/HTMpandaVis.png new file mode 100644 index 0000000..bb7b41f Binary files /dev/null and b/images/HTMpandaVis.png differ diff --git a/images/dashVis.png b/images/dashVis.png new file mode 100644 index 0000000..ba060da Binary files /dev/null and b/images/dashVis.png differ diff --git a/img1.png b/images/img1.png similarity index 100% rename from img1.png rename to images/img1.png diff --git a/img2.png b/images/img2.png similarity index 100% rename from img2.png rename to images/img2.png diff --git a/img3.png b/images/img3.png similarity index 100% rename from img3.png rename to images/img3.png diff --git a/not used/poolWorkers.py b/not used/poolWorkers.py new file mode 100644 index 0000000..aec8f9f --- /dev/null +++ b/not used/poolWorkers.py @@ -0,0 +1,69 @@ +# ---------------- DISTAL SYNAPSES ---------------------------------- + timeStartDistalSynapsesCalc = time.time() + tm = layer.tm + if self.bakeDistalSynapses: + if tm is None: + Log("This layer doesn't have TM, can't get distal synapses.. skipping") + continue + + columnCount = layer.params['sp_columnCount'] + oneBatchSize = int(columnCount/CPU_CORES) + split_startColIdx = [x*oneBatchSize for x in range(CPU_CORES)] + + # the division can be with remainder so add to the last thread the rest + if split_startColIdx[CPU_CORES-1]+oneBatchSize> "+str(s)+"\n") diff --git a/pandaBaker/dataStructs.py b/pandaBaker/dataStructs.py new file mode 100644 index 0000000..7e9bec4 --- /dev/null +++ b/pandaBaker/dataStructs.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +import numpy as np + +def Params(sp, tm): + + spPars={} + tmPars={} + if sp is not None: + + sp_inputDims_x = sp.getInputDimensions()[0] + sp_inputDims_y = sp.getInputDimensions()[1] if len(sp.getInputDimensions()) > 1 else 1 + sp_columnDimensions_x = sp.getColumnDimensions()[0] + sp_columnDimensions_y = sp.getColumnDimensions()[1] if len(sp.getColumnDimensions()) > 1 else 1 + + sp_columnCount = sp_columnDimensions_x * sp_columnDimensions_y; + + spPars = {'sp_inputDimensions_x': sp_inputDims_x,'sp_inputDimensions_y': sp_inputDims_y, + 'sp_columnCount' : sp_columnCount, + 'sp_columnDimensions_x': sp_columnDimensions_x, 'sp_columnDimensions_y': sp_columnDimensions_y, + 'sp_potentialPct': sp.getPotentialPct(), 'sp_potentialRadius': sp.getPotentialRadius(), + 'sp_globalInhibition': sp.getGlobalInhibition(), 'sp_localAreaDensity': sp.getLocalAreaDensity(), + 'sp_synPermInactiveDec': sp.getSynPermInactiveDec(), 'sp_synPermActiveInc': sp.getSynPermActiveInc(), + 'sp_synPermConnected': sp.getSynPermConnected(), 'sp_boostStrength': sp.getBoostStrength(), + 'sp_wrapAround': sp.getWrapAround()} + + if tm is not None: + + tmPars = {'tm_activationThreshold': tm.getActivationThreshold(), + 'tm_cellsPerColumn': tm.getCellsPerColumn(), + 'tm_initialPerm': tm.getInitialPermanence(), + 'tm_maxSegmentsPerCell': tm.getMaxSegmentsPerCell(), + 'tm_maxSynapsesPerSegment': tm.getMaxSynapsesPerSegment(), + 'tm_minThreshold': tm.getMinThreshold(), + 'tm_newSynapseCount': tm.getMaxNewSynapseCount(), + 'tm_permanenceDec': tm.getPermanenceDecrement(), + 'tm_permanenceInc': tm.getPermanenceIncrement() + } + + return {**spPars, **tmPars} + +class cLayer(object): + def __init__(self, sp=None, tm=None): + # static vars ---------------------------------------------------------------------- + self.sp = sp + self.tm = tm + + self.params = Params(self.sp, self.tm) + + # to what inputs are the synapses connected + self.proximalInputs = [] # [inputName1,inputName2,...] + # to what distal connections are connected (exluding itself, so these are external distal) + self.distalInputs = [] # [inputName1, inputName2] - can be input or layer + + + + # dynamic vars ---------------------------------------------------------------------- + self.activeColumns = np.empty(0) # currently active columns (sparse) in this layer + self.winnerCells = np.empty(0) + self.activeCells = np.empty(0) + self.predictiveCells = np.empty(0) + + # proximal synapses - contains values of permanences + # dict of numpy arrays, e.g. proximalSynapses[0] is numpy array for column ID 0 + self.proximalSynapses = {} + + # synapses - contains values of permanences + # dict of numpy arrays, e.g. distalSynapses[0] is numpy array for cell ID 0 + self.distalSynapses = {} + + +class cInput(object): + def __init__(self, size): + #static vars ----------- + self.size = size + + #dynamic vars ---------- + self.bits = np.empty(0) # input SDRs (just indicies of active bits) + self.stringValue = "" # ordinary expressed value that is represented by input SDRs + + +class cDataStream(object): + def __init__(self, dataType="REAL"): + self.dataType = dataType + self.value = None + + self.allData = None + + diff --git a/pandaBaker/logs/dummy.txt b/pandaBaker/logs/dummy.txt new file mode 100644 index 0000000..e69de29 diff --git a/pandaBaker/pandaBaker.py b/pandaBaker/pandaBaker.py new file mode 100644 index 0000000..0b722ce --- /dev/null +++ b/pandaBaker/pandaBaker.py @@ -0,0 +1,141 @@ +# -*- coding: utf-8 -*- + +from pandaBaker.bakerDatabase import Database +from pandaBaker.dataStructs import cLayer,cInput,cDataStream + +import numpy as np +import os +import time +from htm.bindings.algorithms import TemporalMemory +import multiprocessing as mp +from multiprocessing import Pool +from multiprocessing import get_context + +CPU_CORES = 4 + +class PandaBaker(object): + def __init__(self, databaseFilePath): + self.databaseFilePath = databaseFilePath + self.db = None + + self.layers = {} # can contain cLayer instances + self.inputs = {} # can contain cInput instances + self.dataStreams = {} # can contain cDataStream instances + + #flags what to bake + self.bakeProximalSynapses = True + self.bakeDistalSynapses = True + + # for raw anomaly calculation + self.previousPredictiveCells = {} # dict storing predictive cells for previous timestamp for each layer + + + def PrepareDatabase(self): + #create database file, delete if exists + if os.path.exists(self.databaseFilePath): + Log("database file "+self.databaseFilePath+" exists, deleting...") + os.remove(self.databaseFilePath) + else: + Log("Creating new database file:"+self.databaseFilePath) + + self.db = Database(self.databaseFilePath) # connect to the database + + + # STATIC tables creation ----------------------------------------------------------------------------- + self.db.CreateTable('connections', "input TEXT, layer TEXT, type TEXT") + + + self.db.CreateTable('par_inputs','name TEXT, size INTEGER') + for inp in self.inputs: + self.db.Insert('par_inputs', [inp, str(self.inputs[inp].size)]) + + + for ly in self.layers: + tableName = 'par_layers_'+ly + self.db.CreateTable(tableName, "name TEXT, value REAL") + self.db.InsertParameters(tableName, self.layers[ly].params) + #create table for defining to what inputs these layers are connected + for pi in self.layers[ly].proximalInputs: + self.db.Insert("connections", [pi, ly, 'proximal']) + for pi in self.layers[ly].distalInputs: + self.db.Insert("connections", [pi, ly, 'distal']) + + # DYNAMIC tables creation ----------------------------------------------------------------------------- + + for inp in self.inputs: + self.db.CreateTable('inputs_'+inp, "iteration INTEGER, value TEXT, data SDR") + + for ly in self.layers: + self.db.CreateTable('layer_activeColumns_'+ly, "iteration INTEGER, data SDR") + self.db.CreateTable('layer_predictiveCells_'+ly, "iteration INTEGER, data SDR") + self.db.CreateTable('layer_winnerCells_'+ly, "iteration INTEGER, data SDR") + self.db.CreateTable('layer_activeCells_'+ly, "iteration INTEGER, data SDR") + self.db.CreateTable('layer_proximalSynapses_'+ ly, "iteration INTEGER, column INTEGER, data array")#using float numpy array, not sparse SDR + self.db.CreateTable('layer_distalSynapses_' + ly, "iteration INTEGER, column INTEGER, cell INTEGER, segment INTEGER, data SDR") + + # data streams ---------------- + for pl in self.dataStreams: + if(pl.find(' ')!=-1): + raise RuntimeError("Name of datastream can't contain whitespaces. Name:"+str(pl)) + + tableName = 'dataStream_' + pl + self.db.CreateTable(tableName, "iteration INTEGER PRIMARY KEY, value " + self.dataStreams[pl].dataType) + + self.db.conn.commit() + + def CommitBatch(self):# writes to the file, do it reasonably, it takes time + self.db.conn.commit() + + def StoreIteration(self, iteration): + # store input states + for inp in self.inputs: + self.db.InsertDataArray2('inputs_'+inp, iteration, self.inputs[inp].stringValue, self.inputs[inp].bits) + #layer states + for ly in self.layers: + self.db.InsertDataArray('layer_activeColumns_' + ly, + iteration, self.layers[ly].activeColumns) + self.db.InsertDataArray('layer_predictiveCells_' + ly, + iteration, self.layers[ly].predictiveCells) + self.db.InsertDataArray('layer_winnerCells_' + ly, + iteration, self.layers[ly].winnerCells) + self.db.InsertDataArray('layer_activeCells_' + ly, + iteration, self.layers[ly].activeCells) + + layer = self.layers[ly] + # ---------------- PROXIMAL SYNAPSES ---------------------------------- + sp = layer.sp + if sp is not None and self.bakeProximalSynapses: + + layer.proximalSynapses = [] # erase + for col in range(layer.params['sp_columnCount']): + #proximal synapses + synapses = np.zeros( + sp.getNumInputs(), dtype=np.float32 + ) + sp.getPermanence(col, synapses, + 0.0)#get all permanences + + #layer.proximalSynapses.append(synapses) no need to store it probably + self.db.InsertDataArray2('layer_proximalSynapses_' + ly, + iteration, col, synapses) + + + layer.tm.saveToFile(os.path.join(os.path.splitext(self.databaseFilePath)[0]+"_distalDump",""+str(ly)+"_"+str(iteration)+".dump")) + + # ---------------- DATA STREAMS ----------------------------------- + + for pl in self.dataStreams: + tableName = 'dataStream_' + pl + + if(type(self.dataStreams[pl].value)not in [float, int]): + raise RuntimeError("Datatype:"+str(type(self.dataStreams[pl].value))+" is not supported!") + self.db.InsertDataArray(tableName, iteration, self.dataStreams[pl].value) + + +def Log(s): + print(str(s)) + from datetime import datetime + dateStr=datetime.now().strftime('%Y-%m-%d %H:%M:%S') + with open("logs/pandaBaker.log","a") as file: + file.write(dateStr+" >> "+str(s)+"\n") + diff --git a/readmeDiagram.png b/readmeDiagram.png deleted file mode 100644 index b39074e..0000000 Binary files a/readmeDiagram.png and /dev/null differ diff --git a/requirements.txt b/requirements.txt index 5c265a3..aa028ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ numpy>=1.15 matplotlib>=3.1.1 panda3d>=1.10.4.1 pysimplegui>=4.4.1 +dash>=1.12.0 diff --git a/run.py b/run.py new file mode 100644 index 0000000..ffb7982 --- /dev/null +++ b/run.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +import os +import sys +#change working directory to pandaVis folder +abspath = os.path.abspath(__file__) +workDir = os.path.dirname(abspath) +workDir = os.path.join(workDir,'PandaVis') +os.chdir(workDir) + +sys.path.append(os.getcwd()) + +from entryWindow import cEntryWindow +from Explorer3D import cExplorer3D +from dashVis.dashVis import cDashVis +import threading + +if __name__ == "__main__": + entryWin = cEntryWindow() + entryWin.Show() + + if entryWin.command == 'terminate': + print("App terminated") + + if entryWin.command == '-runBoth-': + print("RUN DASH in thread") + dashVis = cDashVis() + dashThread = threading.Thread(target=dashVis.run,args=(entryWin.databaseFilePath, entryWin.dashLayout)) + dashThread.start() + + if entryWin.command == '-runDash-': + print("RUN DASH") + dashVis = cDashVis() + dashVis.run(entryWin.databaseFilePath, entryWin.dashLayout) + + if entryWin.command == '-run3Dexplorer-' or entryWin.command == '-runBoth-': + print("RUN 3D explorer") + app = cExplorer3D(entryWin.databaseFilePath) + app.run() + + + + + diff --git a/setup.py b/setup.py index 79d8c3f..4a29107 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,8 @@ from setuptools import setup, find_packages setup(name='HTMpandaVis', - version='0.2', - packages = ["PandaVis/pandaComm"], + version='0.1', + packages = ["pandaBaker"], author="Zbysek Zapadlik from HTM community", url="https://github.com/htm-community/HTMpandaVis", python_requires='>=3.6', - ) \ No newline at end of file + )