From 50159b3149c21a818e5b3e81962287de30af7d10 Mon Sep 17 00:00:00 2001 From: Attila Bagoly Date: Tue, 19 Jul 2016 11:02:09 +0200 Subject: [PATCH 01/14] added GetCorrelation matrix to TMVA::DataLoader --- tmva/tmva/inc/TMVA/DataLoader.h | 3 +++ tmva/tmva/src/DataLoader.cxx | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/tmva/tmva/inc/TMVA/DataLoader.h b/tmva/tmva/inc/TMVA/DataLoader.h index 8d9c78665f340..4dfc794b4087e 100644 --- a/tmva/tmva/inc/TMVA/DataLoader.h +++ b/tmva/tmva/inc/TMVA/DataLoader.h @@ -49,6 +49,7 @@ class TFile; class TTree; class TDirectory; +class TH2; namespace TMVA { @@ -168,6 +169,8 @@ namespace TMVA { std::vector> SplitSets(std::vector& oldSet, int seedNum, int numFolds); const DataSetInfo& GetDefaultDataSetInfo(){ return DefaultDataSetInfo(); } + + TH2* GetCorrelationMatrix(const TString& className); //Copy method use in VI and CV DEPRECATED: you can just call Clone DataLoader *dl2=(DataLoader *)dl1->Clone("dl2") DataLoader* MakeCopy(TString name); diff --git a/tmva/tmva/src/DataLoader.cxx b/tmva/tmva/src/DataLoader.cxx index 8c37d08f574c8..db74996ae15b0 100644 --- a/tmva/tmva/src/DataLoader.cxx +++ b/tmva/tmva/src/DataLoader.cxx @@ -749,5 +749,13 @@ void TMVA::DataLoaderCopy(TMVA::DataLoader* des, TMVA::DataLoader* src) } } +//_______________________________________________________________________ +TH2* TMVA::DataLoader::GetCorrelationMatrix(const TString& className) +{ + //returns the correlation matrix of datasets + const TMatrixD * m = DefaultDataSetInfo().CorrelationMatrix(className); + return DefaultDataSetInfo().CreateCorrelationMatrixHist(m, + "CorrelationMatrix"+className, "Correlation Matrix ("+className+")"); +} From 6900796a2291e01a3c481276eeb7c4027d9d1d33 Mon Sep 17 00:00:00 2001 From: Attila Bagoly Date: Tue, 19 Jul 2016 21:48:23 +0200 Subject: [PATCH 02/14] interactive training in jupyter envrionment for all method. MLP, DNN, BDT has error graphs --- tmva/tmva/inc/TMVA/FitterBase.h | 11 ++++ tmva/tmva/inc/TMVA/MethodBase.h | 62 +++++++++++++++++++ tmva/tmva/inc/TMVA/NeuralNet.h | 18 ++++++ tmva/tmva/inc/TMVA/NeuralNet.icc | 10 ++++ tmva/tmva/inc/TMVA/SVWorkingSet.h | 10 ++++ tmva/tmva/inc/TMVA/SimulatedAnnealing.h | 9 +++ tmva/tmva/src/GeneticFitter.cxx | 3 + tmva/tmva/src/MCFitter.cxx | 3 + tmva/tmva/src/MethodBDT.cxx | 15 ++++- tmva/tmva/src/MethodBase.cxx | 69 ++++++++++++++++++++++ tmva/tmva/src/MethodCFMlpANN.cxx | 3 + tmva/tmva/src/MethodCuts.cxx | 11 ++++ tmva/tmva/src/MethodDNN.cxx | 7 +++ tmva/tmva/src/MethodDT.cxx | 1 + tmva/tmva/src/MethodFDA.cxx | 1 + tmva/tmva/src/MethodFisher.cxx | 2 + tmva/tmva/src/MethodHMatrix.cxx | 1 + tmva/tmva/src/MethodKNN.cxx | 2 + tmva/tmva/src/MethodLD.cxx | 2 + tmva/tmva/src/MethodLikelihood.cxx | 1 + tmva/tmva/src/MethodMLP.cxx | 14 +++++ tmva/tmva/src/MethodPDEFoam.cxx | 1 + tmva/tmva/src/MethodPDERS.cxx | 1 + tmva/tmva/src/MethodRuleFit.cxx | 1 + tmva/tmva/src/MethodSVM.cxx | 6 ++ tmva/tmva/src/SVWorkingSet.cxx | 2 + tmva/tmva/src/SimulatedAnnealing.cxx | 2 + tmva/tmva/src/SimulatedAnnealingFitter.cxx | 3 + 28 files changed, 270 insertions(+), 1 deletion(-) diff --git a/tmva/tmva/inc/TMVA/FitterBase.h b/tmva/tmva/inc/TMVA/FitterBase.h index d71afda3fb4fb..8a7d511834378 100644 --- a/tmva/tmva/inc/TMVA/FitterBase.h +++ b/tmva/tmva/inc/TMVA/FitterBase.h @@ -75,6 +75,13 @@ namespace TMVA { // remove namespace in name const char* GetName() const { return fClassName; } + // setting up variables for JsMVA interactive training + void SetIPythonInteractive(bool* ExitFromTraining, UInt_t *fIPyMaxIter_, UInt_t *fIPyCurrentIter_){ + fExitFromTraining = ExitFromTraining; + fIPyMaxIter = fIPyMaxIter_; + fIPyCurrentIter = fIPyCurrentIter_; + } + protected: // need to implement option declaration @@ -89,6 +96,10 @@ namespace TMVA { TString fClassName; // remove TMVA:: from TObject name + // variables needed by JsMVA + UInt_t *fIPyCurrentIter=nullptr, *fIPyMaxIter=nullptr; + bool* fExitFromTraining; + ClassDef(FitterBase,0); // Baseclass for fitters }; diff --git a/tmva/tmva/inc/TMVA/MethodBase.h b/tmva/tmva/inc/TMVA/MethodBase.h index 817547c252571..b08c148086467 100644 --- a/tmva/tmva/inc/TMVA/MethodBase.h +++ b/tmva/tmva/inc/TMVA/MethodBase.h @@ -82,6 +82,7 @@ class TDirectory; class TSpline; class TH1F; class TH1D; +class TMultiGraph; namespace TMVA { @@ -92,6 +93,30 @@ namespace TMVA { class MethodBoost; class DataSetInfo; + ////////////////////////////////////////////////////////////////////////// + // // + // IPythonInteractive // + // // + // Helper class for tracking errors during the training // + // in Jupyter notebook. This class is needed by JsMVA // + // // + ////////////////////////////////////////////////////////////////////////// + class IPythonInteractive { + public: + IPythonInteractive(); + ~IPythonInteractive(); + void Init(std::vector& graphTitles); + void AddPoint(Double_t x, Double_t y1, Double_t y2); + void AddPoint(std::vector& dat); + inline TMultiGraph* Get() {return fMultiGraph;} + inline bool NotInitialized(){ return fNumGraphs==0;}; + private: + TMultiGraph* fMultiGraph; + std::vector fGraphs; + Int_t fNumGraphs; + Int_t fIndex; + }; + class MethodBase : virtual public IMethod, public Configurable { friend class Factory; @@ -413,6 +438,43 @@ namespace TMVA { // setter method for suppressing writing to XML and writing of standalone classes void DisableWriting(Bool_t setter){ fModelPersistence = setter?kFALSE:kTRUE; }//DEPRECATED + protected: + // helper variables for JsMVA + IPythonInteractive *fInteractive = nullptr; + bool fExitFromTraining = false; + UInt_t fIPyMaxIter = 0, fIPyCurrentIter = 0; + + public: + + // initializing IPythonInteractive class (for JsMVA only) + inline void InitIPythonInteractive(){ + if (fInteractive) delete fInteractive; + fInteractive = new IPythonInteractive(); + } + + // get training errors (for JsMVA only) + inline TMultiGraph* GetInteractiveTrainingError(){return fInteractive->Get();} + + // stop's the training process (for JsMVA only) + inline void ExitFromTraining(){ + fExitFromTraining = true; + } + + // check's if the training ended (for JsMVA only) + inline bool TrainingEnded(){ + if (fExitFromTraining && fInteractive){ + delete fInteractive; + fInteractive = nullptr; + } + return fExitFromTraining; + } + + // get fIPyMaxIter + inline UInt_t GetMaxIter(){ return fIPyMaxIter; } + + // get fIPyCurrentIter + inline UInt_t GetCurrentIter(){ return fIPyCurrentIter; } + protected: // ---------- protected acccessors ------------------------------------------- diff --git a/tmva/tmva/inc/TMVA/NeuralNet.h b/tmva/tmva/inc/TMVA/NeuralNet.h index 0c1cc3f10a0ff..777bcd0f9dbd3 100644 --- a/tmva/tmva/inc/TMVA/NeuralNet.h +++ b/tmva/tmva/inc/TMVA/NeuralNet.h @@ -56,6 +56,8 @@ namespace TMVA { + class IPythonInteractive; + namespace DNN { @@ -1272,6 +1274,22 @@ namespace TMVA size_t m_sizeInput; ///< input size of this DNN size_t m_sizeOutput; ///< outut size of this DNN std::vector m_layers; ///< layer-structure-data + + protected: + // variables for JsMVA (interactive training in jupyter notebook) + IPythonInteractive *fInteractive = nullptr; + bool * fExitFromTraining = nullptr; + UInt_t *fIPyMaxIter = nullptr, *fIPyCurrentIter = nullptr; + + public: + + // setup ipython interactive variables + void SetIpythonInteractive(IPythonInteractive* fI, bool* fE, UInt_t *M, UInt_t *C){ + fInteractive = fI; + fExitFromTraining = fE; + fIPyMaxIter = M; + fIPyCurrentIter = C; + } }; diff --git a/tmva/tmva/inc/TMVA/NeuralNet.icc b/tmva/tmva/inc/TMVA/NeuralNet.icc index 4f6ebe4797eff..50558e9602305 100644 --- a/tmva/tmva/inc/TMVA/NeuralNet.icc +++ b/tmva/tmva/inc/TMVA/NeuralNet.icc @@ -8,6 +8,7 @@ #include "Math/Util.h" +#include "MethodBase.h" namespace TMVA { @@ -707,6 +708,9 @@ template // std::cout << "START TRAINING" << std::endl; settings.startTrainCycle (); + // JsMVA progress bar maximum (100%) + if (fIPyMaxIter) *fIPyMaxIter = 100; + settings.pads (4); settings.create ("trainErrors", 100, 0, 100, 100, 0,1); settings.create ("testErrors", 100, 0, 100, 100, 0,1); @@ -872,6 +876,12 @@ template settings.plot ("testErrors", "C", 1, kMagenta); + // setup error plots and progress bar variables for JsMVA + if (fInteractive){ + fInteractive->AddPoint(cycleCount, trainError, testError); + if (*fExitFromTraining) break; + *fIPyCurrentIter = 100*(double)settings.maxConvergenceCount () /(double)settings.convergenceSteps (); + } if (hasConverged) break; diff --git a/tmva/tmva/inc/TMVA/SVWorkingSet.h b/tmva/tmva/inc/TMVA/SVWorkingSet.h index e360c221febb3..5376e5917b184 100644 --- a/tmva/tmva/inc/TMVA/SVWorkingSet.h +++ b/tmva/tmva/inc/TMVA/SVWorkingSet.h @@ -62,6 +62,12 @@ namespace TMVA { Bool_t TakeStepReg(SVEvent*, SVEvent*); Bool_t IsDiffSignificant(Float_t, Float_t, Float_t); void TrainReg(); + + //setting up helper variables for JsMVA + void SetIPythonInteractive(bool* ExitFromTraining, UInt_t *fIPyCurrentIter_){ + fExitFromTraining = ExitFromTraining; + fIPyCurrentIter = fIPyCurrentIter_; + } private: @@ -81,6 +87,10 @@ namespace TMVA { mutable MsgLogger* fLogger; //! message logger + // variables for JsMVA + UInt_t *fIPyCurrentIter=nullptr; + bool * fExitFromTraining; + void SetIndex( TMVA::SVEvent* ); }; } diff --git a/tmva/tmva/inc/TMVA/SimulatedAnnealing.h b/tmva/tmva/inc/TMVA/SimulatedAnnealing.h index 5a4bb5af90b57..7033d42057afc 100644 --- a/tmva/tmva/inc/TMVA/SimulatedAnnealing.h +++ b/tmva/tmva/inc/TMVA/SimulatedAnnealing.h @@ -73,6 +73,11 @@ namespace TMVA { TString kernelTemperatureS, Double_t temperatureScale, Double_t adaptiveSpeed, Double_t temperatureAdaptiveStep, Bool_t useDefaultScale, Bool_t useDefaultTemperature ); + //setting up helper variables for JsMVA + void SetIPythonInteractive(bool* ExitFromTraining, UInt_t *fIPyCurrentIter_){ + fExitFromTraining = ExitFromTraining; + fIPyCurrentIter = fIPyCurrentIter_; + } private: @@ -118,6 +123,10 @@ namespace TMVA { Double_t fProgress; + // variables for JsMVA + UInt_t *fIPyCurrentIter=nullptr; + bool * fExitFromTraining; + ClassDef(SimulatedAnnealing,0); // Base class for Simulated Annealing fitting }; diff --git a/tmva/tmva/src/GeneticFitter.cxx b/tmva/tmva/src/GeneticFitter.cxx index 7f9fc2b16de0c..04d00d0b63f49 100644 --- a/tmva/tmva/src/GeneticFitter.cxx +++ b/tmva/tmva/src/GeneticFitter.cxx @@ -113,11 +113,14 @@ Double_t TMVA::GeneticFitter::Run( std::vector& pars ) // timing of GA Timer timer( 100*(fCycles), GetName() ); + if (fIPyMaxIter) *fIPyMaxIter = 100*(fCycles); timer.DrawProgressBar( 0 ); Double_t progress = 0.; for (Int_t cycle = 0; cycle < fCycles; cycle++) { + if (fIPyCurrentIter) *fIPyCurrentIter = 100*(cycle); + if (fExitFromTraining && *fExitFromTraining) break; GetFitterTarget().ProgressNotifier( "GA", "cycle" ); // ---- perform series of fits to achieve best convergence diff --git a/tmva/tmva/src/MCFitter.cxx b/tmva/tmva/src/MCFitter.cxx index 7efdae30e53dd..b8e5c20d0a8a3 100644 --- a/tmva/tmva/src/MCFitter.cxx +++ b/tmva/tmva/src/MCFitter.cxx @@ -95,6 +95,7 @@ Double_t TMVA::MCFitter::Run( std::vector& pars ) // timing of MC Timer timer( fSamples, GetName() ); + if (fIPyMaxIter) *fIPyMaxIter = fSamples; std::vector parameters; std::vector bestParameters; @@ -122,6 +123,8 @@ Double_t TMVA::MCFitter::Run( std::vector& pars ) // loop over all MC samples for (Int_t sample = 0; sample < fSamples; sample++) { + if (fIPyCurrentIter) *fIPyCurrentIter = sample; + if (fExitFromTraining && *fExitFromTraining) break; // dice the parameters parIt = parameters.begin(); diff --git a/tmva/tmva/src/MethodBDT.cxx b/tmva/tmva/src/MethodBDT.cxx index a7dcc07592112..ead20c3d5565a 100644 --- a/tmva/tmva/src/MethodBDT.cxx +++ b/tmva/tmva/src/MethodBDT.cxx @@ -1145,6 +1145,12 @@ void TMVA::MethodBDT::Train() fNTrees = 1; } + if (fInteractive && fInteractive->NotInitialized()){ + std::vector titles = {"Boost weight", "Error Fraction"}; + fInteractive->Init(titles); + } + fIPyMaxIter = fNTrees; + // HHV (it's been here since looong but I really don't know why we cannot handle // normalized variables in BDTs... todo if (IsNormalised()) Log() << kFATAL << "\"Normalise\" option cannot be used with BDT; " @@ -1252,6 +1258,8 @@ void TMVA::MethodBDT::Train() Bool_t continueBoost=kTRUE; //for (int itree=0; itreeGetResults(GetMethodName(), Types::kTraining, GetAnalysisType()); // TH1 *hxx = new TH1F(Form("swdist%d",itree),Form("swdist%d",itree),10000,0,15); @@ -1342,7 +1350,10 @@ void TMVA::MethodBDT::Train() nNodesAfterPruning = fForest.back()->GetNNodes(); nNodesAfterPruningCount += nNodesAfterPruning; nodesAfterPruningVsTree->SetBinContent(itree+1,nNodesAfterPruning); - + + if (fInteractive){ + fInteractive->AddPoint(itree, fBoostWeight, fErrorFraction); + } fITree = itree; fMonitorNtuple->Fill(); if (fDoBoostMonitor){ @@ -1385,6 +1396,8 @@ void TMVA::MethodBDT::Train() fEventSample.clear(); fValidationSample.clear(); + if (!fExitFromTraining) fIPyMaxIter = fIPyCurrentIter; + ExitFromTraining(); } diff --git a/tmva/tmva/src/MethodBase.cxx b/tmva/tmva/src/MethodBase.cxx index 60eca2607a440..4b4d5ef671997 100644 --- a/tmva/tmva/src/MethodBase.cxx +++ b/tmva/tmva/src/MethodBase.cxx @@ -137,6 +137,75 @@ const Int_t NBIN_HIST_HIGH = 10000; #pragma warning ( disable : 4355 ) #endif + +#include "TGraph.h" +#include "TMultiGraph.h" + +//////////////////////////////////////////////////////////////////////////////// +/// standard constructur +TMVA::IPythonInteractive::IPythonInteractive() : fMultiGraph(new TMultiGraph()) +{ + fNumGraphs = 0; + fIndex = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// standard destructor +TMVA::IPythonInteractive::~IPythonInteractive() +{ + if (fMultiGraph){ + delete fMultiGraph; + fMultiGraph = nullptr; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// creating error graphs with specific names and adding them to multigraph +void TMVA::IPythonInteractive::Init(std::vector& graphTitles) +{ + if (fNumGraphs!=0){ + std::cerr << kERROR << "IPythonInteractive::Init: already initialized..." << std::endl; + return; + } + Int_t color = 2; + for(auto& title : graphTitles){ + fGraphs.push_back( new TGraph() ); + fGraphs.back()->SetTitle(title); + fGraphs.back()->SetName(title); + fGraphs.back()->SetFillColor(color); + fGraphs.back()->SetMarkerColor(color); + fMultiGraph->Add(fGraphs.back()); + color += 2; + fNumGraphs += 1; + } + return; +} +//////////////////////////////////////////////////////////////////////////////// +/// inserting points to graphs +void TMVA::IPythonInteractive::AddPoint(Double_t x, Double_t y1, Double_t y2) +{ + fGraphs[0]->Set(fIndex+1); + fGraphs[1]->Set(fIndex+1); + fGraphs[0]->SetPoint(fIndex, x, y1); + fGraphs[1]->SetPoint(fIndex, x, y2); + fIndex++; + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// inserting points to graphs +void TMVA::IPythonInteractive::AddPoint(std::vector& dat) +{ + for(Int_t i=0; iSet(fIndex+1); + fGraphs[i]->SetPoint(fIndex, dat[0], dat[i+1]); + } + fIndex++; + return; +} + + //////////////////////////////////////////////////////////////////////////////// /// standard constructur diff --git a/tmva/tmva/src/MethodCFMlpANN.cxx b/tmva/tmva/src/MethodCFMlpANN.cxx index 73f3e8fce95a7..1f213f4445250 100644 --- a/tmva/tmva/src/MethodCFMlpANN.cxx +++ b/tmva/tmva/src/MethodCFMlpANN.cxx @@ -286,6 +286,7 @@ void TMVA::MethodCFMlpANN::Train( void ) Int_t *nodes = new Int_t[nlayers]; Int_t ncycles(fNcycles); + for (Int_t i=0; iSetIPythonInteractive(&fExitFromTraining, &fIPyMaxIter, &fIPyCurrentIter); + fitter->CheckForUnusedOptions(); // perform the fit @@ -711,11 +713,14 @@ void TMVA::MethodCuts::Train( void ) // timing of MC Int_t nsamples = Int_t(0.5*nevents*(nevents - 1)); Timer timer( nsamples, GetName() ); + fIPyMaxIter = nsamples; Log() << kINFO << "Running full event scan: " << Endl; for (Int_t ievt1=0; ievt1 pars( 2*GetNvar() ); for (Int_t itoy=0; itoyNotInitialized()){ + std::vector titles = {"Error on training set", "Error on test set"}; + fInteractive->Init(titles); + } + std::vector trainPattern; std::vector testPattern; @@ -677,6 +682,8 @@ void TMVA::MethodDNN::Train() } } } + if (!fExitFromTraining) fIPyMaxIter = fIPyCurrentIter; + ExitFromTraining(); } //______________________________________________________________________________ diff --git a/tmva/tmva/src/MethodDT.cxx b/tmva/tmva/src/MethodDT.cxx index 8ecf0f6398f82..c8e5efaa8a776 100644 --- a/tmva/tmva/src/MethodDT.cxx +++ b/tmva/tmva/src/MethodDT.cxx @@ -389,6 +389,7 @@ void TMVA::MethodDT::Train( void ) if (fPruneMethod != DecisionTree::kNoPruning) fTree->PruneTree(); TMVA::DecisionTreeNode::fgIsTraining=false; + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodFDA.cxx b/tmva/tmva/src/MethodFDA.cxx index e48d69fea259f..99287835b38b7 100644 --- a/tmva/tmva/src/MethodFDA.cxx +++ b/tmva/tmva/src/MethodFDA.cxx @@ -403,6 +403,7 @@ void TMVA::MethodFDA::Train( void ) delete fConvergerFitter; fConvergerFitter = 0; } + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodFisher.cxx b/tmva/tmva/src/MethodFisher.cxx index 7d3ec7f52d10b..906bdfc1c177c 100644 --- a/tmva/tmva/src/MethodFisher.cxx +++ b/tmva/tmva/src/MethodFisher.cxx @@ -257,6 +257,8 @@ void TMVA::MethodFisher::Train( void ) // nice output PrintCoefficients(); + + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodHMatrix.cxx b/tmva/tmva/src/MethodHMatrix.cxx index 09b853a260adb..4ac0554c11d55 100644 --- a/tmva/tmva/src/MethodHMatrix.cxx +++ b/tmva/tmva/src/MethodHMatrix.cxx @@ -189,6 +189,7 @@ void TMVA::MethodHMatrix::Train( void ) // invert matrix fInvHMatrixS->Invert(); fInvHMatrixB->Invert(); + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodKNN.cxx b/tmva/tmva/src/MethodKNN.cxx index 94f7c653c8727..970a4c2bb8e15 100644 --- a/tmva/tmva/src/MethodKNN.cxx +++ b/tmva/tmva/src/MethodKNN.cxx @@ -288,6 +288,8 @@ void TMVA::MethodKNN::Train() // create kd-tree (binary tree) structure MakeKNN(); + + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodLD.cxx b/tmva/tmva/src/MethodLD.cxx index 22f656a65ab32..2aa2c79b5d59a 100644 --- a/tmva/tmva/src/MethodLD.cxx +++ b/tmva/tmva/src/MethodLD.cxx @@ -144,6 +144,8 @@ void TMVA::MethodLD::Train( void ) // nice output PrintCoefficients(); + + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodLikelihood.cxx b/tmva/tmva/src/MethodLikelihood.cxx index e541c07da1e06..8546dc4287585 100644 --- a/tmva/tmva/src/MethodLikelihood.cxx +++ b/tmva/tmva/src/MethodLikelihood.cxx @@ -443,6 +443,7 @@ void TMVA::MethodLikelihood::Train( void ) if ((*fPDFSig)[ivar]->GetSmoothedHist() != 0) (*fHistSig_smooth)[ivar] = (*fPDFSig)[ivar]->GetSmoothedHist(); if ((*fPDFBgd)[ivar]->GetSmoothedHist() != 0) (*fHistBgd_smooth)[ivar] = (*fPDFBgd)[ivar]->GetSmoothedHist(); } + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodMLP.cxx b/tmva/tmva/src/MethodMLP.cxx index b5676839fd603..b30f05016ea5b 100644 --- a/tmva/tmva/src/MethodMLP.cxx +++ b/tmva/tmva/src/MethodMLP.cxx @@ -444,6 +444,12 @@ void TMVA::MethodMLP::Train(Int_t nEpochs) if (nSynapses>nEvents) Log()<Init(titles); + } + #ifdef MethodMLP_UseMinuit__ if (useMinuit) MinuitMinimize(); #else @@ -466,6 +472,7 @@ void TMVA::MethodMLP::Train(Int_t nEpochs) fInvHessian.ResizeTo(numSynapses,numSynapses); GetApproxInvHessian( fInvHessian ,false); } + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// @@ -517,6 +524,9 @@ void TMVA::MethodMLP::BFGSMinimize( Int_t nEpochs ) // start training cycles (epochs) for (Int_t i = 0; i < nEpochs; i++) { + + if (fExitFromTraining) break; + fIPyCurrentIter = i; if (Float_t(i)/nEpochs < fSamplingEpoch) { if ((i+1)%fTestRate == 0 || (i == 0)) { if (fSamplingTraining) { @@ -598,6 +608,7 @@ void TMVA::MethodMLP::BFGSMinimize( Int_t nEpochs ) //testE = CalculateEstimator( Types::kTesting, i ) - fPrior/Float_t(GetNEvents()); // estimator for test sample //zjh trainE = CalculateEstimator( Types::kTraining, i ) ; // estimator for training sample //zjh testE = CalculateEstimator( Types::kTesting, i ) ; // estimator for test sample //zjh + if (fInteractive) fInteractive->AddPoint(i+1, trainE, testE); if(!IsSilentFile()) //saved to see in TMVAGui, no needed without file { fEstimatorHistTrain->Fill( i+1, trainE ); @@ -1045,6 +1056,8 @@ void TMVA::MethodMLP::BackPropagationMinimize(Int_t nEpochs) // start training cycles (epochs) for (Int_t i = 0; i < nEpochs; i++) { + if (fExitFromTraining) break; + fIPyCurrentIter = i; if (Float_t(i)/nEpochs < fSamplingEpoch) { if ((i+1)%fTestRate == 0 || (i == 0)) { if (fSamplingTraining) { @@ -1074,6 +1087,7 @@ void TMVA::MethodMLP::BackPropagationMinimize(Int_t nEpochs) if ((i+1)%fTestRate == 0) { trainE = CalculateEstimator( Types::kTraining, i ); // estimator for training sample testE = CalculateEstimator( Types::kTesting, i ); // estimator for test samplea + if (fInteractive) fInteractive->AddPoint(i+1, trainE, testE); if(!IsSilentFile()) { fEstimatorHistTrain->Fill( i+1, trainE ); diff --git a/tmva/tmva/src/MethodPDEFoam.cxx b/tmva/tmva/src/MethodPDEFoam.cxx index 3c4603d912fa2..f3400a96fa2eb 100644 --- a/tmva/tmva/src/MethodPDEFoam.cxx +++ b/tmva/tmva/src/MethodPDEFoam.cxx @@ -470,6 +470,7 @@ void TMVA::MethodPDEFoam::Train( void ) if(fFoam.at(i)) fFoam.at(i)->DeleteBinarySearchTree(); } + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodPDERS.cxx b/tmva/tmva/src/MethodPDERS.cxx index 9235472259cb7..cfa76f0e48c21 100644 --- a/tmva/tmva/src/MethodPDERS.cxx +++ b/tmva/tmva/src/MethodPDERS.cxx @@ -360,6 +360,7 @@ void TMVA::MethodPDERS::Train( void ) SetVolumeElement(); fInitializedVolumeEle = kTRUE; + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodRuleFit.cxx b/tmva/tmva/src/MethodRuleFit.cxx index e716cc1e911e8..26476ae7e976d 100644 --- a/tmva/tmva/src/MethodRuleFit.cxx +++ b/tmva/tmva/src/MethodRuleFit.cxx @@ -453,6 +453,7 @@ void TMVA::MethodRuleFit::Train( void ) } fRuleFit.GetRuleEnsemblePtr()->ClearRuleMap(); TMVA::DecisionTreeNode::fgIsTraining=false; + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/MethodSVM.cxx b/tmva/tmva/src/MethodSVM.cxx index 9a7c45d1de9c8..a841931cb2cf4 100644 --- a/tmva/tmva/src/MethodSVM.cxx +++ b/tmva/tmva/src/MethodSVM.cxx @@ -287,6 +287,7 @@ void TMVA::MethodSVM::ProcessOptions() void TMVA::MethodSVM::Train() { + fIPyMaxIter = fMaxIter; Data()->SetCurrentType(Types::kTraining); Log() << kDEBUG << "Create event vector"<< Endl; @@ -382,6 +383,8 @@ void TMVA::MethodSVM::Train() Timer timer( GetName() ); Log() << kINFO << "Sorry, no computing time forecast available for SVM, please wait ..." << Endl; + fWgSet->SetIPythonInteractive(&fExitFromTraining, &fIPyCurrentIter); + fWgSet->Train(fMaxIter); Log() << kINFO << "Elapsed time: " << timer.GetElapsedTime() @@ -391,6 +394,9 @@ void TMVA::MethodSVM::Train() fSupportVectors = fWgSet->GetSupportVectors(); delete fWgSet; fWgSet=0; + + if (!fExitFromTraining) fIPyMaxIter = fIPyCurrentIter; + ExitFromTraining(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/tmva/tmva/src/SVWorkingSet.cxx b/tmva/tmva/src/SVWorkingSet.cxx index f8b891bf65710..a62b337381c19 100644 --- a/tmva/tmva/src/SVWorkingSet.cxx +++ b/tmva/tmva/src/SVWorkingSet.cxx @@ -390,6 +390,8 @@ void TMVA::SVWorkingSet::Train(UInt_t nMaxIter) std::vector::iterator idIter; while ((numChanged > 0) || (examineAll > 0)) { + if (fIPyCurrentIter) *fIPyCurrentIter = numit; + if (*fExitFromTraining) break; numChanged = 0; if (examineAll) { for (idIter = fInputData->begin(); idIter!=fInputData->end(); idIter++){ diff --git a/tmva/tmva/src/SimulatedAnnealing.cxx b/tmva/tmva/src/SimulatedAnnealing.cxx index 4c8b8ea1d568f..5910f8ead6677 100644 --- a/tmva/tmva/src/SimulatedAnnealing.cxx +++ b/tmva/tmva/src/SimulatedAnnealing.cxx @@ -349,6 +349,8 @@ Double_t TMVA::SimulatedAnnealing::Minimize( std::vector& parameters ) Timer timer( fMaxCalls, fLogger->GetSource().c_str() ); for (Int_t sample = 0; sample < generalCalls; sample++) { + if (fIPyCurrentIter) *fIPyCurrentIter = sample; + if (fExitFromTraining && *fExitFromTraining) break; GenerateNeighbour( parameters, oldParameters, currentTemperature ); Double_t localFit = fFitterTarget.EstimatorFunction( parameters ); diff --git a/tmva/tmva/src/SimulatedAnnealingFitter.cxx b/tmva/tmva/src/SimulatedAnnealingFitter.cxx index faaaab0256ece..5c7ec03f55bce 100644 --- a/tmva/tmva/src/SimulatedAnnealingFitter.cxx +++ b/tmva/tmva/src/SimulatedAnnealingFitter.cxx @@ -142,6 +142,9 @@ Double_t TMVA::SimulatedAnnealingFitter::Run( std::vector& pars ) sa.SetOptions( fMaxCalls, fInitialTemperature, fMinTemperature, fEps, fKernelTemperatureS, fTemperatureScale, fAdaptiveSpeed, fTemperatureAdaptiveStep, fUseDefaultScale, fUseDefaultTemperature ); + + if (fIPyMaxIter) *fIPyMaxIter = fMaxCalls; + sa.SetIPythonInteractive(fExitFromTraining, fIPyCurrentIter); // minimise Double_t fcn = sa.Minimize( pars ); From bd73d1a12ae0c3b936920f419ce1978e47bab6b6 Mon Sep 17 00:00:00 2001 From: Attila Bagoly Date: Wed, 20 Jul 2016 00:31:43 +0200 Subject: [PATCH 03/14] Added interactive module for Jupyter notebook environment --- bindings/pyroot/CMakeLists.txt | 4 + bindings/pyroot/JsMVA/js/DecisionTree.js | 526 +++++++++++++++ bindings/pyroot/JsMVA/js/DecisionTree.min.js | 1 + bindings/pyroot/JsMVA/js/JsMVA.js | 71 +++ bindings/pyroot/JsMVA/js/JsMVA.min.js | 1 + bindings/pyroot/JsMVA/js/NeuralNetwork.js | 449 +++++++++++++ bindings/pyroot/JsMVA/js/NeuralNetwork.min.js | 1 + .../pyroot/JsMVA/python/JsMVA/DataLoader.py | 92 +++ bindings/pyroot/JsMVA/python/JsMVA/Factory.py | 599 ++++++++++++++++++ .../pyroot/JsMVA/python/JsMVA/JPyInterface.py | 168 +++++ .../pyroot/JsMVA/python/JsMVA/JsMVAMagic.py | 31 + .../pyroot/JsMVA/python/JsMVA/__init__.py | 9 + bindings/pyroot/ROOT.py | 4 + 13 files changed, 1956 insertions(+) create mode 100644 bindings/pyroot/JsMVA/js/DecisionTree.js create mode 100644 bindings/pyroot/JsMVA/js/DecisionTree.min.js create mode 100644 bindings/pyroot/JsMVA/js/JsMVA.js create mode 100644 bindings/pyroot/JsMVA/js/JsMVA.min.js create mode 100644 bindings/pyroot/JsMVA/js/NeuralNetwork.js create mode 100644 bindings/pyroot/JsMVA/js/NeuralNetwork.min.js create mode 100644 bindings/pyroot/JsMVA/python/JsMVA/DataLoader.py create mode 100644 bindings/pyroot/JsMVA/python/JsMVA/Factory.py create mode 100644 bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py create mode 100644 bindings/pyroot/JsMVA/python/JsMVA/JsMVAMagic.py create mode 100644 bindings/pyroot/JsMVA/python/JsMVA/__init__.py diff --git a/bindings/pyroot/CMakeLists.txt b/bindings/pyroot/CMakeLists.txt index 85df6f97cf0b2..ba30ce20a8c44 100644 --- a/bindings/pyroot/CMakeLists.txt +++ b/bindings/pyroot/CMakeLists.txt @@ -45,5 +45,9 @@ set( JupyROOTDirName "JupyROOT") install (DIRECTORY ${JupyROOTDirName} DESTINATION ${runtimedir}) file(COPY ${JupyROOTDirName} DESTINATION ${CMAKE_BINARY_DIR}/${runtimedir}) +set( JsMVADirName "JsMVA") +install (DIRECTORY ${JsMVADirName} DESTINATION ${runtimedir}) +file(COPY ${JsMVADirName} DESTINATION ${CMAKE_BINARY_DIR}/${runtimedir}) + #---Install headers---------------------------------------------------------- ROOT_INSTALL_HEADERS() diff --git a/bindings/pyroot/JsMVA/js/DecisionTree.js b/bindings/pyroot/JsMVA/js/DecisionTree.js new file mode 100644 index 0000000000000..5f4e5c2b11f7c --- /dev/null +++ b/bindings/pyroot/JsMVA/js/DecisionTree.js @@ -0,0 +1,526 @@ +/** + * Created by Attila Bagoly on 6/11/16. + */ + + +(function(factory){ + + require.config({ + paths:{ + d3: "https://root.cern.ch/js/notebook/scripts/d3.v3.min" + } + }); + + var url = ""; + if (require.s.contexts.hasOwnProperty("_")) { + url = require.s.contexts._.config.paths["JsMVA"].replace("src/js/JsMVA.min",""); + } + + define(["d3"], function(d3){ + return factory({}, d3, url); + }); + +})(function(DecisionTree, d3, url){ + + Object.size = function(obj){ + return Object.keys(obj).length; + }; + + var style = { + margin: { + x: 20, + y: 20 + }, + node: { + padding: 10, + yspace: 40, + xspace: 20, + width: 150, + height: 40, + mwidth: 150, + mheight: 60, + colors: { + focus: "#033A00", + closed: "#00A62B", + pureBkg: "red", + pureSig: "blue" + }, + swidth: "4px" + }, + link: { + colors:{ + default: "#ccc", + focus: "#033A00" + }, + width: "4px", + focus_width: "8px" + }, + aduration: 1500, + legend: { + size: 20, + rect_width: 100, + rect_height: 30, + rect_fucus_width: 115 + }, + text: { + color: "#DEDEDE", + padding: "4px" + }, + buttons:{ + reset:{ + width: "36px", + height: "36px", + alpha: "0.5", + img: url+"img/reset.png", + background: "white" + } + } + }; + + + var nodeColor = d3.scale.linear() + .range([style.node.colors.pureBkg, style.node.colors.pureSig]); + + var canvas, svg, roottree, variables; + + var d3tree = d3.layout.tree(); + var d3path = (function(){ + var diagonal = d3.svg.diagonal(); + var forEachData = function(d, i, hidden){ + if (hidden) return diagonal(d); + return diagonal({ + source: { + x: (d.source.x + style.node.width), + y: d.source.y + }, + target: { + x: (d.target.x + style.node.width), + y: d.target.y - style.node.height + } + }); + }; + return forEachData; + })(); + + var clickOnNode = function(d){ + if ("children" in d){ + d._children = d.children; + d.children = null; + } else { + d.children = d._children; + d._children = null; + } + drawTree(d); + }; + + var drawLabels = function(nodeContainer) { + nodeContainer.append("text") + .attr("dy", (style.node.height * 0.35) + "px") + .attr("class", "label1") + .attr("dx", style.text.padding) + .style("fill-opacity", 1e-6) + .style("font-size", 1e-6+"px") + .style("cursor", "pointer") + .style("fill", style.text.color) + .style("font-weight", "bold") + .text(function (d) { + return "S/(S+B)=" + Number(d.info.purity).toFixed(3); + }); + nodeContainer.append("text") + .attr("class", "label2") + .attr("dx", style.text.padding) + .attr("dy", (style.node.height*0.75)+"px") + .style("fill-opacity", 1e-6) + .style("cursor", "pointer") + .text(function(d){ + return d.info.IVar!=-1 + ? variables[d.info.IVar]+">"+(Number(d.info.Cut).toFixed(3)) + : ""; + }) + .style("font-size", 1e-6+"px") + .style("fill", style.text.color) + .style("font-weight", "bold"); + }; + + var drawNodes = function(nodeSelector, father){ + var nodeContainer = nodeSelector.enter() + .append("g").attr("class", "nodes") + .attr("transform", function(d){return "translate("+father.x0+","+father.y0+")";}) + .style("cursor","pointer"); + + nodeContainer.filter(function(d){ + return d.parent; + }) + .on("click", clickOnNode) + .on("mouseover", path) + .on("contextmenu", function(d, i){ + d3.event.preventDefault(); + makePathNodesBigger(d); + }) + .on("mouseleave", function(d, i){ + if (d.bigger) makePathNodesBigger(d, i, 1); + return path(d, i, 1); + }); + + nodeContainer.append("rect") + .attr("width", 1e-6) + .attr("height", 1e-6); + + drawLabels(nodeContainer); + + nodeSelector.transition().duration(style.aduration) + .attr("transform", function(d){ + return "translate(" + + (d.x+style.node.width*0.5) + "," + + (d.y-style.node.height) + ")"; + }); + + nodeSelector.select("rect").transition().duration(style.aduration) + .attr("width", style.node.width) + .attr("height", style.node.height) + .attr("fill", function(d){return nodeColor(Number(d.info.purity));}) + .style("stroke-width", style.node.swidth) + .style("stroke", function(d){ + return (d._children) ? style.node.colors.closed : ""; + }); + + nodeSelector.selectAll("text") + .transition().duration(style.aduration) + .style("font-size", function(d) { + var l1 = "S/(S+B)=" + Number(d.info.purity).toFixed(3); + var l2 = d.info.IVar!=-1 + ? variables[d.info.IVar]+">"+(Number(d.info.Cut).toFixed(3)) + : ""; + d.font_size = 1.5*(style.node.width-2*Number(style.node.swidth.replace("px","")))/Math.max(l1.length, l2.length); + return d.font_size+"px"; + }) + .attr("dx", style.text.padding) + .attr("dy", function(d){ + return ((d3.select(this).attr("class")=="label1")? (style.node.height * 0.35) : (style.node.height * 0.75))+"px"; }) + .style("fill-opacity", 1); + + var nodeExit = nodeSelector.exit() + .transition().duration(style.aduration) + .attr("transform", function(d){ + return "translate(" + + (father.x+style.node.width) + "," + + father.y + ")"; + }) + .remove(); + + nodeExit.select("rect") + .attr("width", 1e-6) + .attr("height", 1e-6); + + nodeExit.selectAll("text") + .style("font-size", 1e-6+"px") + .style("fill-opacity", 1e-6); + }; + + var drawLinks = function(linkSelector, father){ + linkSelector.enter() + .insert("path", "g") + .attr("class", "link") + .attr("d", function(d, i){ + var o = {x:father.x0, y:father.y0}; + return d3path({source: o, target: o},i, 1); + }); + + linkSelector.transition().duration(style.aduration) + .attr("d", d3path) + .style("fill", "none") + .style("stroke", style.link.colors.default) + .style("stroke-width", style.link.width) + .attr("id", function(d){return "link"+d.target.id;}); + + linkSelector.exit() + .transition().duration(style.aduration) + .attr("d", function(d, i){ + var o = {x:father.x+style.node.width, y:father.y}; + return d3path({source:o, target:o},i, 1); + }) + .remove(); + }; + + var path = function(node, i, clear){ + svg.selectAll("path.link").filter(function(d){return d.target.id==node.id;}) + .style("stroke-width", (clear) ? style.link.width : style.link.focus_width) + .style("stroke", (clear) ? style.link.colors.default : style.link.colors.focus); + + svg.selectAll("g.nodes rect").filter(function(d){return d.id==node.id;}) + .style("stroke-width", style.node.swidth) + .style("stroke", function(d){ + return (clear) + ? (d._children) ? style.node.colors.closed : nodeColor(d.info.purity) + : style.node.colors.focus; + }); + + if (node.parent) path(node.parent, i, clear); + }; + + var makePathNodesBigger = function(node, i, clear){ + var width = (clear) ? style.node.width : 2*style.node.width, + height = (clear) ? style.node.height : 1.5*style.node.height; + svg.selectAll("g.nodes rect").filter(function(d){d.bigger=(clear) ? false : true; return d.id==node.id;}) + .transition().duration(style.aduration/2) + .attr("width", width+"px") + .attr("height", height+"px"); + + svg.selectAll("g.nodes text").filter(function(d){return d.id==node.id;}) + .transition().duration(style.aduration/2) + .style("font-size", function(d){ + return ((clear) ? d.font_size : 2*d.font_size)+"px"; + }) + .attr("dx", (clear) ? style.text.padding : (2*Number(style.text.padding.replace("px", ""))+"px")) + .attr("dy", function(){ + return ((d3.select(this).attr("class")=="label1")? (height * 0.35) : (height * 0.75))+"px"; + }); + if (node.parent) makePathNodesBigger(node.parent, i, clear); + }; + + var drawTree = function(father, nox0y0Calc){ + updateSizesColors(); + var nodes = d3tree.nodes(roottree), + links = d3tree.links(nodes); + + var maxDepth = 0; + nodes.forEach(function(d){ + if (maxDepthmax) max = pur; + } + return [min, max]; + }; + + var updateSizesColors = function(first){ + var nodes = d3tree.nodes(roottree); + var tree; + for(var i in nodes){ + if (!nodes[i].parent){ + tree = nodes[i]; + break; + } + } + var height = treeHeight(tree); + + style.node.height = canvas.height/(height+1)-style.node.yspace; + if (style.node.height>style.node.mheight) style.node.height = style.node.mheight; + var corr = 0; + while(height!=0){ + if (!(height%4)) corr++; + height /= 4; + } + style.node.width = canvas.width/(treeWidth(nodes)+1-corr)-style.node.xspace; + if (style.node.width>style.node.mwidth) style.node.height = style.node.mwidth; + + d3tree.size([canvas.width, canvas.height]); + + nodeColor.domain(purityToColor(nodes)); + }; + + var drawLegend = function(svgOriginal){ + var labels = [ + {text: "Pure Backg.", id: "label1", color: nodeColor(nodeColor.domain()[0]), x:5,y:5}, + {text: "Pure Signal", id: "label2", color: nodeColor(nodeColor.domain()[1]), x:5,y:40} + ]; + var legend = svgOriginal.append("g") + .attr("transform", "translate(5,5)"); + + var group = legend.selectAll("g") + .data(labels, function(d){return d.id;}) + .enter() + .append("g") + .style("cursor", "pointer") + .attr("transform", function(d){return "translate("+d.x+","+d.y+")";}); + + group.on("mouseover", function(d){ + d3.select("#"+d.id).style("font-weight", "bold"); + d3.select("#"+d.id+"_rect").attr("width", style.legend.rect_fucus_width); + }); + group.on("mouseout", function(d){ + d3.select("#"+d.id).style("font-weight", "normal"); + d3.select("#"+d.id+"_rect").attr("width", style.legend.rect_width); + + }); + + group.append("rect") + .attr("id", function(d){return d.id+"_rect";}) + .attr("width", style.legend.rect_width) + .attr("height", style.legend.rect_height) + .attr("fill", function(d){return d.color;}); + + group.append("text") + .attr("id", function(d){return d.id;}) + .attr("x", function(d){return 5;}) + .attr("y", function(d){return 20;}) + .text(function(d){return d.text;}) + .style("fill", style.text.color); + }; + + var openSubTree = function(node){ + if ("_children" in node && node.children==null){ + node.children = node._children; + node._children = null; + } + if ("children" in node && node.children!=null){ + for(var i in node.children){ + openSubTree(node.children[i]); + } + } + }; + + var findFathers = function(start){ + var fathers = []; + var Q = []; + Q.push(start); + while(Q.length>0){ + var node = Q.shift(); + if ("_children" in node && node._children!=null){ + fathers.push(node); + } + if (node.children!=null) { + for (var i = 0; i < node.children.length; i++) { + Q.push(node.children[i]); + } + } + } + return fathers.length>0 ? fathers : start; + }; + + + DecisionTree.draw = function(divid, pyobj){ + var div = d3.select("#"+divid); + + roottree = pyobj["tree"]; + variables = pyobj["variables"]; + + if (Object.size(roottree)==0){ + div.innerHTML = "Tree empty..."; + return; + } + + canvas = { + width: div.property("style")["width"], + height: div.property("style")["height"] + }; + + svg = div.append("svg") + .attr("width", canvas.width) + .attr("height", canvas.height); + var svgOriginal = svg; + Object.keys(canvas).forEach(function (key) { + canvas[key] = Number(canvas[key].replace("px","")); + canvas[key] -= key=="width" ? 2*style.margin.x+style.node.width : 2*style.margin.y+style.node.height; + }); + + updateSizesColors(1); + + var zoom = d3.behavior.zoom() + .scaleExtent([1, 10]) + .on("zoom", function(){ + svg.attr("transform", + "translate("+(-style.node.width)+", "+style.node.height + +")translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); + }); + svg = svg + .on("dblclick", function(){ + zoom.scale(1); + zoom.translate([0, 0]); + svg.transition().attr("transform", "translate("+(-style.node.width)+", "+style.node.height+")"); + }) + .append("g").call(zoom).append("g") + .attr("transform", "translate("+(-style.node.width)+", "+style.node.height+")"); + + drawLegend(svgOriginal); + + drawTree(roottree); + + div.append("button") + .style("position", "relative") + .style("top", "-"+style.buttons.reset.height) + .style("width", style.buttons.reset.width) + .style("height", style.buttons.reset.height) + .style("opacity", style.buttons.reset.alpha) + .style("background", style.buttons.reset.background) + .style("background-size", "contain") + .style("background-image", "url("+style.buttons.reset.img+")") + .style("cursor", "pointer") + .style("border", "none") + .on("mouseover", function(){ + d3.select(this).style("opacity", "1"); + }) + .on("mouseout", function(){ + d3.select(this).style("opacity", style.buttons.reset.alpha); + }) + .on("click", function(){ + zoom.scale(1); + zoom.translate([0, 0]); + svg.transition().attr("transform", "translate("+(-style.node.width)+", "+style.node.height+")"); + var fathers = findFathers(roottree); + for(var i=0;i"+Number(t.info.Cut).toFixed(3):""}).style("font-size",1e-6+"px").style("fill",r.text.color).style("font-weight","bold")};var f=function(t,n){var o=t.enter().append("g").attr("class","nodes").attr("transform",function(t){return"translate("+n.x0+","+n.y0+")"}).style("cursor","pointer");o.filter(function(t){return t.parent}).on("click",u).on("mouseover",p).on("contextmenu",function(t,n){e.event.preventDefault();y(t)}).on("mouseleave",function(t,e){if(t.bigger)y(t,e,1);return p(t,e,1)});o.append("rect").attr("width",1e-6).attr("height",1e-6);h(o);t.transition().duration(r.aduration).attr("transform",function(t){return"translate("+(t.x+r.node.width*.5)+","+(t.y-r.node.height)+")"});t.select("rect").transition().duration(r.aduration).attr("width",r.node.width).attr("height",r.node.height).attr("fill",function(t){return i(Number(t.info.purity))}).style("stroke-width",r.node.swidth).style("stroke",function(t){return t._children?r.node.colors.closed:""});t.selectAll("text").transition().duration(r.aduration).style("font-size",function(t){var e="S/(S+B)="+Number(t.info.purity).toFixed(3);var n=t.info.IVar!=-1?l[t.info.IVar]+">"+Number(t.info.Cut).toFixed(3):"";t.font_size=1.5*(r.node.width-2*Number(r.node.swidth.replace("px","")))/Math.max(e.length,n.length);return t.font_size+"px"}).attr("dx",r.text.padding).attr("dy",function(t){return(e.select(this).attr("class")=="label1"?r.node.height*.35:r.node.height*.75)+"px"}).style("fill-opacity",1);var a=t.exit().transition().duration(r.aduration).attr("transform",function(t){return"translate("+(n.x+r.node.width)+","+n.y+")"}).remove();a.select("rect").attr("width",1e-6).attr("height",1e-6);a.selectAll("text").style("font-size",1e-6+"px").style("fill-opacity",1e-6)};var g=function(t,e){t.enter().insert("path","g").attr("class","link").attr("d",function(t,n){var r={x:e.x0,y:e.y0};return c({source:r,target:r},n,1)});t.transition().duration(r.aduration).attr("d",c).style("fill","none").style("stroke",r.link.colors.default).style("stroke-width",r.link.width).attr("id",function(t){return"link"+t.target.id});t.exit().transition().duration(r.aduration).attr("d",function(t,n){var i={x:e.x+r.node.width,y:e.y};return c({source:i,target:i},n,1)}).remove()};var p=function(t,e,n){a.selectAll("path.link").filter(function(e){return e.target.id==t.id}).style("stroke-width",n?r.link.width:r.link.focus_width).style("stroke",n?r.link.colors.default:r.link.colors.focus);a.selectAll("g.nodes rect").filter(function(e){return e.id==t.id}).style("stroke-width",r.node.swidth).style("stroke",function(t){return n?t._children?r.node.colors.closed:i(t.info.purity):r.node.colors.focus});if(t.parent)p(t.parent,e,n)};var y=function(t,n,i){var o=i?r.node.width:2*r.node.width,d=i?r.node.height:1.5*r.node.height;a.selectAll("g.nodes rect").filter(function(e){e.bigger=i?false:true;return e.id==t.id}).transition().duration(r.aduration/2).attr("width",o+"px").attr("height",d+"px");a.selectAll("g.nodes text").filter(function(e){return e.id==t.id}).transition().duration(r.aduration/2).style("font-size",function(t){return(i?t.font_size:2*t.font_size)+"px"}).attr("dx",i?r.text.padding:2*Number(r.text.padding.replace("px",""))+"px").attr("dy",function(){return(e.select(this).attr("class")=="label1"?d*.35:d*.75)+"px"});if(t.parent)y(t.parent,n,i)};var x=function(t,e){b();var n=s.nodes(d),i=s.links(n);var l=0;n.forEach(function(t){if(ln)n=r}return[e,n]};var b=function(t){var e=s.nodes(d);var n;for(var a in e){if(!e[a].parent){n=e[a];break}}var l=v(n);r.node.height=o.height/(l+1)-r.node.yspace;if(r.node.height>r.node.mheight)r.node.height=r.node.mheight;var c=0;while(l!=0){if(!(l%4))c++;l/=4}r.node.width=o.width/(w(e)+1-c)-r.node.xspace;if(r.node.width>r.node.mwidth)r.node.height=r.node.mwidth;s.size([o.width,o.height]);i.domain(m(e))};var k=function(t){var n=[{text:"Pure Backg.",id:"label1",color:i(i.domain()[0]),x:5,y:5},{text:"Pure Signal",id:"label2",color:i(i.domain()[1]),x:5,y:40}];var o=t.append("g").attr("transform","translate(5,5)");var a=o.selectAll("g").data(n,function(t){return t.id}).enter().append("g").style("cursor","pointer").attr("transform",function(t){return"translate("+t.x+","+t.y+")"});a.on("mouseover",function(t){e.select("#"+t.id).style("font-weight","bold");e.select("#"+t.id+"_rect").attr("width",r.legend.rect_fucus_width)});a.on("mouseout",function(t){e.select("#"+t.id).style("font-weight","normal");e.select("#"+t.id+"_rect").attr("width",r.legend.rect_width)});a.append("rect").attr("id",function(t){return t.id+"_rect"}).attr("width",r.legend.rect_width).attr("height",r.legend.rect_height).attr("fill",function(t){return t.color});a.append("text").attr("id",function(t){return t.id}).attr("x",function(t){return 5}).attr("y",function(t){return 20}).text(function(t){return t.text}).style("fill",r.text.color)};var _=function(t){if("_children"in t&&t.children==null){t.children=t._children;t._children=null}if("children"in t&&t.children!=null){for(var e in t.children){_(t.children[e])}}};var z=function(t){var e=[];var n=[];n.push(t);while(n.length>0){var r=n.shift();if("_children"in r&&r._children!=null){e.push(r)}if(r.children!=null){for(var i=0;i0?e:t};t.draw=function(t,n){var i=e.select("#"+t);d=n["tree"];l=n["variables"];if(Object.size(d)==0){i.innerHTML="Tree empty...";return}o={width:i.property("style")["width"],height:i.property("style")["height"]};a=i.append("svg").attr("width",o.width).attr("height",o.height);var s=a;Object.keys(o).forEach(function(t){o[t]=Number(o[t].replace("px",""));o[t]-=t=="width"?2*r.margin.x+r.node.width:2*r.margin.y+r.node.height});b(1);var c=e.behavior.zoom().scaleExtent([1,10]).on("zoom",function(){a.attr("transform","translate("+-r.node.width+", "+r.node.height+")translate("+e.event.translate+")scale("+e.event.scale+")")});a=a.on("dblclick",function(){c.scale(1);c.translate([0,0]);a.transition().attr("transform","translate("+-r.node.width+", "+r.node.height+")")}).append("g").call(c).append("g").attr("transform","translate("+-r.node.width+", "+r.node.height+")");k(s);x(d);i.append("button").style("position","relative").style("top","-"+r.buttons.reset.height).style("width",r.buttons.reset.width).style("height",r.buttons.reset.height).style("opacity",r.buttons.reset.alpha).style("background",r.buttons.reset.background).style("background-size","contain").style("background-image","url("+r.buttons.reset.img+")").style("cursor","pointer").style("border","none").on("mouseover",function(){e.select(this).style("opacity","1")}).on("mouseout",function(){e.select(this).style("opacity",r.buttons.reset.alpha)}).on("click",function(){c.scale(1);c.translate([0,0]);a.transition().attr("transform","translate("+-r.node.width+", "+r.node.height+")");var t=z(d);for(var e=0;e on 5/14/16. + */ + +(function(factory){ + + var JSROOT_source_dir = "https://root.cern.ch/js/notebook/scripts/"; + + var url = ""; + if (requirejs.s.contexts.hasOwnProperty("_")) { + url = requirejs.s.contexts._.config.paths["JsMVA"].replace("JsMVA.min",""); + } + if ((console!==undefined) && (typeof console.log == 'function')) { + if (url!=""){ + console.log("JsMVA source dir:" + url.substring(0, url.length-1)); + } else { + console.log("JsMVA source dir can't be resolved, requireJS doesn't have context '_', this will be a problem!"); + } + } + + require.config({ + paths: { + 'JsRootCore': JSROOT_source_dir+'JSRootCore', + 'nn': url+'NeuralNetwork.min', + 'dtree': url+'DecisionTree.min', + 'IChart': url+'IChart' + } + }); + + define(['JsRootCore'], function(jsroot){ + return factory({}, jsroot); + }); + +}(function(JsMVA, JSROOT){ + + JsMVA.drawTH2 = function(divid, dat_json){ + var obj = JSROOT.parse(dat_json); + JSROOT.draw(divid, obj, "colz;PAL50;text"); + }; + + JsMVA.draw = function(divid, dat_json){ + var obj = JSROOT.parse(dat_json); + JSROOT.draw(divid, obj); + }; + + JsMVA.drawNeuralNetwork = function(divid, dat_json){ + var obj = JSON.parse(dat_json); + require(['nn'], function(nn){ + nn.draw(divid, obj); + }); + }; + + JsMVA.drawDecisionTree = function(divid, dat_json){ + require(['dtree'], function(dtree){ + var obj = JSON.parse(dat_json); + dtree.draw(divid, obj); + }); + }; + + JsMVA.drawTrainingTestingErrors = function(divid, dat_json){ + var obj = JSROOT.parse(dat_json); + JSROOT.draw(divid, obj, "ALP"); + }; + + JsMVA.updateTrainingTestingErrors = function(divid, dat_json){ + var obj = JSROOT.parse(dat_json); + JSROOT.redraw(divid, obj, "ALP"); + }; + + return JsMVA; +})); diff --git a/bindings/pyroot/JsMVA/js/JsMVA.min.js b/bindings/pyroot/JsMVA/js/JsMVA.min.js new file mode 100644 index 0000000000000..56388694a1b64 --- /dev/null +++ b/bindings/pyroot/JsMVA/js/JsMVA.min.js @@ -0,0 +1 @@ +(function(r){var e="https://root.cern.ch/js/notebook/scripts/";var n="";if(requirejs.s.contexts.hasOwnProperty("_")){n=requirejs.s.contexts._.config.paths["JsMVA"].replace("JsMVA.min","")}if(console!==undefined&&typeof console.log=="function"){if(n!=""){console.log("JsMVA source dir:"+n.substring(0,n.length-1))}else{console.log("JsMVA source dir can't be resolved, requireJS doesn't have context '_', this will be a problem!")}}require.config({paths:{JsRootCore:e+"JSRootCore",nn:n+"NeuralNetwork.min",dtree:n+"DecisionTree.min",IChart:n+"IChart"}});define(["JsRootCore"],function(e){return r({},e)})})(function(r,e){r.drawTH2=function(r,n){var o=e.parse(n);e.draw(r,o,"colz;PAL50;text")};r.draw=function(r,n){var o=e.parse(n);e.draw(r,o)};r.drawNeuralNetwork=function(r,e){var n=JSON.parse(e);require(["nn"],function(e){e.draw(r,n)})};r.drawDecisionTree=function(r,e){require(["dtree"],function(n){var o=JSON.parse(e);n.draw(r,o)})};r.drawTrainingTestingErrors=function(r,n){var o=e.parse(n);e.draw(r,o,"ALP")};r.updateTrainingTestingErrors=function(r,n){var o=e.parse(n);e.redraw(r,o,"ALP")};return r}); \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/js/NeuralNetwork.js b/bindings/pyroot/JsMVA/js/NeuralNetwork.js new file mode 100644 index 0000000000000..ef2eabc92f462 --- /dev/null +++ b/bindings/pyroot/JsMVA/js/NeuralNetwork.js @@ -0,0 +1,449 @@ +/** + * Created by Attila Bagoly on 6/9/16. + */ + +(function(factory){ + require.config({ + paths: { + d3: "https://root.cern.ch/js/notebook/scripts/d3.v3.min" + } + }); + + define(['d3'], function(d3){ + return factory({}, d3); + }); +}(function(NeuralNetwork, d3) { + + // https://github.com/wbkd/d3-extended + d3.selection.prototype.moveToFront = function() { + return this.each(function(){ + this.parentNode.appendChild(this); + }); + }; + d3.selection.prototype.moveToBack = function() { + return this.each(function() { + var firstChild = this.parentNode.firstChild; + if (firstChild) { + this.parentNode.insertBefore(this, firstChild); + } + }); + }; + + var style = { + "neuron": { + "colors": { + "input": "#00A000", + "hidden": "#0000C7", + "output": "#F6BD00", + "bias": "#8F686F" + }, + "mouseon": { + "change_radius": 2, + "alpha": 0.2 + } + }, + "synapse": { + "colors": { + "negative": "#00005E", + "positive": "#5E0000"//"#FF4B00" + }, + "default_width_range": [0.5, 2], + "width_range": [0.5, 2], + "default_alpha": 0.7, + "alpha": 0.7, + "mouseon": { + "width_range": [0.5, 10], + "alpha": 0.1 + } + }, + "variables":{ + "labels_layer0_padding": 0.03 + }, + "legend": { + "pos": {"x": 150, "y": 10}, + "rect": {"width": 10, "height":10}, + "dy": 20, + "padding": 10 + } + }; + + var canvas; + + var getNeuronNumber = function(net, layer_index){ + return Number(Object.keys(net["layout"]["layer_"+layer_index]).length-1); + }; + + var getNeuronsAttr = function (net, num_layers, layer_index) { + var numn = getNeuronNumber(net, layer_index); + var neuronsAttr = Array(numn); + for(var i=0;i5?0:5))/4, + "type": (i==(numn-1) ? "bias" : (layer_index==0 ? "input" : "hidden")), + "neuron": i, + "layer": layer_index, + }; + if (layer_index==(num_layers-1)){ + neuronsAttr[i]["type"] = "output"; + } + } + return neuronsAttr; + }; + + var getWeights = function(net, layer, neuron){ + var neuron = net["layout"]["layer_"+layer]["neuron_"+neuron]; + if (neuron["nsynapses"]!=0) return neuron["weights"]; + return []; + }; + + var getMinMaxWeight = function(net, num_layers){ + var max = -1e30; + var min = 1e30; + var tmp; + for(var i=0;i tmp) min = tmp; + } + } + return {"min": min, "max": max}; + }; + + var getSynapses = function(net, layer_index, neuron, pos, layer2){ + var weights = getWeights(net, layer_index, neuron); + var synapses = Array(weights.length); + for(var i in weights){ + synapses[i] = { + "layer": layer_index, + "neuron": neuron, + "nextlayer_neuron": i, + "pos": [pos, layer2[i].position], + "weight": weights[i], + "type": (weights[i]<0 ? "negative" : "positive") + }; + } + return synapses; + }; + + var getInputLabels = function(net, layer0){ + var labels = net["variables"]; + labels.push("Bias node"); + var variables = Array(labels.length); + for(var i in layer0){ + variables[i] = { + "x": layer0[i].position.x-style["variables"]["labels_layer0_padding"]*canvas.width, + "y": layer0[i].position.y, + "text": labels[i] + ":" + }; + } + return variables; + }; + + var drawInputLabels = function(group){ + group.append("text") + .text(function(d){return d[1].text;}) + .attr("x", function(d){return d[1].x-this.getComputedTextLength();}) + .attr("y", function(d){return d[1].y+0.25*this.getBBox().height;}); + }; + + var drawNeurons = function (svg, net, neuronsattr, layer_num, input_variable_labels) { + if (input_variable_labels!==undefined){ + var dat = d3.zip(neuronsattr, getInputLabels(net, neuronsattr)); + } else { + var dat = d3.zip(neuronsattr, Array(neuronsattr.length)); + } + var group = svg.append("g").attr("id", "layer_"+layer_num).attr("class", "layer").selectAll("g") + .data(dat) + .enter() + .append("g").attr("id", function(d){return "neuron_"+layer_num+""+d[0].neuron;}); + group.append("circle") + .attr('r', function(d){return d[0].radius}) + .attr('cx', function(d){return d[0].position.x;}) + .attr('cy', function(d){return d[0].position.y;}) + .style("fill", function(d){return style["neuron"]["colors"][d[0].type];}); + if (input_variable_labels!==undefined){ + drawInputLabels(group) + } + animate(svg, group); + }; + + var scaleSynapsisPos = d3.scale.linear(); + var scaleSynapsisNeg = d3.scale.linear(); + + var synapse = d3.svg.line() + .x(function(d){return d.x;}) + .y(function(d){return d.y;}) + .interpolate("linear"); + + var drawSynapses = function(svg, net, layer1, layer1_index, layer2){ + for(var idx in layer1){ + var synapses = getSynapses(net, layer1_index, idx, layer1[idx].position, layer2); + svg.select("g#neuron_"+layer1_index+""+idx) + .selectAll("path") + .data(synapses) + .enter() + .append("path").moveToBack() + .attr("d", function(d){return synapse(d.pos);}) + .attr("stroke", function(d){return style["synapse"]["colors"][d.type]}) + .attr("stroke-width", function(d){ + return d.type=="positive" ? scaleSynapsisPos(d.weight) : scaleSynapsisNeg(Math.abs(d.weight)); + }) + .attr("stroke-opacity", style["synapse"]["alpha"]); + } + }; + + var animate = function(svg, group){ + style.synapse.width_range = Object.assign({}, style.synapse.default_width_range); + style.synapse.alpha = Object.assign({}, style.synapse.default_alpha); + group.on('mouseover', function(d) { + scaleSynapsisPos.range(style["synapse"]["mouseon"]["width_range"]); + scaleSynapsisNeg.range(style["synapse"]["mouseon"]["width_range"]); + var self = d3.select(this).moveToFront().transition(); + self.selectAll("path") + .style("stroke-opacity", 1) + .attr("stroke-width", function(d){ + return d.type=="positive" ? scaleSynapsisPos(d.weight) : scaleSynapsisNeg(Math.abs(d.weight)); + }); + self.selectAll("circle") + .style("fill-opacity", 1) + .attr("r", function(d){return d[0].radius*style["neuron"]["mouseon"]["change_radius"]}); + self.selectAll("text") + .attr("x", function(d){return d[1].x-d[0].radius-this.getComputedTextLength();}); + + var allbutnotthis = svg.selectAll("g.layer").selectAll("g") + .filter(function(x){return !(d[0].neuron==x[0].neuron&&d[0].layer==x[0].layer);}).transition(); + allbutnotthis.selectAll("circle").filter(function(x){return (d[0].layer+1)!=x[0].layer}) + .style("fill-opacity", style["neuron"]["mouseon"]["alpha"]) + .attr("r", function(d){return d[0].radius}) + allbutnotthis.selectAll("path") + .style("stroke-opacity", style["synapse"]["mouseon"]["alpha"]); + }); + group.on('mouseout', function(d){ + scaleSynapsisPos.range(style["synapse"]["width_range"]); + scaleSynapsisNeg.range(style["synapse"]["width_range"]); + var gg = svg.selectAll("g.layer").selectAll("g").transition(); + gg.selectAll("circle") + .style("fill-opacity", 1) + .attr("r", function(d){return d[0].radius;}); + gg.selectAll("path") + .style("stroke-opacity", style["synapse"]["alpha"]) + .attr("stroke-width", function(d){ + return d.type=="positive" ? scaleSynapsisPos(d.weight) : scaleSynapsisNeg(Math.abs(d.weight)); + }); + gg.selectAll("text") + .attr("x", function(d){return d[1].x-this.getComputedTextLength();}); + }); + }; + + var drawLegend = function(svg){ + var labels = [ + {"c": style["synapse"]["colors"]["positive"], "txt": "Positive weight"}, + {"c": style["synapse"]["colors"]["negative"], "txt": "Negative weight"} + ]; + var attr = style["legend"]; + + var container = svg.append("g").attr("id", "legend"); + container.selectAll("g") + .data(labels) + .enter() + .append("g") + .each(function(d, i){ + var g = d3.select(this); + g.append("rect") + .attr("x", canvas.width-attr.pos.x) + .attr("y", attr.pos.y+i*attr.dy) + .attr("width", attr.rect.width) + .attr("height", attr.rect.height) + .style("fill", function(d){return d.c;}); + g.append("text") + .attr("x", canvas.width-attr.pos.x+attr.rect.width+attr.padding) + .attr("y", attr.pos.y+i*attr.dy+attr.rect.height) + .text(function(d){return d.txt;}) + .style("fill", function(d){return d.c;}); + }); + }; + + NeuralNetwork.draw = function (divid, netobj) { + if ("layers" in netobj && "synapses" in netobj) return NeuralNetwork.drawDeepNetwork(divid, netobj); + + var svg, net; + + var div = d3.select("#"+divid); + canvas = { + width: div.property("style")["width"], + height: div.property("style")["height"] + }; + + net = netobj; + style.synapse.width_range = Object.assign({}, style.synapse.default_width_range); + style.synapse.alpha = Object.assign({}, style.synapse.default_alpha); + scaleSynapsisPos.range(style["synapse"]["width_range"]); + scaleSynapsisNeg.range(style["synapse"]["width_range"]); + + svg = div.append("svg") + .attr("id", "svg_"+divid) + .attr("width", canvas.width) + .attr("height", canvas.height); + Object.keys(canvas).forEach(function (key) { + canvas[key] = Number(canvas[key].replace("px","")) + }); + + var num_layers = Number(net["layout"]["nlayers"]); + + scaleSynapsisPos.domain([0,getMinMaxWeight(net, num_layers).max]); + scaleSynapsisNeg.domain([0, Math.abs(getMinMaxWeight(net, num_layers).min)]); + var zoom = d3.behavior.zoom() + .scaleExtent([1, 20]) + .on("zoom", function(){ + svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); + }); + svg = svg + .on("dblclick", function(){ + zoom.scale(1); + zoom.translate([0, 0]); + svg.transition().attr("transform", "translate(0,0)scale(1)"); + }) + .append("g").call(zoom).append("g"); + + var layers = Array(num_layers); + for(var i=0;i5?0:5))/4,type:o==i-1?"bias":r==0?"input":"hidden",neuron:o,layer:r};if(r==t-1){s[o]["type"]="output"}}return s};var s=function(e,t,r){var r=e["layout"]["layer_"+t]["neuron_"+r];if(r["nsynapses"]!=0)return r["weights"];return[]};var o=function(e,r){var n=-1e30;var i=1e30;var o;for(var l=0;lo)i=o}}return{min:i,max:n}};var l=function(e,t,r,n,a){var i=s(e,t,r);var o=Array(i.length);for(var l in i){o[l]={layer:t,neuron:r,nextlayer_neuron:l,pos:[n,a[l].position],weight:i[l],type:i[l]<0?"negative":"positive"}}return o};var u=function(e,t){var a=e["variables"];a.push("Bias node");var i=Array(a.length);for(var s in t){i[s]={x:t[s].position.x-r["variables"]["labels_layer0_padding"]*n.width,y:t[s].position.y,text:a[s]+":"}}return i};var p=function(e){e.append("text").text(function(e){return e[1].text}).attr("x",function(e){return e[1].x-this.getComputedTextLength()}).attr("y",function(e){return e[1].y+.25*this.getBBox().height})};var c=function(e,n,a,i,s){if(s!==undefined){var o=t.zip(a,u(n,a))}else{var o=t.zip(a,Array(a.length))}var l=e.append("g").attr("id","layer_"+i).attr("class","layer").selectAll("g").data(o).enter().append("g").attr("id",function(e){return"neuron_"+i+""+e[0].neuron});l.append("circle").attr("r",function(e){return e[0].radius}).attr("cx",function(e){return e[0].position.x}).attr("cy",function(e){return e[0].position.y}).style("fill",function(e){return r["neuron"]["colors"][e[0].type]});if(s!==undefined){p(l)}f(e,l)};var h=t.scale.linear();var y=t.scale.linear();var d=t.svg.line().x(function(e){return e.x}).y(function(e){return e.y}).interpolate("linear");var v=function(e,t,n,a,i){for(var s in n){var o=l(t,a,s,n[s].position,i);e.select("g#neuron_"+a+""+s).selectAll("path").data(o).enter().append("path").moveToBack().attr("d",function(e){return d(e.pos)}).attr("stroke",function(e){return r["synapse"]["colors"][e.type]}).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):y(Math.abs(e.weight))}).attr("stroke-opacity",r["synapse"]["alpha"])}};var f=function(e,n){r.synapse.width_range=Object.assign({},r.synapse.default_width_range);r.synapse.alpha=Object.assign({},r.synapse.default_alpha);n.on("mouseover",function(n){h.range(r["synapse"]["mouseon"]["width_range"]);y.range(r["synapse"]["mouseon"]["width_range"]);var a=t.select(this).moveToFront().transition();a.selectAll("path").style("stroke-opacity",1).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):y(Math.abs(e.weight))});a.selectAll("circle").style("fill-opacity",1).attr("r",function(e){return e[0].radius*r["neuron"]["mouseon"]["change_radius"]});a.selectAll("text").attr("x",function(e){return e[1].x-e[0].radius-this.getComputedTextLength()});var i=e.selectAll("g.layer").selectAll("g").filter(function(e){return!(n[0].neuron==e[0].neuron&&n[0].layer==e[0].layer)}).transition();i.selectAll("circle").filter(function(e){return n[0].layer+1!=e[0].layer}).style("fill-opacity",r["neuron"]["mouseon"]["alpha"]).attr("r",function(e){return e[0].radius});i.selectAll("path").style("stroke-opacity",r["synapse"]["mouseon"]["alpha"])});n.on("mouseout",function(t){h.range(r["synapse"]["width_range"]);y.range(r["synapse"]["width_range"]);var n=e.selectAll("g.layer").selectAll("g").transition();n.selectAll("circle").style("fill-opacity",1).attr("r",function(e){return e[0].radius});n.selectAll("path").style("stroke-opacity",r["synapse"]["alpha"]).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):y(Math.abs(e.weight))});n.selectAll("text").attr("x",function(e){return e[1].x-this.getComputedTextLength()})})};var g=function(e){var a=[{c:r["synapse"]["colors"]["positive"],txt:"Positive weight"},{c:r["synapse"]["colors"]["negative"],txt:"Negative weight"}];var i=r["legend"];var s=e.append("g").attr("id","legend");s.selectAll("g").data(a).enter().append("g").each(function(e,r){var a=t.select(this);a.append("rect").attr("x",n.width-i.pos.x).attr("y",i.pos.y+r*i.dy).attr("width",i.rect.width).attr("height",i.rect.height).style("fill",function(e){return e.c});a.append("text").attr("x",n.width-i.pos.x+i.rect.width+i.padding).attr("y",i.pos.y+r*i.dy+i.rect.height).text(function(e){return e.txt}).style("fill",function(e){return e.c})})};e.draw=function(a,s){if("layers"in s&&"synapses"in s)return e.drawDeepNetwork(a,s);var l,u;var p=t.select("#"+a);n={width:p.property("style")["width"],height:p.property("style")["height"]};u=s;r.synapse.width_range=Object.assign({},r.synapse.default_width_range);r.synapse.alpha=Object.assign({},r.synapse.default_alpha);h.range(r["synapse"]["width_range"]);y.range(r["synapse"]["width_range"]);l=p.append("svg").attr("id","svg_"+a).attr("width",n.width).attr("height",n.height);Object.keys(n).forEach(function(e){n[e]=Number(n[e].replace("px",""))});var d=Number(u["layout"]["nlayers"]);h.domain([0,o(u,d).max]);y.domain([0,Math.abs(o(u,d).min)]);var f=t.behavior.zoom().scaleExtent([1,20]).on("zoom",function(){l.attr("transform","translate("+t.event.translate+")scale("+t.event.scale+")")});l=l.on("dblclick",function(){f.scale(1);f.translate([0,0]);l.transition().attr("transform","translate(0,0)scale(1)")}).append("g").call(f).append("g");var w=Array(d);for(var x=0;x + + +from ROOT import TH1F, TMVA +import JPyInterface + + +## Creates the input variable histogram and perform the transformations if necessary +# @param dl DataLoader object +# @param className string Signal/Background +# @param variableName string containing the variable name +# @param numBin for creating the histogram +# @param processTrfs string containing the list of transformations to be used on input variable; eg. "I;N;D;P;U;G,D" +def GetInputVariableHist(dl, className, variableName, numBin, processTrfs=""): + dsinfo = dl.GetDefaultDataSetInfo() + vi = 0 + ivar = 0 + for i in range(dsinfo.GetNVariables()): + if dsinfo.GetVariableInfo(i).GetLabel()==variableName: + vi = dsinfo.GetVariableInfo(i) + ivar = i + break + if vi==0: + return 0 + + h = TH1F(className, str(vi.GetExpression()) + " ("+className+")", numBin, vi.GetMin(), vi.GetMax()) + + clsn = dsinfo.GetClassInfo(className).GetNumber() + ds = dsinfo.GetDataSet() + + trfsDef = processTrfs.split(';') + trfs = [] + for trfDef in trfsDef: + trfs.append(TMVA.TransformationHandler(dsinfo, "DataLoader")) + TMVA.CreateVariableTransforms( trfDef, dsinfo, trfs[-1], dl.Log()) + + inputEvents = ds.GetEventCollection() + transformed = 0 + tmp = 0 + for trf in trfs: + if transformed==0: + transformed = trf.CalcTransformations(inputEvents, 1) + else: + tmp = trf.CalcTransformations(transformed, 1) + del transformed[:] + transformed = tmp + + if transformed!=0: + for event in transformed: + if event.GetClass() != clsn: + continue + h.Fill(event.GetValue(ivar)) + del transformed + else: + for event in inputEvents: + if event.GetClass() != clsn: + continue + h.Fill(event.GetValue(ivar)) + return (h) + + +## Draw correlation matrix +# This function uses the TMVA::DataLoader::GetCorrelationMatrix function added newly to root +# @param dl the object pointer +# @param className Signal/Background +def DrawCorrelationMatrix(dl, className): + th2 = dl.GetCorrelationMatrix(className) + th2.SetMarkerSize(1.5) + th2.SetMarkerColor(0) + labelSize = 0.040 + th2.GetXaxis().SetLabelSize(labelSize) + th2.GetYaxis().SetLabelSize(labelSize) + th2.LabelsOption("d") + th2.SetLabelOffset(0.011) + JPyInterface.JsDraw.Draw(th2, 'drawTH2') + +## Draw input variables +# This function uses the previously defined GetInputVariableHist function to create the histograms +# @param dl The object pointer +# @param variableName string containing the variable name +# @param numBin for creating the histogram +# @param processTrfs string containing the list of transformations to be used on input variable; eg. "I;N;D;P;U;G,D" +def DrawInputVariable(dl, variableName, numBin=100, processTrfs=""): + sig = GetInputVariableHist(dl, "Signal", variableName, numBin, processTrfs) + bkg = GetInputVariableHist(dl, "Background", variableName, numBin, processTrfs) + c, l = JPyInterface.JsDraw.sbPlot(sig, bkg, {"xaxis": sig.GetTitle(), + "yaxis": "N", + "plot": "Input variable: "+sig.GetTitle()}) + JPyInterface.JsDraw.Draw(c) diff --git a/bindings/pyroot/JsMVA/python/JsMVA/Factory.py b/bindings/pyroot/JsMVA/python/JsMVA/Factory.py new file mode 100644 index 0000000000000..82ea47a5be4e6 --- /dev/null +++ b/bindings/pyroot/JsMVA/python/JsMVA/Factory.py @@ -0,0 +1,599 @@ +# -*- coding: utf-8 -*- +## @package JsMVA/Factory +# @authors Attila Bagoly +# Factory module with the functions to be inserted to TMVA::Factory class and helper functions and classes + + +import ROOT +from ROOT import TMVA +import JPyInterface +from xml.etree.ElementTree import ElementTree +import json +from IPython.core.display import display, HTML, clear_output +from ipywidgets import widgets +from threading import Thread +import time +from string import Template + + +## Getting method object from factory +# @param fac the TMVA::Factory object +# @param datasetName selecting the dataset +# @param methodName which method we want to get +def GetMethodObject(fac, datasetName, methodName): + method = [] + for methodMapElement in fac.fMethodsMap: + if methodMapElement[0] != datasetName: + continue + methods = methodMapElement[1] + for m in methods: + m.GetName._threaded = True + if m.GetName() == methodName: + method.append( m ) + break + if len(method) != 1: + print("Factory.GetMethodObject: no method object found") + return None + return (method[0]) + +## Reads deep neural network weights from file and returns it in JSON format +# @param xml_file path to DNN weight file +def GetDeepNetwork(xml_file): + tree = ElementTree() + tree.parse(xml_file) + roottree = tree.getroot() + network = {} + network["variables"] = [] + for v in roottree.find("Variables"): + network["variables"].append(v.get('Title')) + layout = roottree.find("Weights").find("Layout") + net = [] + for layer in layout: + net.append({"Connection": layer.get("Connection"), + "Nodes": layer.get("Nodes"), + "ActivationFunction": layer.get("ActivationFunction"), + "OutputMode": layer.get("OutputMode") + }) + network["layers"] = net + Synapses = roottree.find("Weights").find("Synapses") + synapses = { + "InputSize": Synapses.get("InputSize"), + "OutputSize": Synapses.get("OutputSize"), + "NumberSynapses": Synapses.get("NumberSynapses"), + "synapses": [] + } + for i in Synapses.text.split(" "): + tmp = i.replace("\n", "") + if len(tmp)>1: + synapses["synapses"].append(tmp) + network["synapses"] = synapses + return json.dumps(network) + +## Reads neural network weights from file and returns it in JSON format +# @param xml_file path to weight file +def GetNetwork(xml_file): + tree = ElementTree() + tree.parse(xml_file) + roottree = tree.getroot() + network = {} + network["variables"] = [] + for v in roottree.find("Variables"): + network["variables"].append(v.get('Title')) + layout = roottree.find("Weights").find("Layout") + + net = { "nlayers": layout.get("NLayers") } + for layer in layout: + neuron_num = int(layer.get("NNeurons")) + neurons = { "nneurons": neuron_num } + i = 0 + for neuron in layer: + label = "neuron_"+str(i) + i += 1 + nsynapses = int(neuron.get('NSynapses')) + neurons[label] = {"nsynapses": nsynapses} + if nsynapses == 0: + break + text = str(neuron.text) + wall = text.replace("\n","").split(" ") + weights = [] + for w in wall: + if w!="": + weights.append(float(w)) + neurons[label]["weights"] = weights + net["layer_"+str(layer.get('Index'))] = neurons + network["layout"] = net + return json.dumps(network) + +## Helper class for reading decision tree from XML file +class TreeReader: + + ## Standard Constructor + def __init__(self, fileName): + self.__xmltree = ElementTree() + self.__xmltree.parse(fileName) + self.__NTrees = int(self.__xmltree.find("Weights").get('NTrees')) + + ## Returns the number of trees + # @param self object pointer + def getNTrees(self): + return (self.__NTrees) + + # Returns DOM object to selected tree + # @param self object pointer + # @param itree the index of tree + def __getBinaryTree(self, itree): + if self.__NTrees<=itree: + print( "to big number, tree number must be less then %s"%self.__NTrees ) + return 0 + return self.__xmltree.find("Weights").find("BinaryTree["+str(itree+1)+"]") + + ## Reads the tree + # @param self the object pointer + # @param binaryTree the tree DOM object to be read + # @param tree empty object, this will be filled + # @param depth current depth + def __readTree(self, binaryTree, tree={}, depth=0): + nodes = binaryTree.findall("Node") + if len(nodes)==0: + return + if len(nodes)==1 and nodes[0].get("pos")=="s": + info = { + "IVar": nodes[0].get("IVar"), + "Cut" : nodes[0].get("Cut"), + "purity": nodes[0].get("purity"), + "pos": 0 + } + tree["info"] = info + tree["children"] = [] + self.__readTree(nodes[0], tree, 1) + return + for node in nodes: + info = { + "IVar": node.get("IVar"), + "Cut" : node.get("Cut"), + "purity": node.get("purity"), + "pos": node.get("pos") + } + tree["children"].append({ + "info": info, + "children": [] + }) + self.__readTree(node, tree["children"][-1], depth+1) + + ## Public function which returns the specified tree object + # @param self the object pointer + # @param itree selected tree index + def getTree(self, itree): + binaryTree = self.__getBinaryTree(itree) + if binaryTree==0: + return {} + tree = {} + self.__readTree(binaryTree, tree) + return tree + + ## Returns a list with input variable names + # @param self the object pointer + def getVariables(self): + varstree = self.__xmltree.find("Variables").findall("Variable") + variables = [None]*len(varstree) + for v in varstree: + variables[int(v.get('VarIndex'))] = v.get('Expression') + return variables + + +## Draw ROC curve +# @param fac the object pointer +# @param datasetName the dataset name +def DrawROCCurve(fac, datasetName): + canvas = fac.GetROCCurve(datasetName) + JPyInterface.JsDraw.Draw(canvas) + +## Draw output distributions +# @param fac the object pointer +# @param datasetName the dataset name +# @param methodName we want to see the output distribution of this method +def DrawOutputDistribution(fac, datasetName, methodName): + method = GetMethodObject(fac, datasetName, methodName) + if method==None: + return None + mvaRes = method.Data().GetResults(method.GetMethodName(), TMVA.Types.kTesting, TMVA.Types.kMaxAnalysisType) + sig = mvaRes.GetHist("MVA_S") + bgd = mvaRes.GetHist("MVA_B") + c, l = JPyInterface.JsDraw.sbPlot(sig, bgd, {"xaxis": methodName+" response", + "yaxis": "(1/N) dN^{ }/^{ }dx", + "plot": "TMVA response for classifier: "+methodName}) + JPyInterface.JsDraw.Draw(c) + +## Draw output probability distribution +# @param fac the object pointer +# @param datasetName the dataset name +# @param methodName we want to see the output probability distribution of this method +def DrawProbabilityDistribution(fac, datasetName, methodName): + method = GetMethodObject(fac, datasetName, methodName) + if method==0: + return + mvaRes = method.Data().GetResults(method.GetMethodName(), TMVA.Types.kTesting, TMVA.Types.kMaxAnalysisType) + sig = mvaRes.GetHist("Prob_S") + bgd = mvaRes.GetHist("Prob_B") #Rar_S + c, l = JPyInterface.JsDraw.sbPlot(sig, bgd, {"xaxis": "Signal probability", + "yaxis": "(1/N) dN^{ }/^{ }dx", + "plot": "TMVA probability for classifier: "+methodName}) + JPyInterface.JsDraw.Draw(c) + +## Draw cut efficiencies +# @param fac the object pointer +# @param datasetName the dataset name +# @param methodName we want to see the cut efficiencies of this method +def DrawCutEfficiencies(fac, datasetName, methodName): + method = GetMethodObject(fac, datasetName, methodName) + if method==0: + return + mvaRes = method.Data().GetResults(method.GetMethodName(), TMVA.Types.kTesting, TMVA.Types.kMaxAnalysisType) + sigE = mvaRes.GetHist("MVA_EFF_S") + bgdE = mvaRes.GetHist("MVA_EFF_B") + + fNSignal = 1000 + fNBackground = 1000 + + f = ROOT.TFormula("sigf", "x/sqrt(x+y)") + + pname = "purS_" + methodName + epname = "effpurS_" + methodName + ssigname = "significance_" + methodName + + nbins = sigE.GetNbinsX() + low = sigE.GetBinLowEdge(1) + high = sigE.GetBinLowEdge(nbins+1) + + purS = ROOT.TH1F(pname, pname, nbins, low, high) + sSig = ROOT.TH1F(ssigname, ssigname, nbins, low, high) + effpurS = ROOT.TH1F(epname, epname, nbins, low, high) + + #chop off useless stuff + sigE.SetTitle( "Cut efficiencies for "+methodName+" classifier") + + TMVA.TMVAGlob.SetSignalAndBackgroundStyle( sigE, bgdE ) + TMVA.TMVAGlob.SetSignalAndBackgroundStyle( purS, bgdE ) + TMVA.TMVAGlob.SetSignalAndBackgroundStyle( effpurS, bgdE ) + sigE.SetFillStyle( 0 ) + bgdE.SetFillStyle( 0 ) + sSig.SetFillStyle( 0 ) + sigE.SetLineWidth( 3 ) + bgdE.SetLineWidth( 3 ) + sSig.SetLineWidth( 3 ) + + purS.SetFillStyle( 0 ) + purS.SetLineWidth( 2 ) + purS.SetLineStyle( 5 ) + effpurS.SetFillStyle( 0 ) + effpurS.SetLineWidth( 2 ) + effpurS.SetLineStyle( 6 ) + sig = 0 + maxSigErr = 0 + for i in range(1,sigE.GetNbinsX()+1): + eS = sigE.GetBinContent( i ) + S = eS * fNSignal + B = bgdE.GetBinContent( i ) * fNBackground + if (S+B)==0: + purS.SetBinContent( i, 0) + else: + purS.SetBinContent( i, S/(S+B) ) + + sSig.SetBinContent( i, f.Eval(S,B) ) + effpurS.SetBinContent( i, eS*purS.GetBinContent( i ) ) + + maxSignificance = sSig.GetMaximum() + maxSignificanceErr = 0 + sSig.Scale(1/maxSignificance) + + c = ROOT.TCanvas( "canvasCutEff","Cut efficiencies for "+methodName+" classifier", JPyInterface.JsDraw.jsCanvasWidth, + JPyInterface.JsDraw.jsCanvasHeight) + + c.SetGrid(1) + c.SetTickx(0) + c.SetTicky(0) + + TMVAStyle = ROOT.gROOT.GetStyle("Plain") + TMVAStyle.SetLineStyleString( 5, "[32 22]" ) + TMVAStyle.SetLineStyleString( 6, "[12 22]" ) + + c.SetTopMargin(.2) + + effpurS.SetTitle("Cut efficiencies and optimal cut value") + if methodName.find("Cuts")!=-1: + effpurS.GetXaxis().SetTitle( "Signal Efficiency" ) + else: + effpurS.GetXaxis().SetTitle( "Cut value applied on " + methodName + " output" ) + effpurS.GetYaxis().SetTitle( "Efficiency (Purity)" ) + TMVA.TMVAGlob.SetFrameStyle( effpurS ) + + c.SetTicks(0,0) + c.SetRightMargin ( 2.0 ) + + effpurS.SetMaximum(1.1) + effpurS.Draw("histl") + + purS.Draw("samehistl") + + sigE.Draw("samehistl") + bgdE.Draw("samehistl") + + signifColor = ROOT.TColor.GetColor( "#00aa00" ) + + sSig.SetLineColor( signifColor ) + sSig.Draw("samehistl") + + effpurS.Draw( "sameaxis" ) + + + legend1 = ROOT.TLegend( c.GetLeftMargin(), 1 - c.GetTopMargin(), + c.GetLeftMargin() + 0.4, 1 - c.GetTopMargin() + 0.12 ) + legend1.SetFillStyle( 1 ) + legend1.AddEntry(sigE,"Signal efficiency","L") + legend1.AddEntry(bgdE,"Background efficiency","L") + legend1.Draw("same") + legend1.SetBorderSize(1) + legend1.SetMargin( 0.3 ) + + + legend2 = ROOT.TLegend( c.GetLeftMargin() + 0.4, 1 - c.GetTopMargin(), + 1 - c.GetRightMargin(), 1 - c.GetTopMargin() + 0.12 ) + legend2.SetFillStyle( 1 ) + legend2.AddEntry(purS,"Signal purity","L") + legend2.AddEntry(effpurS,"Signal efficiency*purity","L") + legend2.AddEntry(sSig, "S/#sqrt{ S+B }","L") + legend2.Draw("same") + legend2.SetBorderSize(1) + legend2.SetMargin( 0.3 ) + + effline = ROOT.TLine( sSig.GetXaxis().GetXmin(), 1, sSig.GetXaxis().GetXmax(), 1 ) + effline.SetLineWidth( 1 ) + effline.SetLineColor( 1 ) + effline.Draw() + + c.Update() + + tl = ROOT.TLatex() + tl.SetNDC() + tl.SetTextSize( 0.033 ) + maxbin = sSig.GetMaximumBin() + line1 = tl.DrawLatex( 0.15, 0.23, "For %1.0f signal and %1.0f background"%(fNSignal, fNBackground)) + tl.DrawLatex( 0.15, 0.19, "events the maximum S/#sqrt{S+B} is") + + if maxSignificanceErr > 0: + line2 = tl.DrawLatex( 0.15, 0.15, "%5.2f +- %4.2f when cutting at %5.2f"%( + maxSignificance, + maxSignificanceErr, + sSig.GetXaxis().GetBinCenter(maxbin)) ) + else: + line2 = tl.DrawLatex( 0.15, 0.15, "%4.2f when cutting at %5.2f"%( + maxSignificance, + sSig.GetXaxis().GetBinCenter(maxbin)) ) + + if methodName.find("Cuts")!=-1: + tl.DrawLatex( 0.13, 0.77, "Method Cuts provides a bundle of cut selections, each tuned to a") + tl.DrawLatex(0.13, 0.74, "different signal efficiency. Shown is the purity for each cut selection.") + + wx = (sigE.GetXaxis().GetXmax()+abs(sigE.GetXaxis().GetXmin()))*0.135 + rightAxis = ROOT.TGaxis( sigE.GetXaxis().GetXmax()+wx, + c.GetUymin()-0.3, + sigE.GetXaxis().GetXmax()+wx, + 0.7, 0, 1.1*maxSignificance,510, "+L") + rightAxis.SetLineColor ( signifColor ) + rightAxis.SetLabelColor( signifColor ) + rightAxis.SetTitleColor( signifColor ) + + rightAxis.SetTitleSize( sSig.GetXaxis().GetTitleSize() ) + rightAxis.SetTitle( "Significance" ) + rightAxis.Draw() + + c.Update() + + JPyInterface.JsDraw.Draw(c) + +## Draw neural network +# @param fac the object pointer +# @param datasetName the dataset name +# @param methodName we want to see the network created by this method +def DrawNeuralNetwork(fac, datasetName, methodName): + m = GetMethodObject(fac, datasetName, methodName) + if m==None: + return None + if (methodName=="DNN"): + net = GetDeepNetwork(str(m.GetWeightFileName())) + else: + net = GetNetwork(str(m.GetWeightFileName())) + JPyInterface.JsDraw.Draw(net, "drawNeuralNetwork", True) + +## Draw deep neural network +# @param fac the object pointer +# @param datasetName the dataset name +# @param methodName we want to see the deep network created by this method +def DrawDecisionTree(fac, datasetName, methodName): + m = GetMethodObject(fac, datasetName, methodName) + if m==None: + return None + tr = TreeReader(str(m.GetWeightFileName())) + + variables = tr.getVariables(); + + def clicked(b): + if treeSelector.value>tr.getNTrees(): + treeSelector.value = tr.getNTrees() + clear_output() + toJs = { + "variables": variables, + "tree": tr.getTree(treeSelector.value) + } + json_str = json.dumps(toJs) + JPyInterface.JsDraw.Draw(json_str, "drawDecisionTree", True) + + mx = str(tr.getNTrees()-1) + + treeSelector = widgets.IntText(value=0, font_weight="bold") + drawTree = widgets.Button(description="Draw", font_weight="bold") + label = widgets.HTML("
Decision Tree [0-"+mx+"]:
") + + drawTree.on_click(clicked) + container = widgets.HBox([label,treeSelector, drawTree]) + display(container) + +## Rewrites the TMVA::Factory::TrainAllMethods function. This function provides interactive training. +# @param fac the factory object pointer +def __TrainAllMethods(fac): + clear_output() + #stop button + button = """ + + + + """ + #progress bar + inc = Template(""" + + """) + progress_bar = Template(""" + +
+
+
0%
+
+
+ """) + progress_bar_idx = 0 + + def exit_supported(mn): + name = str(mn) + es = ["SVM", "Cuts", "Boost", "BDT"] + for e in es: + if name.find(e) != -1: + return True + return False + + wait_times = {"MLP": 0.5, "DNN": 1, "BDT": 0.5} + + for methodMapElement in fac.fMethodsMap: + display(HTML("

Dataset: "+str(methodMapElement[0])+"

")) + for m in methodMapElement[1]: + m.GetName._threaded = True + name = str(m.GetName()) + display(HTML("

Train method: "+name+"

")) + m.InitIPythonInteractive() + t = Thread(target=ROOT.TMVA.MethodBase.TrainMethod, args=[m]) + t.start() + if name in wait_times: + display(HTML(button)) + time.sleep(wait_times[name]) + if m.GetMaxIter() != 0: + display(HTML(progress_bar.substitute({"id": progress_bar_idx}))) + display(HTML(inc.substitute({"id": progress_bar_idx, "progress": 100 * m.GetCurrentIter() / m.GetMaxIter()}))) + JPyInterface.JsDraw.Draw(m.GetInteractiveTrainingError(), "drawTrainingTestingErrors") + try: + while not m.TrainingEnded(): + JPyInterface.JsDraw.InsertData(m.GetInteractiveTrainingError()) + if m.GetMaxIter() != 0: + display(HTML(inc.substitute({ + "id": progress_bar_idx, + "progress": 100 * m.GetCurrentIter() / m.GetMaxIter() + }))) + time.sleep(0.5) + except KeyboardInterrupt: + m.ExitFromTraining() + else: + if exit_supported(name): + display(HTML(button)) + time.sleep(0.5) + if m.GetMaxIter()!=0: + display(HTML(progress_bar.substitute({"id": progress_bar_idx}))) + display(HTML(inc.substitute({"id": progress_bar_idx, "progress": 100*m.GetCurrentIter()/m.GetMaxIter()}))) + else: + display(HTML("Training...")) + if exit_supported(name): + try: + while not m.TrainingEnded(): + if m.GetMaxIter()!=0: + display(HTML(inc.substitute({ + "id": progress_bar_idx, + "progress": 100 * m.GetCurrentIter() / m.GetMaxIter() + }))) + time.sleep(0.5) + except KeyboardInterrupt: + m.ExitFromTraining() + else: + while not m.TrainingEnded(): + if m.GetMaxIter() != 0: + display(HTML(inc.substitute({ + "id": progress_bar_idx, + "progress": 100 * m.GetCurrentIter() / m.GetMaxIter() + }))) + time.sleep(0.5) + if m.GetMaxIter() != 0: + display(HTML(inc.substitute({ + "id": progress_bar_idx, + "progress": 100 * m.GetCurrentIter() / m.GetMaxIter() + }))) + else: + display(HTML("End")) + progress_bar_idx += 1 + t.join() + return + + +ROOT.TMVA.MethodBase.GetInteractiveTrainingError._threaded = True +ROOT.TMVA.MethodBase.ExitFromTraining._threaded = True +ROOT.TMVA.MethodBase.TrainingEnded._threaded = True +ROOT.TMVA.MethodBase.TrainMethod._threaded = True +ROOT.TMVA.Factory.TrainAllMethods = __TrainAllMethods \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py b/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py new file mode 100644 index 0000000000000..2bd266e7f97ba --- /dev/null +++ b/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- +## @package JsMVA/JPyInterface +# @authors Attila Bagoly +# This package is responsible for adding the drawing methods to TMVA +# and for creating the JavaScript outputs from objects. + + +from IPython.core.display import display, HTML +from string import Template +import ROOT +import DataLoader +import Factory + + +## Function inserter class +# This class contains the methods which are invoked by using jsmva magic, and will inject the new methods +# to TMVA::Factory, TMVA::DataLoader +class functions: + + ## The method inserter function + # @param target which class to insert + # @param source module which contains the methods to insert + # @param args list of methods to insert + @staticmethod + def __register(target, source, *args): + for arg in args: + if hasattr(target, arg): + continue + setattr(target, arg, getattr(source, arg)) + + ## The method removes inserted functions from class + # @param target from which class to remove functions + # @param args list of methods to remove + @staticmethod + def __unregister(target, *args): + for arg in args: + if hasattr(target, arg): + delattr(target, arg) + + ## Reads all methods containing a selector from specified module + # @param module finding methods in this module + # @param selector if method in module contains this string will be selected + @staticmethod + def __getMethods(module, selector): + methods = [] + for method in dir(module): + if method.find(selector)!=-1: + methods.append(method) + return methods + + ## This function will register all functions which name contains "Draw" to TMVA.DataLoader and TMVA.Factory + # from DataLoader and Factory modules + @staticmethod + def register(): + functions.__register(ROOT.TMVA.DataLoader, DataLoader, *functions.__getMethods(DataLoader, "Draw")) + functions.__register(ROOT.TMVA.Factory, Factory, *functions.__getMethods(Factory, "Draw")) + + ## This function will remove all functions which name contains "Draw" from TMVA.DataLoader and TMVA.Factory + # if the function was inserted from DataLoader and Factory modules + @staticmethod + def unregister(): + functions.__register(ROOT.TMVA.DataLoader, DataLoader, *functions.__getMethods(DataLoader, "Draw")) + functions.__register(ROOT.TMVA.Factory, Factory, *functions.__getMethods(Factory, "Draw")) + + +## Class for creating the output scripts and inserting them to cell output +class JsDraw: + #__jsMVASourceDir = "https://rawgit.com/qati/GSOC16/master/src/js" + ## String containing the link to JavaScript files + __jsMVASourceDir = "http://localhost:8888/notebooks/code/GSOC/wd/src/js" + + ## Drawing are sizes + jsCanvasWidth = 800 + jsCanvasHeight = 450 + + ## id for drawing area + __divUID = 0 + + ## Template containing HTML code with draw area and drawing JavaScript + __jsCode = Template(""" +
+ +""") + + ## Template containing data insertion JavaScript code + __jsCodeForDataInsert = Template("""""") + + ## Inserts the draw area and drawing JavaScript to output + # @param obj ROOT object (will be converted to JSON) or JSON string containing the data to be drawed + # @param jsDrawMethod the JsMVA JavaScrip object method name to be used for drawing + # @param objIsJSON obj is ROOT object or JSON + @staticmethod + def Draw(obj, jsDrawMethod='draw', objIsJSON=False): + if objIsJSON: + dat = obj + else: + dat = ROOT.TBufferJSON.ConvertToJSON(obj) + dat = str(dat).replace("\n","") + JsDraw.__divUID += 1 + display(HTML(JsDraw.__jsCode.substitute({ + 'funcName': jsDrawMethod, + 'divid':'jstmva_'+str(JsDraw.__divUID), + 'dat': dat, + 'PATH': JsDraw.__jsMVASourceDir, + 'width': JsDraw.jsCanvasWidth, + 'height': JsDraw.jsCanvasHeight + }))) + + ## Inserts the data inserter JavaScript code to output + # @param obj ROOT object (will be converted to JSON) or JSON string containing the data to be inserted + # @param jsDrawMethod the JsMVA JavaScrip object method name to be used for inserting the new data + # @param objIsJSON obj is ROOT object or JSON + @staticmethod + def InsertData(obj, dataInserterMethod="updateTrainingTestingErrors", objIsJSON=False): + if objIsJSON: + dat = obj + else: + dat = ROOT.TBufferJSON.ConvertToJSON(obj) + dat = str(dat).replace("\n", "") + display(HTML(JsDraw.__jsCodeForDataInsert.substitute({ + 'funcName': dataInserterMethod, + 'divid': 'jstmva_'+str(JsDraw.__divUID), + 'dat': dat + }))) + + ## Draws a signal and background histogram to a newly created TCanvas + # @param sig signal histogram + # @param bkg background histogram + # @param title all labels + @staticmethod + def sbPlot(sig, bkg, title): + canvas = ROOT.TCanvas("csbplot", title["plot"], JsDraw.jsCanvasWidth, JsDraw.jsCanvasHeight) + sig.SetMaximum(ROOT.TMath.Max(sig.GetMaximum(),bkg.GetMaximum()*1.1)) + sig.SetTitle(sig.GetTitle().replace("(Signal)","")) + sig.GetXaxis().SetTitle(title["xaxis"]) + sig.GetYaxis().SetTitle(title["yaxis"]) + sig.SetTitle(title["plot"]) + bkg.SetFillColorAlpha(ROOT.kRed, 0.3) + sig.SetFillColor(ROOT.kBlue) + bkg.SetLineColor(ROOT.kRed) + sig.Draw("hist") + bkg.Draw("histsame") + + legend = ROOT.TLegend(1-canvas.GetLeftMargin()-0.39, 1-canvas.GetTopMargin()-0.15, + 1-canvas.GetLeftMargin()-0.01, 1-canvas.GetTopMargin()-0.01) + legend.SetFillStyle(1) + legend.AddEntry(sig, "Signal", "F") + legend.AddEntry(bkg, "Background", "F") + legend.SetBorderSize(1) + legend.SetMargin(0.3) + legend.Draw() + + return (canvas, legend) \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/python/JsMVA/JsMVAMagic.py b/bindings/pyroot/JsMVA/python/JsMVA/JsMVAMagic.py new file mode 100644 index 0000000000000..a7262cf0d67e9 --- /dev/null +++ b/bindings/pyroot/JsMVA/python/JsMVA/JsMVAMagic.py @@ -0,0 +1,31 @@ +# -*- coding:utf-8 -*- +## Magic class for JsMVA +# @authors: Attila Bagoly + +from IPython.core.magic import Magics, magics_class, line_magic +from IPython.core.magic_arguments import argument, magic_arguments, parse_argstring + + +@magics_class +class JsMVAMagic(Magics): + + ## Standard constructor + def __init__(self, shell): + super(JsMVAMagic, self).__init__(shell) + + ## jsmva magic + @line_magic + @magic_arguments() + @argument('arg', nargs="?", default="on", help='Enable/Disable JavaScript visualisation for TMVA') + def jsmva(self, line): + from JPyInterface import functions + args = parse_argstring(self.jsmva, line) + if args.arg == 'on': + functions.register() + elif args.arg == 'off': + functions.unregister() + + +## Function for registering the magic class +def load_ipython_extension(ipython): + ipython.register_magics(JsMVAMagic) \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/python/JsMVA/__init__.py b/bindings/pyroot/JsMVA/python/JsMVA/__init__.py new file mode 100644 index 0000000000000..63a0addd6be5c --- /dev/null +++ b/bindings/pyroot/JsMVA/python/JsMVA/__init__.py @@ -0,0 +1,9 @@ +from IPython.core.extensions import ExtensionManager + +def loadExtensions(): + ip = get_ipython() + extMgr = ExtensionManager(ip) + extMgr.load_extension("JsMVA.JsMVAMagic") + + +loadExtensions(); \ No newline at end of file diff --git a/bindings/pyroot/ROOT.py b/bindings/pyroot/ROOT.py index 70c7302dc6383..e616d925907d7 100644 --- a/bindings/pyroot/ROOT.py +++ b/bindings/pyroot/ROOT.py @@ -583,9 +583,13 @@ def _finishSchedule( ROOT = self ): ### Add some infrastructure if we are being imported via a Jupyter Kernel ------ if '__IPYTHON__' in __builtins__ and __IPYTHON__: from IPython import get_ipython + import sys, os + pathToThisFile = os.path.dirname(os.path.abspath(__file__)) + sys.path.append(os.path.expanduser(os.path.join(pathToThisFile,"JsMVA/python"))) ip = get_ipython() if hasattr(ip,"kernel"): import JupyROOT + import JsMVA ### b/c of circular references, the facade needs explicit cleanup --------------- import atexit From 81acead5f2dfff54c8b9627040d85c71962bf201 Mon Sep 17 00:00:00 2001 From: Attila Bagoly Date: Wed, 20 Jul 2016 11:22:34 +0200 Subject: [PATCH 04/14] Fixes in interactive training in jupyter integration layer --- tmva/tmva/inc/TMVA/FitterBase.h | 4 ++-- tmva/tmva/inc/TMVA/SVWorkingSet.h | 4 ++-- tmva/tmva/inc/TMVA/SimulatedAnnealing.h | 4 ++-- tmva/tmva/src/MethodCFMlpANN.cxx | 1 - tmva/tmva/src/MethodCuts.cxx | 2 +- tmva/tmva/src/MethodDNN.cxx | 8 ++++---- tmva/tmva/src/MethodSVM.cxx | 2 +- tmva/tmva/src/SVWorkingSet.cxx | 2 +- tmva/tmva/src/SimulatedAnnealingFitter.cxx | 6 ++++-- 9 files changed, 17 insertions(+), 16 deletions(-) diff --git a/tmva/tmva/inc/TMVA/FitterBase.h b/tmva/tmva/inc/TMVA/FitterBase.h index 8a7d511834378..1b7e967fe3f81 100644 --- a/tmva/tmva/inc/TMVA/FitterBase.h +++ b/tmva/tmva/inc/TMVA/FitterBase.h @@ -97,8 +97,8 @@ namespace TMVA { TString fClassName; // remove TMVA:: from TObject name // variables needed by JsMVA - UInt_t *fIPyCurrentIter=nullptr, *fIPyMaxIter=nullptr; - bool* fExitFromTraining; + UInt_t *fIPyCurrentIter = nullptr, *fIPyMaxIter = nullptr; + bool* fExitFromTraining = nullptr; ClassDef(FitterBase,0); // Baseclass for fitters }; diff --git a/tmva/tmva/inc/TMVA/SVWorkingSet.h b/tmva/tmva/inc/TMVA/SVWorkingSet.h index 5376e5917b184..ee4605d28cf01 100644 --- a/tmva/tmva/inc/TMVA/SVWorkingSet.h +++ b/tmva/tmva/inc/TMVA/SVWorkingSet.h @@ -88,8 +88,8 @@ namespace TMVA { mutable MsgLogger* fLogger; //! message logger // variables for JsMVA - UInt_t *fIPyCurrentIter=nullptr; - bool * fExitFromTraining; + UInt_t *fIPyCurrentIter = nullptr; + bool * fExitFromTraining = nullptr; void SetIndex( TMVA::SVEvent* ); }; diff --git a/tmva/tmva/inc/TMVA/SimulatedAnnealing.h b/tmva/tmva/inc/TMVA/SimulatedAnnealing.h index 7033d42057afc..024dde34975ee 100644 --- a/tmva/tmva/inc/TMVA/SimulatedAnnealing.h +++ b/tmva/tmva/inc/TMVA/SimulatedAnnealing.h @@ -124,8 +124,8 @@ namespace TMVA { Double_t fProgress; // variables for JsMVA - UInt_t *fIPyCurrentIter=nullptr; - bool * fExitFromTraining; + UInt_t *fIPyCurrentIter = nullptr; + bool * fExitFromTraining = nullptr; ClassDef(SimulatedAnnealing,0); // Base class for Simulated Annealing fitting }; diff --git a/tmva/tmva/src/MethodCFMlpANN.cxx b/tmva/tmva/src/MethodCFMlpANN.cxx index 1f213f4445250..61168155df394 100644 --- a/tmva/tmva/src/MethodCFMlpANN.cxx +++ b/tmva/tmva/src/MethodCFMlpANN.cxx @@ -286,7 +286,6 @@ void TMVA::MethodCFMlpANN::Train( void ) Int_t *nodes = new Int_t[nlayers]; Int_t ncycles(fNcycles); - for (Int_t i=0; iSetIPythonInteractive(&fExitFromTraining, &fIPyMaxIter, &fIPyCurrentIter); + if (fInteractive) fitter->SetIPythonInteractive(&fExitFromTraining, &fIPyMaxIter, &fIPyCurrentIter); fitter->CheckForUnusedOptions(); diff --git a/tmva/tmva/src/MethodDNN.cxx b/tmva/tmva/src/MethodDNN.cxx index 228fd18e81092..9e1e857cd2a81 100644 --- a/tmva/tmva/src/MethodDNN.cxx +++ b/tmva/tmva/src/MethodDNN.cxx @@ -508,10 +508,10 @@ void TMVA::MethodDNN::Train() Log() << kINFO << "Using Standard Implementation."; - if (fInteractive && fInteractive->NotInitialized()){ - std::vector titles = {"Error on training set", "Error on test set"}; - fInteractive->Init(titles); - } + if (fInteractive && fInteractive->NotInitialized()){ + std::vector titles = {"Error on training set", "Error on test set"}; + fInteractive->Init(titles); + } std::vector trainPattern; std::vector testPattern; diff --git a/tmva/tmva/src/MethodSVM.cxx b/tmva/tmva/src/MethodSVM.cxx index a841931cb2cf4..2bd0d072208b0 100644 --- a/tmva/tmva/src/MethodSVM.cxx +++ b/tmva/tmva/src/MethodSVM.cxx @@ -383,7 +383,7 @@ void TMVA::MethodSVM::Train() Timer timer( GetName() ); Log() << kINFO << "Sorry, no computing time forecast available for SVM, please wait ..." << Endl; - fWgSet->SetIPythonInteractive(&fExitFromTraining, &fIPyCurrentIter); + if (fInteractive) fWgSet->SetIPythonInteractive(&fExitFromTraining, &fIPyCurrentIter); fWgSet->Train(fMaxIter); diff --git a/tmva/tmva/src/SVWorkingSet.cxx b/tmva/tmva/src/SVWorkingSet.cxx index a62b337381c19..632db3004ae7a 100644 --- a/tmva/tmva/src/SVWorkingSet.cxx +++ b/tmva/tmva/src/SVWorkingSet.cxx @@ -391,7 +391,7 @@ void TMVA::SVWorkingSet::Train(UInt_t nMaxIter) while ((numChanged > 0) || (examineAll > 0)) { if (fIPyCurrentIter) *fIPyCurrentIter = numit; - if (*fExitFromTraining) break; + if (fExitFromTraining && *fExitFromTraining) break; numChanged = 0; if (examineAll) { for (idIter = fInputData->begin(); idIter!=fInputData->end(); idIter++){ diff --git a/tmva/tmva/src/SimulatedAnnealingFitter.cxx b/tmva/tmva/src/SimulatedAnnealingFitter.cxx index 5c7ec03f55bce..fa36eafc1f53e 100644 --- a/tmva/tmva/src/SimulatedAnnealingFitter.cxx +++ b/tmva/tmva/src/SimulatedAnnealingFitter.cxx @@ -143,8 +143,10 @@ Double_t TMVA::SimulatedAnnealingFitter::Run( std::vector& pars ) fTemperatureScale, fAdaptiveSpeed, fTemperatureAdaptiveStep, fUseDefaultScale, fUseDefaultTemperature ); - if (fIPyMaxIter) *fIPyMaxIter = fMaxCalls; - sa.SetIPythonInteractive(fExitFromTraining, fIPyCurrentIter); + if (fIPyMaxIter){ + *fIPyMaxIter = fMaxCalls; + sa.SetIPythonInteractive(fExitFromTraining, fIPyCurrentIter); + } // minimise Double_t fcn = sa.Minimize( pars ); From f12c04ed25bee5dce80e255708576a2c4aaae0da Mon Sep 17 00:00:00 2001 From: Attila Bagoly Date: Wed, 20 Jul 2016 23:45:32 +0200 Subject: [PATCH 05/14] JsMVA: TMVA's method rewriter; 'overloaded' methods: Factory::TrainAllMethods, Factory::Factory, Factory::BookMethods --- bindings/pyroot/JsMVA/python/JsMVA/Factory.py | 95 +++++++++++++++++-- .../pyroot/JsMVA/python/JsMVA/JPyInterface.py | 32 ++++++- .../pyroot/JsMVA/python/JsMVA/__init__.py | 3 +- 3 files changed, 118 insertions(+), 12 deletions(-) diff --git a/bindings/pyroot/JsMVA/python/JsMVA/Factory.py b/bindings/pyroot/JsMVA/python/JsMVA/Factory.py index 82ea47a5be4e6..219f58bbd5194 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/Factory.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/Factory.py @@ -14,6 +14,7 @@ from threading import Thread import time from string import Template +import types ## Getting method object from factory @@ -438,9 +439,9 @@ def clicked(b): container = widgets.HBox([label,treeSelector, drawTree]) display(container) -## Rewrites the TMVA::Factory::TrainAllMethods function. This function provides interactive training. +## Rewrite function for TMVA::Factory::TrainAllMethods. This function provides interactive training. # @param fac the factory object pointer -def __TrainAllMethods(fac): +def ChangeTrainAllMethods(fac): clear_output() #stop button button = """ @@ -591,9 +592,87 @@ def exit_supported(mn): t.join() return - -ROOT.TMVA.MethodBase.GetInteractiveTrainingError._threaded = True -ROOT.TMVA.MethodBase.ExitFromTraining._threaded = True -ROOT.TMVA.MethodBase.TrainingEnded._threaded = True -ROOT.TMVA.MethodBase.TrainMethod._threaded = True -ROOT.TMVA.Factory.TrainAllMethods = __TrainAllMethods \ No newline at end of file +## Get's special parameters from kwargs and converts to positional parameter +def __ConvertKwargsToArgs(positionalArgumentsToNamed, *args, **kwargs): + # args[0] = self + args = list(args) + idx = 0 + PositionalArgsEnded = False + for argName in positionalArgumentsToNamed: + if not PositionalArgsEnded: + if argName in kwargs: + if (idx+1)!=len(args): + raise AttributeError + PositionalArgsEnded = True + else: + idx += 1 + if PositionalArgsEnded and argName not in kwargs: + raise AttributeError + if argName in kwargs: + args.append(kwargs[argName]) + del kwargs[argName] + args = tuple(args) + return (args, kwargs) + +## Converts object to TMVA style option string +def __ProcessParameters(optStringStartIndex, *args, **kwargs): + originalFunction = None + if optStringStartIndex!=-10: + originalFunction = kwargs["originalFunction"] + del kwargs["originalFunction"] + OptionStringPassed = False + if (len(args)-1) == optStringStartIndex: + opt = args[optStringStartIndex] + ":" + tmp = list(args) + del tmp[optStringStartIndex] + args = tuple(tmp) + OptionStringPassed = True + else: + opt = "" + for key in kwargs: + if type(kwargs[key]) == types.BooleanType: + if kwargs[key] == True: + opt += key + ":" + else: + opt += "!" + key + ":" + else: + opt += key + "=" + str(kwargs[key]) + ":" + tmp = list(args) + if OptionStringPassed or len(kwargs)>0: + tmp.append( opt[:-1] ) + return ( originalFunction, tuple(tmp) ) + +## Rewrite the constructor of TMVA::Factory +def ChangeCallOriginal__init__(*args, **kwargs): + try: + args, kwargs = __ConvertKwargsToArgs(["JobName", "TargetFile"], *args, **kwargs) + except AttributeError: + try: + args, kwargs = __ConvertKwargsToArgs(["JobName"], *args, **kwargs) + except AttributeError: + raise AttributeError + originalFunction, args = __ProcessParameters(3, *args, **kwargs) + return originalFunction(*args) + +## Rewrite the constructor of TMVA::Factory +def ChangeCallOriginalBookMethod(*args, **kwargs): + compositeOpts = False + composite = False + if "Composite" in kwargs: + composite = kwargs["Composite"] + del kwargs["Composite"] + if "CompositeOptions" in kwargs: + compositeOpts = kwargs["CompositeOptions"] + del kwargs["CompositeOptions"] + args, kwargs = __ConvertKwargsToArgs(["DataLoader", "Method", "MethodTitle"], *args, **kwargs) + originalFunction, args = __ProcessParameters(4, *args, **kwargs) + if composite!=False: + args = list(args) + args.append(composite) + args = tuple(args) + if compositeOpts!=False: + o, compositeOptStr = __ProcessParameters(-10, **compositeOpts) + args = list(args) + args.append(compositeOptStr[0]) + args = tuple(args) + return originalFunction(*args) \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py b/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py index 2bd266e7f97ba..13de4c14c98b3 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py @@ -17,6 +17,12 @@ # to TMVA::Factory, TMVA::DataLoader class functions: + ## Threaded functions + ThreadedFunctions = { + "MethodBase": ["GetInteractiveTrainingError", "ExitFromTraining", "TrainingEnded", "TrainMethod", + "GetMaxIter", "GetCurrentIter"] + } + ## The method inserter function # @param target which class to insert # @param source module which contains the methods to insert @@ -28,6 +34,24 @@ def __register(target, source, *args): continue setattr(target, arg, getattr(source, arg)) + ## This method change TMVA methods with new methods + # @param target which class to insert + # @param source module which contains the methods to insert + # @param args list of methods to insert + @staticmethod + def __changeMethod(target, source, *args): + def rewriter(originalFunction, newFunction): + def wrapper(*args, **kwargs): + kwargs["originalFunction"] = originalFunction + return newFunction(*args, **kwargs) + return wrapper + for arg in args: + if arg.find("CallOriginal")!=-1: + originalName = arg.replace("Change", "").replace("CallOriginal", "") + setattr(target, originalName, rewriter(getattr(target, originalName), getattr(source, arg))) + else: + setattr(target, arg.replace("Change", ""), getattr(source, arg)) + ## The method removes inserted functions from class # @param target from which class to remove functions # @param args list of methods to remove @@ -54,6 +78,11 @@ def __getMethods(module, selector): def register(): functions.__register(ROOT.TMVA.DataLoader, DataLoader, *functions.__getMethods(DataLoader, "Draw")) functions.__register(ROOT.TMVA.Factory, Factory, *functions.__getMethods(Factory, "Draw")) + functions.__changeMethod(ROOT.TMVA.Factory, Factory, *functions.__getMethods(Factory, "Change")) + functions.__changeMethod(ROOT.TMVA.DataLoader, DataLoader, *functions.__getMethods(DataLoader, "Change")) + for key in functions.ThreadedFunctions: + for func in functions.ThreadedFunctions[key]: + setattr(getattr(getattr(ROOT.TMVA, key), func), "_threaded", True) ## This function will remove all functions which name contains "Draw" from TMVA.DataLoader and TMVA.Factory # if the function was inserted from DataLoader and Factory modules @@ -65,9 +94,8 @@ def unregister(): ## Class for creating the output scripts and inserting them to cell output class JsDraw: - #__jsMVASourceDir = "https://rawgit.com/qati/GSOC16/master/src/js" ## String containing the link to JavaScript files - __jsMVASourceDir = "http://localhost:8888/notebooks/code/GSOC/wd/src/js" + __jsMVASourceDir = "https://rawgit.com/qati/GSOC16/master/src/js" ## Drawing are sizes jsCanvasWidth = 800 diff --git a/bindings/pyroot/JsMVA/python/JsMVA/__init__.py b/bindings/pyroot/JsMVA/python/JsMVA/__init__.py index 63a0addd6be5c..75d52d3d33ba1 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/__init__.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/__init__.py @@ -5,5 +5,4 @@ def loadExtensions(): extMgr = ExtensionManager(ip) extMgr.load_extension("JsMVA.JsMVAMagic") - -loadExtensions(); \ No newline at end of file +loadExtensions() \ No newline at end of file From 7fd8d9fc1cf8933c74f09f9f53a2b5922bbc98ad Mon Sep 17 00:00:00 2001 From: Attila Bagoly Date: Mon, 25 Jul 2016 10:42:12 +0200 Subject: [PATCH 06/14] sync with qati/GSOC16/src --- bindings/pyroot/JsMVA/js/DecisionTree.js | 13 +-- bindings/pyroot/JsMVA/js/DecisionTree.min.js | 2 +- bindings/pyroot/JsMVA/js/JsMVA.js | 48 ++++++++- bindings/pyroot/JsMVA/js/JsMVA.min.js | 2 +- bindings/pyroot/JsMVA/js/NeuralNetwork.js | 81 +++++++++----- bindings/pyroot/JsMVA/js/NeuralNetwork.min.js | 2 +- .../pyroot/JsMVA/python/JsMVA/DataLoader.py | 19 +++- bindings/pyroot/JsMVA/python/JsMVA/Factory.py | 101 +++++++----------- .../pyroot/JsMVA/python/JsMVA/JPyInterface.py | 71 ++++++++++-- .../pyroot/JsMVA/python/JsMVA/JsMVAMagic.py | 3 +- .../pyroot/JsMVA/python/JsMVA/__init__.py | 3 + 11 files changed, 237 insertions(+), 108 deletions(-) diff --git a/bindings/pyroot/JsMVA/js/DecisionTree.js b/bindings/pyroot/JsMVA/js/DecisionTree.js index 5f4e5c2b11f7c..4493f355cc7c1 100644 --- a/bindings/pyroot/JsMVA/js/DecisionTree.js +++ b/bindings/pyroot/JsMVA/js/DecisionTree.js @@ -4,13 +4,6 @@ (function(factory){ - - require.config({ - paths:{ - d3: "https://root.cern.ch/js/notebook/scripts/d3.v3.min" - } - }); - var url = ""; if (require.s.contexts.hasOwnProperty("_")) { url = require.s.contexts._.config.paths["JsMVA"].replace("src/js/JsMVA.min",""); @@ -114,8 +107,9 @@ }; var drawLabels = function(nodeContainer) { + var height = style.node.height; nodeContainer.append("text") - .attr("dy", (style.node.height * 0.35) + "px") + .attr("dy", height* 0.35) .attr("class", "label1") .attr("dx", style.text.padding) .style("fill-opacity", 1e-6) @@ -129,7 +123,7 @@ nodeContainer.append("text") .attr("class", "label2") .attr("dx", style.text.padding) - .attr("dy", (style.node.height*0.75)+"px") + .attr("dy", height*0.75) .style("fill-opacity", 1e-6) .style("cursor", "pointer") .text(function(d){ @@ -261,6 +255,7 @@ var makePathNodesBigger = function(node, i, clear){ var width = (clear) ? style.node.width : 2*style.node.width, height = (clear) ? style.node.height : 1.5*style.node.height; + console.log("anim height:"+String(height)); svg.selectAll("g.nodes rect").filter(function(d){d.bigger=(clear) ? false : true; return d.id==node.id;}) .transition().duration(style.aduration/2) .attr("width", width+"px") diff --git a/bindings/pyroot/JsMVA/js/DecisionTree.min.js b/bindings/pyroot/JsMVA/js/DecisionTree.min.js index 5856d68aa83d5..79db6c2ae9237 100644 --- a/bindings/pyroot/JsMVA/js/DecisionTree.min.js +++ b/bindings/pyroot/JsMVA/js/DecisionTree.min.js @@ -1 +1 @@ -(function(t){require.config({paths:{d3:"https://root.cern.ch/js/notebook/scripts/d3.v3.min"}});var e="";if(require.s.contexts.hasOwnProperty("_")){e=require.s.contexts._.config.paths["JsMVA"].replace("src/js/JsMVA.min","")}define(["d3"],function(n){return t({},n,e)})})(function(t,e,n){Object.size=function(t){return Object.keys(t).length};var r={margin:{x:20,y:20},node:{padding:10,yspace:40,xspace:20,width:150,height:40,mwidth:150,mheight:60,colors:{focus:"#033A00",closed:"#00A62B",pureBkg:"red",pureSig:"blue"},swidth:"4px"},link:{colors:{default:"#ccc",focus:"#033A00"},width:"4px",focus_width:"8px"},aduration:1500,legend:{size:20,rect_width:100,rect_height:30,rect_fucus_width:115},text:{color:"#DEDEDE",padding:"4px"},buttons:{reset:{width:"36px",height:"36px",alpha:"0.5",img:n+"img/reset.png",background:"white"}}};var i=e.scale.linear().range([r.node.colors.pureBkg,r.node.colors.pureSig]);var o,a,d,l;var s=e.layout.tree();var c=function(){var t=e.svg.diagonal();var n=function(e,n,i){if(i)return t(e);return t({source:{x:e.source.x+r.node.width,y:e.source.y},target:{x:e.target.x+r.node.width,y:e.target.y-r.node.height}})};return n}();var u=function(t){if("children"in t){t._children=t.children;t.children=null}else{t.children=t._children;t._children=null}x(t)};var h=function(t){t.append("text").attr("dy",r.node.height*.35+"px").attr("class","label1").attr("dx",r.text.padding).style("fill-opacity",1e-6).style("font-size",1e-6+"px").style("cursor","pointer").style("fill",r.text.color).style("font-weight","bold").text(function(t){return"S/(S+B)="+Number(t.info.purity).toFixed(3)});t.append("text").attr("class","label2").attr("dx",r.text.padding).attr("dy",r.node.height*.75+"px").style("fill-opacity",1e-6).style("cursor","pointer").text(function(t){return t.info.IVar!=-1?l[t.info.IVar]+">"+Number(t.info.Cut).toFixed(3):""}).style("font-size",1e-6+"px").style("fill",r.text.color).style("font-weight","bold")};var f=function(t,n){var o=t.enter().append("g").attr("class","nodes").attr("transform",function(t){return"translate("+n.x0+","+n.y0+")"}).style("cursor","pointer");o.filter(function(t){return t.parent}).on("click",u).on("mouseover",p).on("contextmenu",function(t,n){e.event.preventDefault();y(t)}).on("mouseleave",function(t,e){if(t.bigger)y(t,e,1);return p(t,e,1)});o.append("rect").attr("width",1e-6).attr("height",1e-6);h(o);t.transition().duration(r.aduration).attr("transform",function(t){return"translate("+(t.x+r.node.width*.5)+","+(t.y-r.node.height)+")"});t.select("rect").transition().duration(r.aduration).attr("width",r.node.width).attr("height",r.node.height).attr("fill",function(t){return i(Number(t.info.purity))}).style("stroke-width",r.node.swidth).style("stroke",function(t){return t._children?r.node.colors.closed:""});t.selectAll("text").transition().duration(r.aduration).style("font-size",function(t){var e="S/(S+B)="+Number(t.info.purity).toFixed(3);var n=t.info.IVar!=-1?l[t.info.IVar]+">"+Number(t.info.Cut).toFixed(3):"";t.font_size=1.5*(r.node.width-2*Number(r.node.swidth.replace("px","")))/Math.max(e.length,n.length);return t.font_size+"px"}).attr("dx",r.text.padding).attr("dy",function(t){return(e.select(this).attr("class")=="label1"?r.node.height*.35:r.node.height*.75)+"px"}).style("fill-opacity",1);var a=t.exit().transition().duration(r.aduration).attr("transform",function(t){return"translate("+(n.x+r.node.width)+","+n.y+")"}).remove();a.select("rect").attr("width",1e-6).attr("height",1e-6);a.selectAll("text").style("font-size",1e-6+"px").style("fill-opacity",1e-6)};var g=function(t,e){t.enter().insert("path","g").attr("class","link").attr("d",function(t,n){var r={x:e.x0,y:e.y0};return c({source:r,target:r},n,1)});t.transition().duration(r.aduration).attr("d",c).style("fill","none").style("stroke",r.link.colors.default).style("stroke-width",r.link.width).attr("id",function(t){return"link"+t.target.id});t.exit().transition().duration(r.aduration).attr("d",function(t,n){var i={x:e.x+r.node.width,y:e.y};return c({source:i,target:i},n,1)}).remove()};var p=function(t,e,n){a.selectAll("path.link").filter(function(e){return e.target.id==t.id}).style("stroke-width",n?r.link.width:r.link.focus_width).style("stroke",n?r.link.colors.default:r.link.colors.focus);a.selectAll("g.nodes rect").filter(function(e){return e.id==t.id}).style("stroke-width",r.node.swidth).style("stroke",function(t){return n?t._children?r.node.colors.closed:i(t.info.purity):r.node.colors.focus});if(t.parent)p(t.parent,e,n)};var y=function(t,n,i){var o=i?r.node.width:2*r.node.width,d=i?r.node.height:1.5*r.node.height;a.selectAll("g.nodes rect").filter(function(e){e.bigger=i?false:true;return e.id==t.id}).transition().duration(r.aduration/2).attr("width",o+"px").attr("height",d+"px");a.selectAll("g.nodes text").filter(function(e){return e.id==t.id}).transition().duration(r.aduration/2).style("font-size",function(t){return(i?t.font_size:2*t.font_size)+"px"}).attr("dx",i?r.text.padding:2*Number(r.text.padding.replace("px",""))+"px").attr("dy",function(){return(e.select(this).attr("class")=="label1"?d*.35:d*.75)+"px"});if(t.parent)y(t.parent,n,i)};var x=function(t,e){b();var n=s.nodes(d),i=s.links(n);var l=0;n.forEach(function(t){if(ln)n=r}return[e,n]};var b=function(t){var e=s.nodes(d);var n;for(var a in e){if(!e[a].parent){n=e[a];break}}var l=v(n);r.node.height=o.height/(l+1)-r.node.yspace;if(r.node.height>r.node.mheight)r.node.height=r.node.mheight;var c=0;while(l!=0){if(!(l%4))c++;l/=4}r.node.width=o.width/(w(e)+1-c)-r.node.xspace;if(r.node.width>r.node.mwidth)r.node.height=r.node.mwidth;s.size([o.width,o.height]);i.domain(m(e))};var k=function(t){var n=[{text:"Pure Backg.",id:"label1",color:i(i.domain()[0]),x:5,y:5},{text:"Pure Signal",id:"label2",color:i(i.domain()[1]),x:5,y:40}];var o=t.append("g").attr("transform","translate(5,5)");var a=o.selectAll("g").data(n,function(t){return t.id}).enter().append("g").style("cursor","pointer").attr("transform",function(t){return"translate("+t.x+","+t.y+")"});a.on("mouseover",function(t){e.select("#"+t.id).style("font-weight","bold");e.select("#"+t.id+"_rect").attr("width",r.legend.rect_fucus_width)});a.on("mouseout",function(t){e.select("#"+t.id).style("font-weight","normal");e.select("#"+t.id+"_rect").attr("width",r.legend.rect_width)});a.append("rect").attr("id",function(t){return t.id+"_rect"}).attr("width",r.legend.rect_width).attr("height",r.legend.rect_height).attr("fill",function(t){return t.color});a.append("text").attr("id",function(t){return t.id}).attr("x",function(t){return 5}).attr("y",function(t){return 20}).text(function(t){return t.text}).style("fill",r.text.color)};var _=function(t){if("_children"in t&&t.children==null){t.children=t._children;t._children=null}if("children"in t&&t.children!=null){for(var e in t.children){_(t.children[e])}}};var z=function(t){var e=[];var n=[];n.push(t);while(n.length>0){var r=n.shift();if("_children"in r&&r._children!=null){e.push(r)}if(r.children!=null){for(var i=0;i0?e:t};t.draw=function(t,n){var i=e.select("#"+t);d=n["tree"];l=n["variables"];if(Object.size(d)==0){i.innerHTML="Tree empty...";return}o={width:i.property("style")["width"],height:i.property("style")["height"]};a=i.append("svg").attr("width",o.width).attr("height",o.height);var s=a;Object.keys(o).forEach(function(t){o[t]=Number(o[t].replace("px",""));o[t]-=t=="width"?2*r.margin.x+r.node.width:2*r.margin.y+r.node.height});b(1);var c=e.behavior.zoom().scaleExtent([1,10]).on("zoom",function(){a.attr("transform","translate("+-r.node.width+", "+r.node.height+")translate("+e.event.translate+")scale("+e.event.scale+")")});a=a.on("dblclick",function(){c.scale(1);c.translate([0,0]);a.transition().attr("transform","translate("+-r.node.width+", "+r.node.height+")")}).append("g").call(c).append("g").attr("transform","translate("+-r.node.width+", "+r.node.height+")");k(s);x(d);i.append("button").style("position","relative").style("top","-"+r.buttons.reset.height).style("width",r.buttons.reset.width).style("height",r.buttons.reset.height).style("opacity",r.buttons.reset.alpha).style("background",r.buttons.reset.background).style("background-size","contain").style("background-image","url("+r.buttons.reset.img+")").style("cursor","pointer").style("border","none").on("mouseover",function(){e.select(this).style("opacity","1")}).on("mouseout",function(){e.select(this).style("opacity",r.buttons.reset.alpha)}).on("click",function(){c.scale(1);c.translate([0,0]);a.transition().attr("transform","translate("+-r.node.width+", "+r.node.height+")");var t=z(d);for(var e=0;e"+Number(t.info.Cut).toFixed(3):""}).style("font-size",1e-6+"px").style("fill",r.text.color).style("font-weight","bold")};var f=function(t,n){var o=t.enter().append("g").attr("class","nodes").attr("transform",function(t){return"translate("+n.x0+","+n.y0+")"}).style("cursor","pointer");o.filter(function(t){return t.parent}).on("click",u).on("mouseover",p).on("contextmenu",function(t,n){e.event.preventDefault();y(t)}).on("mouseleave",function(t,e){if(t.bigger)y(t,e,1);return p(t,e,1)});o.append("rect").attr("width",1e-6).attr("height",1e-6);h(o);t.transition().duration(r.aduration).attr("transform",function(t){return"translate("+(t.x+r.node.width*.5)+","+(t.y-r.node.height)+")"});t.select("rect").transition().duration(r.aduration).attr("width",r.node.width).attr("height",r.node.height).attr("fill",function(t){return i(Number(t.info.purity))}).style("stroke-width",r.node.swidth).style("stroke",function(t){return t._children?r.node.colors.closed:""});t.selectAll("text").transition().duration(r.aduration).style("font-size",function(t){var e="S/(S+B)="+Number(t.info.purity).toFixed(3);var n=t.info.IVar!=-1?l[t.info.IVar]+">"+Number(t.info.Cut).toFixed(3):"";t.font_size=1.5*(r.node.width-2*Number(r.node.swidth.replace("px","")))/Math.max(e.length,n.length);return t.font_size+"px"}).attr("dx",r.text.padding).attr("dy",function(t){return(e.select(this).attr("class")=="label1"?r.node.height*.35:r.node.height*.75)+"px"}).style("fill-opacity",1);var a=t.exit().transition().duration(r.aduration).attr("transform",function(t){return"translate("+(n.x+r.node.width)+","+n.y+")"}).remove();a.select("rect").attr("width",1e-6).attr("height",1e-6);a.selectAll("text").style("font-size",1e-6+"px").style("fill-opacity",1e-6)};var g=function(t,e){t.enter().insert("path","g").attr("class","link").attr("d",function(t,n){var r={x:e.x0,y:e.y0};return c({source:r,target:r},n,1)});t.transition().duration(r.aduration).attr("d",c).style("fill","none").style("stroke",r.link.colors.default).style("stroke-width",r.link.width).attr("id",function(t){return"link"+t.target.id});t.exit().transition().duration(r.aduration).attr("d",function(t,n){var i={x:e.x+r.node.width,y:e.y};return c({source:i,target:i},n,1)}).remove()};var p=function(t,e,n){a.selectAll("path.link").filter(function(e){return e.target.id==t.id}).style("stroke-width",n?r.link.width:r.link.focus_width).style("stroke",n?r.link.colors.default:r.link.colors.focus);a.selectAll("g.nodes rect").filter(function(e){return e.id==t.id}).style("stroke-width",r.node.swidth).style("stroke",function(t){return n?t._children?r.node.colors.closed:i(t.info.purity):r.node.colors.focus});if(t.parent)p(t.parent,e,n)};var y=function(t,n,i){var o=i?r.node.width:2*r.node.width,d=i?r.node.height:1.5*r.node.height;console.log("anim height:"+String(d));a.selectAll("g.nodes rect").filter(function(e){e.bigger=i?false:true;return e.id==t.id}).transition().duration(r.aduration/2).attr("width",o+"px").attr("height",d+"px");a.selectAll("g.nodes text").filter(function(e){return e.id==t.id}).transition().duration(r.aduration/2).style("font-size",function(t){return(i?t.font_size:2*t.font_size)+"px"}).attr("dx",i?r.text.padding:2*Number(r.text.padding.replace("px",""))+"px").attr("dy",function(){return(e.select(this).attr("class")=="label1"?d*.35:d*.75)+"px"});if(t.parent)y(t.parent,n,i)};var x=function(t,e){b();var n=s.nodes(d),i=s.links(n);var l=0;n.forEach(function(t){if(ln)n=r}return[e,n]};var b=function(t){var e=s.nodes(d);var n;for(var a in e){if(!e[a].parent){n=e[a];break}}var l=v(n);r.node.height=o.height/(l+1)-r.node.yspace;if(r.node.height>r.node.mheight)r.node.height=r.node.mheight;var c=0;while(l!=0){if(!(l%4))c++;l/=4}r.node.width=o.width/(w(e)+1-c)-r.node.xspace;if(r.node.width>r.node.mwidth)r.node.height=r.node.mwidth;s.size([o.width,o.height]);i.domain(m(e))};var k=function(t){var n=[{text:"Pure Backg.",id:"label1",color:i(i.domain()[0]),x:5,y:5},{text:"Pure Signal",id:"label2",color:i(i.domain()[1]),x:5,y:40}];var o=t.append("g").attr("transform","translate(5,5)");var a=o.selectAll("g").data(n,function(t){return t.id}).enter().append("g").style("cursor","pointer").attr("transform",function(t){return"translate("+t.x+","+t.y+")"});a.on("mouseover",function(t){e.select("#"+t.id).style("font-weight","bold");e.select("#"+t.id+"_rect").attr("width",r.legend.rect_fucus_width)});a.on("mouseout",function(t){e.select("#"+t.id).style("font-weight","normal");e.select("#"+t.id+"_rect").attr("width",r.legend.rect_width)});a.append("rect").attr("id",function(t){return t.id+"_rect"}).attr("width",r.legend.rect_width).attr("height",r.legend.rect_height).attr("fill",function(t){return t.color});a.append("text").attr("id",function(t){return t.id}).attr("x",function(t){return 5}).attr("y",function(t){return 20}).text(function(t){return t.text}).style("fill",r.text.color)};var _=function(t){if("_children"in t&&t.children==null){t.children=t._children;t._children=null}if("children"in t&&t.children!=null){for(var e in t.children){_(t.children[e])}}};var z=function(t){var e=[];var n=[];n.push(t);while(n.length>0){var r=n.shift();if("_children"in r&&r._children!=null){e.push(r)}if(r.children!=null){for(var i=0;i0?e:t};t.draw=function(t,n){var i=e.select("#"+t);d=n["tree"];l=n["variables"];if(Object.size(d)==0){i.innerHTML="Tree empty...";return}o={width:i.property("style")["width"],height:i.property("style")["height"]};a=i.append("svg").attr("width",o.width).attr("height",o.height);var s=a;Object.keys(o).forEach(function(t){o[t]=Number(o[t].replace("px",""));o[t]-=t=="width"?2*r.margin.x+r.node.width:2*r.margin.y+r.node.height});b(1);var c=e.behavior.zoom().scaleExtent([1,10]).on("zoom",function(){a.attr("transform","translate("+-r.node.width+", "+r.node.height+")translate("+e.event.translate+")scale("+e.event.scale+")")});a=a.on("dblclick",function(){c.scale(1);c.translate([0,0]);a.transition().attr("transform","translate("+-r.node.width+", "+r.node.height+")")}).append("g").call(c).append("g").attr("transform","translate("+-r.node.width+", "+r.node.height+")");k(s);x(d);i.append("button").style("position","relative").style("top","-"+r.buttons.reset.height).style("width",r.buttons.reset.width).style("height",r.buttons.reset.height).style("opacity",r.buttons.reset.alpha).style("background",r.buttons.reset.background).style("background-size","contain").style("background-image","url("+r.buttons.reset.img+")").style("cursor","pointer").style("border","none").on("mouseover",function(){e.select(this).style("opacity","1")}).on("mouseout",function(){e.select(this).style("opacity",r.buttons.reset.alpha)}).on("click",function(){c.scale(1);c.translate([0,0]);a.transition().attr("transform","translate("+-r.node.width+", "+r.node.height+")");var t=z(d);for(var e=0;e5?0:5))/4,type:o==i-1?"bias":r==0?"input":"hidden",neuron:o,layer:r};if(r==t-1){s[o]["type"]="output"}}return s};var s=function(e,t,r){var r=e["layout"]["layer_"+t]["neuron_"+r];if(r["nsynapses"]!=0)return r["weights"];return[]};var o=function(e,r){var n=-1e30;var i=1e30;var o;for(var l=0;lo)i=o}}return{min:i,max:n}};var l=function(e,t,r,n,a){var i=s(e,t,r);var o=Array(i.length);for(var l in i){o[l]={layer:t,neuron:r,nextlayer_neuron:l,pos:[n,a[l].position],weight:i[l],type:i[l]<0?"negative":"positive"}}return o};var u=function(e,t){var a=e["variables"];a.push("Bias node");var i=Array(a.length);for(var s in t){i[s]={x:t[s].position.x-r["variables"]["labels_layer0_padding"]*n.width,y:t[s].position.y,text:a[s]+":"}}return i};var p=function(e){e.append("text").text(function(e){return e[1].text}).attr("x",function(e){return e[1].x-this.getComputedTextLength()}).attr("y",function(e){return e[1].y+.25*this.getBBox().height})};var c=function(e,n,a,i,s){if(s!==undefined){var o=t.zip(a,u(n,a))}else{var o=t.zip(a,Array(a.length))}var l=e.append("g").attr("id","layer_"+i).attr("class","layer").selectAll("g").data(o).enter().append("g").attr("id",function(e){return"neuron_"+i+""+e[0].neuron});l.append("circle").attr("r",function(e){return e[0].radius}).attr("cx",function(e){return e[0].position.x}).attr("cy",function(e){return e[0].position.y}).style("fill",function(e){return r["neuron"]["colors"][e[0].type]});if(s!==undefined){p(l)}f(e,l)};var h=t.scale.linear();var y=t.scale.linear();var d=t.svg.line().x(function(e){return e.x}).y(function(e){return e.y}).interpolate("linear");var v=function(e,t,n,a,i){for(var s in n){var o=l(t,a,s,n[s].position,i);e.select("g#neuron_"+a+""+s).selectAll("path").data(o).enter().append("path").moveToBack().attr("d",function(e){return d(e.pos)}).attr("stroke",function(e){return r["synapse"]["colors"][e.type]}).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):y(Math.abs(e.weight))}).attr("stroke-opacity",r["synapse"]["alpha"])}};var f=function(e,n){r.synapse.width_range=Object.assign({},r.synapse.default_width_range);r.synapse.alpha=Object.assign({},r.synapse.default_alpha);n.on("mouseover",function(n){h.range(r["synapse"]["mouseon"]["width_range"]);y.range(r["synapse"]["mouseon"]["width_range"]);var a=t.select(this).moveToFront().transition();a.selectAll("path").style("stroke-opacity",1).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):y(Math.abs(e.weight))});a.selectAll("circle").style("fill-opacity",1).attr("r",function(e){return e[0].radius*r["neuron"]["mouseon"]["change_radius"]});a.selectAll("text").attr("x",function(e){return e[1].x-e[0].radius-this.getComputedTextLength()});var i=e.selectAll("g.layer").selectAll("g").filter(function(e){return!(n[0].neuron==e[0].neuron&&n[0].layer==e[0].layer)}).transition();i.selectAll("circle").filter(function(e){return n[0].layer+1!=e[0].layer}).style("fill-opacity",r["neuron"]["mouseon"]["alpha"]).attr("r",function(e){return e[0].radius});i.selectAll("path").style("stroke-opacity",r["synapse"]["mouseon"]["alpha"])});n.on("mouseout",function(t){h.range(r["synapse"]["width_range"]);y.range(r["synapse"]["width_range"]);var n=e.selectAll("g.layer").selectAll("g").transition();n.selectAll("circle").style("fill-opacity",1).attr("r",function(e){return e[0].radius});n.selectAll("path").style("stroke-opacity",r["synapse"]["alpha"]).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):y(Math.abs(e.weight))});n.selectAll("text").attr("x",function(e){return e[1].x-this.getComputedTextLength()})})};var g=function(e){var a=[{c:r["synapse"]["colors"]["positive"],txt:"Positive weight"},{c:r["synapse"]["colors"]["negative"],txt:"Negative weight"}];var i=r["legend"];var s=e.append("g").attr("id","legend");s.selectAll("g").data(a).enter().append("g").each(function(e,r){var a=t.select(this);a.append("rect").attr("x",n.width-i.pos.x).attr("y",i.pos.y+r*i.dy).attr("width",i.rect.width).attr("height",i.rect.height).style("fill",function(e){return e.c});a.append("text").attr("x",n.width-i.pos.x+i.rect.width+i.padding).attr("y",i.pos.y+r*i.dy+i.rect.height).text(function(e){return e.txt}).style("fill",function(e){return e.c})})};e.draw=function(a,s){if("layers"in s&&"synapses"in s)return e.drawDeepNetwork(a,s);var l,u;var p=t.select("#"+a);n={width:p.property("style")["width"],height:p.property("style")["height"]};u=s;r.synapse.width_range=Object.assign({},r.synapse.default_width_range);r.synapse.alpha=Object.assign({},r.synapse.default_alpha);h.range(r["synapse"]["width_range"]);y.range(r["synapse"]["width_range"]);l=p.append("svg").attr("id","svg_"+a).attr("width",n.width).attr("height",n.height);Object.keys(n).forEach(function(e){n[e]=Number(n[e].replace("px",""))});var d=Number(u["layout"]["nlayers"]);h.domain([0,o(u,d).max]);y.domain([0,Math.abs(o(u,d).min)]);var f=t.behavior.zoom().scaleExtent([1,20]).on("zoom",function(){l.attr("transform","translate("+t.event.translate+")scale("+t.event.scale+")")});l=l.on("dblclick",function(){f.scale(1);f.translate([0,0]);l.transition().attr("transform","translate(0,0)scale(1)")}).append("g").call(f).append("g");var w=Array(d);for(var x=0;x5?0:5))/4,type:s==i-1?"bias":r==0?"input":"hidden",neuron:s,layer:r};if(r==t-1){o[s]["type"]="output"}}return o};var o=function(e,t,r){var r=e["layout"]["layer_"+t]["neuron_"+r];if(r["nsynapses"]!=0)return r["weights"];return[]};var s=function(e,r){var n=-1e30;var i=1e30;var s;for(var l=0;ls)i=s}}return{min:i,max:n}};var l=function(e,t,r,n,a){var i=o(e,t,r);var s=Array(i.length);for(var l in i){s[l]={layer:t,neuron:r,nextlayer_neuron:l,pos:[n,a[l].position],weight:i[l],type:i[l]<0?"negative":"positive"}}return s};var p=function(e,t){var a=e["variables"];a.push("Bias node");var i=Array(a.length);for(var o in t){i[o]={x:t[o].position.x-r["variables"]["labels_layer0_padding"]*n.width,y:t[o].position.y,text:a[o]+":"}}return i};var u=function(e){e.append("text").text(function(e){return e[1].text}).attr("x",function(e){return e[1].x-this.getComputedTextLength()}).attr("y",function(e){return e[1].y+.25*this.getBBox().height})};var c=function(e,n,a,i,o){if(o!==undefined){var s=t.zip(a,p(n,a))}else{var s=t.zip(a,Array(a.length))}var l=e.append("g").attr("id","layer_"+i).attr("class","layer").selectAll("g").data(s).enter().append("g").attr("id",function(e){return"neuron_"+i+""+e[0].neuron});l.append("circle").attr("r",function(e){return e[0].radius}).attr("cx",function(e){return e[0].position.x}).attr("cy",function(e){return e[0].position.y}).style("fill",function(e){return r["neuron"]["colors"][e[0].type]});if(o!==undefined){u(l)}v(e,l)};var h=t.scale.linear();var d=t.scale.linear();var y=t.svg.line().x(function(e){return e.x}).y(function(e){return e.y}).interpolate("linear");var g=function(e,t,n,a,i){for(var o in n){var s=l(t,a,o,n[o].position,i);e.select("g#neuron_"+a+""+o).selectAll("path").data(s).enter().append("path").moveToBack().attr("d",function(e){return y(e.pos)}).attr("stroke",function(e){return r["synapse"]["colors"][e.type]}).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):d(Math.abs(e.weight))}).attr("stroke-opacity",r["synapse"]["alpha"])}};var v=function(e,n){r.synapse.width_range=Object.assign({},r.synapse.default_width_range);r.synapse.alpha=Object.assign({},r.synapse.default_alpha);n.on("mouseover",function(n){h.range(r["synapse"]["mouseon"]["width_range"]);d.range(r["synapse"]["mouseon"]["width_range"]);var a=t.select(this).moveToFront().transition();a.selectAll("path").style("stroke-opacity",1).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):d(Math.abs(e.weight))});a.selectAll("circle").style("fill-opacity",1).attr("r",function(e){return e[0].radius*r["neuron"]["mouseon"]["change_radius"]});a.selectAll("text").attr("x",function(e){return e[1].x-e[0].radius-this.getComputedTextLength()});var i=e.selectAll("g.layer").selectAll("g").filter(function(e){return!(n[0].neuron==e[0].neuron&&n[0].layer==e[0].layer)}).transition();i.selectAll("circle").filter(function(e){return n[0].layer+1!=e[0].layer}).style("fill-opacity",r["neuron"]["mouseon"]["alpha"]).attr("r",function(e){return e[0].radius});i.selectAll("path").style("stroke-opacity",r["synapse"]["mouseon"]["alpha"])});n.on("mouseout",function(t){h.range(r["synapse"]["width_range"]);d.range(r["synapse"]["width_range"]);var n=e.selectAll("g.layer").selectAll("g").transition();n.selectAll("circle").style("fill-opacity",1).attr("r",function(e){return e[0].radius});n.selectAll("path").style("stroke-opacity",1).attr("stroke-width",function(e){return e.type=="positive"?h(e.weight):d(Math.abs(e.weight))});n.selectAll("text").attr("x",function(e){return e[1].x-this.getComputedTextLength()})})};var f=function(e){var a=[{c:r["synapse"]["colors"]["positive"],txt:"Positive weight"},{c:r["synapse"]["colors"]["negative"],txt:"Negative weight"}];var i=r["legend"];var o=e.append("g").attr("id","legend");o.selectAll("g").data(a).enter().append("g").each(function(e,r){var a=t.select(this);a.append("rect").attr("x",n.width-i.pos.x).attr("y",i.pos.y+r*i.dy).attr("width",i.rect.width).attr("height",i.rect.height).style("fill",function(e){return e.c});a.append("text").attr("x",n.width-i.pos.x+i.rect.width+i.padding).attr("y",i.pos.y+r*i.dy+i.rect.height).text(function(e){return e.txt}).style("fill",function(e){return e.c})})};e.draw=function(a,o){if("layers"in o&&"synapses"in o)return e.drawDeepNetwork(a,o);var l,p;var u=t.select("#"+a);n={width:u.property("style")["width"],height:u.property("style")["height"]};p=o;r.synapse.width_range=Object.assign({},r.synapse.default_width_range);r.synapse.alpha=Object.assign({},r.synapse.default_alpha);h.range(r["synapse"]["width_range"]);d.range(r["synapse"]["width_range"]);l=u.append("svg").attr("id","svg_"+a).attr("width",n.width).attr("height",n.height);Object.keys(n).forEach(function(e){n[e]=Number(n[e].replace("px",""))});var y=Number(p["layout"]["nlayers"]);h.domain([0,s(p,y).max]);d.domain([0,Math.abs(s(p,y).min)]);var v=t.behavior.zoom().scaleExtent([1,20]).on("zoom",function(){l.attr("transform","translate("+t.event.translate+")scale("+t.event.scale+")")});l=l.on("dblclick",function(){v.scale(1);v.translate([0,0]);l.transition().attr("transform","translate(0,0)scale(1)")}).append("g").call(v).append("g");var w=Array(y);for(var _=0;_ @@ -87,6 +87,21 @@ def DrawInputVariable(dl, variableName, numBin=100, processTrfs=""): sig = GetInputVariableHist(dl, "Signal", variableName, numBin, processTrfs) bkg = GetInputVariableHist(dl, "Background", variableName, numBin, processTrfs) c, l = JPyInterface.JsDraw.sbPlot(sig, bkg, {"xaxis": sig.GetTitle(), - "yaxis": "N", + "yaxis": "Number of events", "plot": "Input variable: "+sig.GetTitle()}) JPyInterface.JsDraw.Draw(c) + +## Rewrite TMVA::DataLoader::PrepareTrainingAndTestTree +def ChangeCallOriginalPrepareTrainingAndTestTree(*args, **kwargs): + if len(kwargs)==0: + originalFunction, args = JPyInterface.functions.ProcessParameters(0, *args, **kwargs) + return originalFunction(*args) + try: + args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["SigCut", "BkgCut"], *args, **kwargs) + except AttributeError: + try: + args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["Cut"], *args, **kwargs) + except AttributeError: + raise AttributeError + originalFunction, args = JPyInterface.functions.ProcessParameters(3, *args, **kwargs) + return originalFunction(*args) \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/python/JsMVA/Factory.py b/bindings/pyroot/JsMVA/python/JsMVA/Factory.py index 219f58bbd5194..91938d9aaf8a2 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/Factory.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/Factory.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -## @package JsMVA/Factory -# @authors Attila Bagoly +## @package JsMVA.Factory # Factory module with the functions to be inserted to TMVA::Factory class and helper functions and classes +# @authors Attila Bagoly import ROOT @@ -14,7 +14,6 @@ from threading import Thread import time from string import Template -import types ## Getting method object from factory @@ -592,69 +591,19 @@ def exit_supported(mn): t.join() return -## Get's special parameters from kwargs and converts to positional parameter -def __ConvertKwargsToArgs(positionalArgumentsToNamed, *args, **kwargs): - # args[0] = self - args = list(args) - idx = 0 - PositionalArgsEnded = False - for argName in positionalArgumentsToNamed: - if not PositionalArgsEnded: - if argName in kwargs: - if (idx+1)!=len(args): - raise AttributeError - PositionalArgsEnded = True - else: - idx += 1 - if PositionalArgsEnded and argName not in kwargs: - raise AttributeError - if argName in kwargs: - args.append(kwargs[argName]) - del kwargs[argName] - args = tuple(args) - return (args, kwargs) - -## Converts object to TMVA style option string -def __ProcessParameters(optStringStartIndex, *args, **kwargs): - originalFunction = None - if optStringStartIndex!=-10: - originalFunction = kwargs["originalFunction"] - del kwargs["originalFunction"] - OptionStringPassed = False - if (len(args)-1) == optStringStartIndex: - opt = args[optStringStartIndex] + ":" - tmp = list(args) - del tmp[optStringStartIndex] - args = tuple(tmp) - OptionStringPassed = True - else: - opt = "" - for key in kwargs: - if type(kwargs[key]) == types.BooleanType: - if kwargs[key] == True: - opt += key + ":" - else: - opt += "!" + key + ":" - else: - opt += key + "=" + str(kwargs[key]) + ":" - tmp = list(args) - if OptionStringPassed or len(kwargs)>0: - tmp.append( opt[:-1] ) - return ( originalFunction, tuple(tmp) ) - ## Rewrite the constructor of TMVA::Factory def ChangeCallOriginal__init__(*args, **kwargs): try: - args, kwargs = __ConvertKwargsToArgs(["JobName", "TargetFile"], *args, **kwargs) + args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["JobName", "TargetFile"], *args, **kwargs) except AttributeError: try: - args, kwargs = __ConvertKwargsToArgs(["JobName"], *args, **kwargs) + args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["JobName"], *args, **kwargs) except AttributeError: raise AttributeError - originalFunction, args = __ProcessParameters(3, *args, **kwargs) + originalFunction, args = JPyInterface.functions.ProcessParameters(3, *args, **kwargs) return originalFunction(*args) -## Rewrite the constructor of TMVA::Factory +## Rewrite TMVA::Factory::BookMethod def ChangeCallOriginalBookMethod(*args, **kwargs): compositeOpts = False composite = False @@ -664,15 +613,47 @@ def ChangeCallOriginalBookMethod(*args, **kwargs): if "CompositeOptions" in kwargs: compositeOpts = kwargs["CompositeOptions"] del kwargs["CompositeOptions"] - args, kwargs = __ConvertKwargsToArgs(["DataLoader", "Method", "MethodTitle"], *args, **kwargs) - originalFunction, args = __ProcessParameters(4, *args, **kwargs) + args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["DataLoader", "Method", "MethodTitle"], *args, **kwargs) + originalFunction, args = JPyInterface.functions.ProcessParameters(4, *args, **kwargs) if composite!=False: args = list(args) args.append(composite) args = tuple(args) if compositeOpts!=False: - o, compositeOptStr = __ProcessParameters(-10, **compositeOpts) + o, compositeOptStr = JPyInterface.functions.ProcessParameters(-10, **compositeOpts) args = list(args) args.append(compositeOptStr[0]) args = tuple(args) + return originalFunction(*args) + +## Rewrite the constructor of TMVA::Factory::EvaluateImportance +def ChangeCallOriginalEvaluateImportance(*args, **kwargs): + if len(kwargs) == 0: + originalFunction, args = JPyInterface.functions.ProcessParameters(0, *args, **kwargs) + return originalFunction(*args) + args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["DataLoader", "VIType", "Method", "MethodTitle"], *args, **kwargs) + originalFunction, args = JPyInterface.functions.ProcessParameters(5, *args, **kwargs) + return originalFunction(*args) + +## Rewrite the constructor of TMVA::Factory::CrossValidate +def ChangeCallOriginalCrossValidate(*args, **kwargs): + if len(kwargs) == 0: + originalFunction, args = JPyInterface.functions.ProcessParameters(0, *args, **kwargs) + return originalFunction(*args) + optParams = False + rocIntegrals = False + if "optParams" in kwargs: + optParams = kwargs["optParams"] + del kwargs["optParams"] + if "rocIntegrals" in kwargs: + rocIntegrals = kwargs["rocIntegrals"] + del kwargs["rocIntegrals"] + args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["DataLoader", "Method", "MethodTitle"], *args, **kwargs) + originalFunction, args = JPyInterface.functions.ProcessParameters(4, *args, **kwargs) + args = list(args) + if optParams!=False: + args.append(optParams) + if rocIntegrals!=False: + args.append(rocIntegrals) + args = tuple(args) return originalFunction(*args) \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py b/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py index 13de4c14c98b3..17c352bff9fec 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- -## @package JsMVA/JPyInterface -# @authors Attila Bagoly -# This package is responsible for adding the drawing methods to TMVA +## @package JsMVA.JPyInterface +# JPyInterface is responsible for adding the drawing methods to TMVA # and for creating the JavaScript outputs from objects. +# @authors Attila Bagoly from IPython.core.display import display, HTML @@ -10,6 +10,7 @@ import ROOT import DataLoader import Factory +import types ## Function inserter class @@ -52,6 +53,63 @@ def wrapper(*args, **kwargs): else: setattr(target, arg.replace("Change", ""), getattr(source, arg)) + ## Get's special parameters from kwargs and converts to positional parameter + @staticmethod + def ConvertSpecKwargsToArgs(positionalArgumentsToNamed, *args, **kwargs): + # args[0] = self + args = list(args) + idx = 0 + PositionalArgsEnded = False + for argName in positionalArgumentsToNamed: + if not PositionalArgsEnded: + if argName in kwargs: + if (idx + 1) != len(args): + raise AttributeError + PositionalArgsEnded = True + else: + idx += 1 + if PositionalArgsEnded and argName not in kwargs: + raise AttributeError + if argName in kwargs: + args.append(kwargs[argName]) + del kwargs[argName] + args = tuple(args) + return (args, kwargs) + + ## Converts object to TMVA style option string + @staticmethod + def ProcessParameters(optStringStartIndex, *args, **kwargs): + originalFunction = None + if optStringStartIndex != -10: + originalFunction = kwargs["originalFunction"] + del kwargs["originalFunction"] + OptionStringPassed = False + if (len(args) - 1) == optStringStartIndex: + opt = args[optStringStartIndex] + ":" + tmp = list(args) + del tmp[optStringStartIndex] + args = tuple(tmp) + OptionStringPassed = True + else: + opt = "" + for key in kwargs: + if type(kwargs[key]) == types.BooleanType: + if kwargs[key] == True: + opt += key + ":" + else: + opt += "!" + key + ":" + elif type(kwargs[key]) == types.ListType: + ss = "" + for o in kwargs[key]: + ss += str(o) + ";" + opt += key + "=" + ss[:-1] + ":" + else: + opt += key + "=" + str(kwargs[key]) + ":" + tmp = list(args) + if OptionStringPassed or len(kwargs) > 0: + tmp.append(opt[:-1]) + return (originalFunction, tuple(tmp)) + ## The method removes inserted functions from class # @param target from which class to remove functions # @param args list of methods to remove @@ -95,7 +153,8 @@ def unregister(): ## Class for creating the output scripts and inserting them to cell output class JsDraw: ## String containing the link to JavaScript files - __jsMVASourceDir = "https://rawgit.com/qati/GSOC16/master/src/js" + #__jsMVASourceDir = "https://rawgit.com/qati/GSOC16/master/src/js" + __jsMVASourceDir = "http://localhost:8888/notebooks/GSOC/wd/src/js" ## Drawing are sizes jsCanvasWidth = 800 @@ -151,7 +210,7 @@ def Draw(obj, jsDrawMethod='draw', objIsJSON=False): ## Inserts the data inserter JavaScript code to output # @param obj ROOT object (will be converted to JSON) or JSON string containing the data to be inserted - # @param jsDrawMethod the JsMVA JavaScrip object method name to be used for inserting the new data + # @param dataInserterMethod the JsMVA JavaScrip object method name to be used for inserting the new data # @param objIsJSON obj is ROOT object or JSON @staticmethod def InsertData(obj, dataInserterMethod="updateTrainingTestingErrors", objIsJSON=False): @@ -173,7 +232,7 @@ def InsertData(obj, dataInserterMethod="updateTrainingTestingErrors", objIsJSON= @staticmethod def sbPlot(sig, bkg, title): canvas = ROOT.TCanvas("csbplot", title["plot"], JsDraw.jsCanvasWidth, JsDraw.jsCanvasHeight) - sig.SetMaximum(ROOT.TMath.Max(sig.GetMaximum(),bkg.GetMaximum()*1.1)) + sig.SetMaximum(ROOT.TMath.Max(sig.GetMaximum()*1.1,bkg.GetMaximum()*1.1)) sig.SetTitle(sig.GetTitle().replace("(Signal)","")) sig.GetXaxis().SetTitle(title["xaxis"]) sig.GetYaxis().SetTitle(title["yaxis"]) diff --git a/bindings/pyroot/JsMVA/python/JsMVA/JsMVAMagic.py b/bindings/pyroot/JsMVA/python/JsMVA/JsMVAMagic.py index a7262cf0d67e9..264a613f91a22 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/JsMVAMagic.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/JsMVAMagic.py @@ -1,5 +1,6 @@ # -*- coding:utf-8 -*- -## Magic class for JsMVA +## @package JsMVA.JsMVAMagic +# IPython magic class for JsMVA # @authors: Attila Bagoly from IPython.core.magic import Magics, magics_class, line_magic diff --git a/bindings/pyroot/JsMVA/python/JsMVA/__init__.py b/bindings/pyroot/JsMVA/python/JsMVA/__init__.py index 75d52d3d33ba1..3200d7afd0ac9 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/__init__.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/__init__.py @@ -1,3 +1,6 @@ +## @mainpage +# @package JsMVA +# @image html JsMVA.jpg from IPython.core.extensions import ExtensionManager def loadExtensions(): From 557652d73491d447dabdd2406b647869d37e49d4 Mon Sep 17 00:00:00 2001 From: Attila Bagoly Date: Thu, 28 Jul 2016 14:10:48 +0200 Subject: [PATCH 07/14] sync with qati/GSOC16/src --- bindings/pyroot/JsMVA/js/JsMVA.js | 16 ++++++++++---- bindings/pyroot/JsMVA/js/JsMVA.min.js | 2 +- .../pyroot/JsMVA/python/JsMVA/DataLoader.py | 15 ++++++++----- bindings/pyroot/JsMVA/python/JsMVA/Factory.py | 21 ++++++++++++++----- .../pyroot/JsMVA/python/JsMVA/JPyInterface.py | 13 +++++++++--- 5 files changed, 49 insertions(+), 18 deletions(-) diff --git a/bindings/pyroot/JsMVA/js/JsMVA.js b/bindings/pyroot/JsMVA/js/JsMVA.js index c583ac6912733..f62dc36edf7a0 100644 --- a/bindings/pyroot/JsMVA/js/JsMVA.js +++ b/bindings/pyroot/JsMVA/js/JsMVA.js @@ -58,12 +58,12 @@ }); }; - JsMVA.drawTrainingTestingErrors = function(divid, dat_json){ - var obj = JSROOT.parse(dat_json); - JSROOT.draw(divid, obj); + var drawLabel = function(divid, obj){ require(['d3'], function(d3){ + var csvg = d3.select("#"+divid+">.interactivePlot_Labels")[0][0]; + if (csvg!=null) return; var div = d3.select("#"+divid).style("position", "relative"); - var svg = div.append("svg") + var svg = div.append("svg").attr("class", "interactivePlot_Labels") .attr("width", "200px") .attr("height", "50px") .style({"position":"absolute", "top": "8px", "right": "8px"}); @@ -106,9 +106,17 @@ }); }; + + JsMVA.drawTrainingTestingErrors = function(divid, dat_json){ + var obj = JSROOT.parse(dat_json); + JSROOT.draw(divid, obj); + drawLabel(divid, obj); + }; + JsMVA.updateTrainingTestingErrors = function(divid, dat_json){ var obj = JSROOT.parse(dat_json); JSROOT.redraw(divid, obj); + drawLabel(divid, obj); }; return JsMVA; diff --git a/bindings/pyroot/JsMVA/js/JsMVA.min.js b/bindings/pyroot/JsMVA/js/JsMVA.min.js index de0539de97fa1..615517f7957a4 100644 --- a/bindings/pyroot/JsMVA/js/JsMVA.min.js +++ b/bindings/pyroot/JsMVA/js/JsMVA.min.js @@ -1 +1 @@ -(function(t){var r="https://root.cern.ch/js/notebook/scripts/";var e="";if(requirejs.s.contexts.hasOwnProperty("_")){e=requirejs.s.contexts._.config.paths["JsMVA"].replace("JsMVA.min","")}if(console!==undefined&&typeof console.log=="function"){if(e!=""){console.log("JsMVA source dir:"+e.substring(0,e.length-1))}else{console.log("JsMVA source dir can't be resolved, requireJS doesn't have context '_', this will be a problem!")}}require.config({paths:{d3:r+"d3.v3.min",JsRootCore:r+"JSRootCore",nn:e+"NeuralNetwork.min",dtree:e+"DecisionTree.min",IChart:e+"IChart"}});define(["JsRootCore"],function(r){return t({},r)})})(function(t,r){t.drawTH2=function(t,e){var n=r.parse(e);r.draw(t,n,"colz;PAL50;text")};t.draw=function(t,e){var n=r.parse(e);r.draw(t,n)};t.drawNeuralNetwork=function(t,r){var e=JSON.parse(r);require(["nn"],function(r){r.draw(t,e)})};t.drawDecisionTree=function(t,r){require(["dtree"],function(e){var n=JSON.parse(r);e.draw(t,n)})};t.drawTrainingTestingErrors=function(t,e){var n=r.parse(e);r.draw(t,n);require(["d3"],function(e){var o=e.select("#"+t).style("position","relative");var i=o.append("svg").attr("width","200px").attr("height","50px").style({position:"absolute",top:"8px",right:"8px"});var a={pos:{x:150,y:10},rect:{width:10,height:10},dy:20,padding:10};canvas={width:160,height:70};var s=i.append("g").attr("id","legend");s.selectAll("g").data(n.fGraphs.arr).enter().append("g").each(function(t,n){var o=e.select(this);o.append("rect").attr("x",canvas.width-a.pos.x).attr("y",a.pos.y+n*a.dy).attr("width",a.rect.width).attr("height",a.rect.height).style("fill",function(t){return r.Painter.root_colors[t.fFillColor]});o.append("text").attr("x",canvas.width-a.pos.x+a.rect.width+a.padding).attr("y",a.pos.y+n*a.dy+a.rect.height).text(function(t){return t.fTitle}).style("fill",function(t){return r.Painter.root_colors[t.fFillColor]})});o.append("svg").attr("width","55px").attr("height","20px").style({position:"absolute",bottom:"15px",right:"40px"}).append("text").attr("x","5px").attr("y","15px").text(n.fGraphs.arr[0].fTitle.indexOf("Error on training set")!=-1?"Epoch":"#tree").style({"font-size":"16px"})})};t.updateTrainingTestingErrors=function(t,e){var n=r.parse(e);r.redraw(t,n)};return t}); \ No newline at end of file +(function(t){var e="https://root.cern.ch/js/notebook/scripts/";var r="";if(requirejs.s.contexts.hasOwnProperty("_")){r=requirejs.s.contexts._.config.paths["JsMVA"].replace("JsMVA.min","")}if(console!==undefined&&typeof console.log=="function"){if(r!=""){console.log("JsMVA source dir:"+r.substring(0,r.length-1))}else{console.log("JsMVA source dir can't be resolved, requireJS doesn't have context '_', this will be a problem!")}}require.config({paths:{d3:e+"d3.v3.min",JsRootCore:e+"JSRootCore",nn:r+"NeuralNetwork.min",dtree:r+"DecisionTree.min",IChart:r+"IChart"}});define(["JsRootCore"],function(e){return t({},e)})})(function(t,e){t.drawTH2=function(t,r){var n=e.parse(r);e.draw(t,n,"colz;PAL50;text")};t.draw=function(t,r){var n=e.parse(r);e.draw(t,n)};t.drawNeuralNetwork=function(t,e){var r=JSON.parse(e);require(["nn"],function(e){e.draw(t,r)})};t.drawDecisionTree=function(t,e){require(["dtree"],function(r){var n=JSON.parse(e);r.draw(t,n)})};var r=function(t,r){require(["d3"],function(n){var i=n.select("#"+t+">.interactivePlot_Labels")[0][0];if(i!=null)return;var o=n.select("#"+t).style("position","relative");var a=o.append("svg").attr("class","interactivePlot_Labels").attr("width","200px").attr("height","50px").style({position:"absolute",top:"8px",right:"8px"});var s={pos:{x:150,y:10},rect:{width:10,height:10},dy:20,padding:10};canvas={width:160,height:70};var c=a.append("g").attr("id","legend");c.selectAll("g").data(r.fGraphs.arr).enter().append("g").each(function(t,r){var i=n.select(this);i.append("rect").attr("x",canvas.width-s.pos.x).attr("y",s.pos.y+r*s.dy).attr("width",s.rect.width).attr("height",s.rect.height).style("fill",function(t){return e.Painter.root_colors[t.fFillColor]});i.append("text").attr("x",canvas.width-s.pos.x+s.rect.width+s.padding).attr("y",s.pos.y+r*s.dy+s.rect.height).text(function(t){return t.fTitle}).style("fill",function(t){return e.Painter.root_colors[t.fFillColor]})});o.append("svg").attr("width","55px").attr("height","20px").style({position:"absolute",bottom:"15px",right:"40px"}).append("text").attr("x","5px").attr("y","15px").text(r.fGraphs.arr[0].fTitle.indexOf("Error on training set")!=-1?"Epoch":"#tree").style({"font-size":"16px"})})};t.drawTrainingTestingErrors=function(t,n){var i=e.parse(n);e.draw(t,i);r(t,i)};t.updateTrainingTestingErrors=function(t,n){var i=e.parse(n);e.redraw(t,i);r(t,i)};return t}); \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/python/JsMVA/DataLoader.py b/bindings/pyroot/JsMVA/python/JsMVA/DataLoader.py index 096611aae3d17..2e3240f504646 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/DataLoader.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/DataLoader.py @@ -45,7 +45,7 @@ def GetInputVariableHist(dl, className, variableName, numBin, processTrfs=""): transformed = trf.CalcTransformations(inputEvents, 1) else: tmp = trf.CalcTransformations(transformed, 1) - del transformed[:] + del transformed transformed = tmp if transformed!=0: @@ -82,10 +82,15 @@ def DrawCorrelationMatrix(dl, className): # @param dl The object pointer # @param variableName string containing the variable name # @param numBin for creating the histogram -# @param processTrfs string containing the list of transformations to be used on input variable; eg. "I;N;D;P;U;G,D" -def DrawInputVariable(dl, variableName, numBin=100, processTrfs=""): - sig = GetInputVariableHist(dl, "Signal", variableName, numBin, processTrfs) - bkg = GetInputVariableHist(dl, "Background", variableName, numBin, processTrfs) +# @param processTrfs list of transformations to be used on input variable; eg. ["I", "N", "D", "P", "U", "G"]" +def DrawInputVariable(dl, variableName, numBin=100, processTrfs=[]): + processTrfsSTR = "" + if len(processTrfs)>0: + for o in processTrfs: + processTrfsSTR += str(o) + ";" + processTrfsSTR = processTrfsSTR[:-1] + sig = GetInputVariableHist(dl, "Signal", variableName, numBin, processTrfsSTR) + bkg = GetInputVariableHist(dl, "Background", variableName, numBin, processTrfsSTR) c, l = JPyInterface.JsDraw.sbPlot(sig, bkg, {"xaxis": sig.GetTitle(), "yaxis": "Number of events", "plot": "Input variable: "+sig.GetTitle()}) diff --git a/bindings/pyroot/JsMVA/python/JsMVA/Factory.py b/bindings/pyroot/JsMVA/python/JsMVA/Factory.py index 91938d9aaf8a2..b6636d5f93a1c 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/Factory.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/Factory.py @@ -633,7 +633,9 @@ def ChangeCallOriginalEvaluateImportance(*args, **kwargs): return originalFunction(*args) args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["DataLoader", "VIType", "Method", "MethodTitle"], *args, **kwargs) originalFunction, args = JPyInterface.functions.ProcessParameters(5, *args, **kwargs) - return originalFunction(*args) + hist = originalFunction(*args) + JPyInterface.JsDraw.Draw(hist) + return hist ## Rewrite the constructor of TMVA::Factory::CrossValidate def ChangeCallOriginalCrossValidate(*args, **kwargs): @@ -641,19 +643,28 @@ def ChangeCallOriginalCrossValidate(*args, **kwargs): originalFunction, args = JPyInterface.functions.ProcessParameters(0, *args, **kwargs) return originalFunction(*args) optParams = False - rocIntegrals = False + NumFolds = 5 + remakeDataSet = True + rocIntegrals = None if "optParams" in kwargs: optParams = kwargs["optParams"] del kwargs["optParams"] + if "NumFolds" in kwargs: + NumFolds = kwargs["NumFolds"] + del kwargs["NumFolds"] + if "remakeDataSet" in kwargs: + remakeDataSet = kwargs["remakeDataSet"] + del kwargs["remakeDataSet"] if "rocIntegrals" in kwargs: rocIntegrals = kwargs["rocIntegrals"] del kwargs["rocIntegrals"] args, kwargs = JPyInterface.functions.ConvertSpecKwargsToArgs(["DataLoader", "Method", "MethodTitle"], *args, **kwargs) originalFunction, args = JPyInterface.functions.ProcessParameters(4, *args, **kwargs) args = list(args) - if optParams!=False: - args.append(optParams) - if rocIntegrals!=False: + args.append(optParams) + args.append(NumFolds) + args.append(remakeDataSet) + if rocIntegrals!=None and rocIntegrals!=0: args.append(rocIntegrals) args = tuple(args) return originalFunction(*args) \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py b/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py index 17c352bff9fec..f2e23f01ecf09 100644 --- a/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py +++ b/bindings/pyroot/JsMVA/python/JsMVA/JPyInterface.py @@ -101,7 +101,15 @@ def ProcessParameters(optStringStartIndex, *args, **kwargs): elif type(kwargs[key]) == types.ListType: ss = "" for o in kwargs[key]: - ss += str(o) + ";" + if type(o) == types.DictType: + sst = "" + for kk in o: + sst += kk + "=" + str(o[kk]) + "," + ss += sst[:-1] + "|" + elif key=="Layout": + ss += str(o) + "," + else: + ss += str(o) + ";" opt += key + "=" + ss[:-1] + ":" else: opt += key + "=" + str(kwargs[key]) + ":" @@ -153,8 +161,7 @@ def unregister(): ## Class for creating the output scripts and inserting them to cell output class JsDraw: ## String containing the link to JavaScript files - #__jsMVASourceDir = "https://rawgit.com/qati/GSOC16/master/src/js" - __jsMVASourceDir = "http://localhost:8888/notebooks/GSOC/wd/src/js" + __jsMVASourceDir = "https://rawgit.com/qati/GSOC16/master/src/js" ## Drawing are sizes jsCanvasWidth = 800 From 5f71cd826af417c09f075fbe6d0ad8dc8ebaabbf Mon Sep 17 00:00:00 2001 From: Attila Bagoly Date: Tue, 13 Sep 2016 23:57:47 +0200 Subject: [PATCH 08/14] Sync with qati/GSOC16 repo: DNN designer, output transformations, DNN weights heat map --- bindings/pyroot/JsMVA/css/NetworkDesigner.css | 167 ++++ .../pyroot/JsMVA/css/NetworkDesigner.min.css | 1 + bindings/pyroot/JsMVA/css/TMVAHTMLOutput.css | 120 +++ .../pyroot/JsMVA/css/TMVAHTMLOutput.min.css | 1 + bindings/pyroot/JsMVA/js/JsMVA.js | 40 +- bindings/pyroot/JsMVA/js/JsMVA.min.js | 2 +- bindings/pyroot/JsMVA/js/NetworkDesigner.js | 754 ++++++++++++++++++ .../pyroot/JsMVA/js/NetworkDesigner.min.js | 1 + .../pyroot/JsMVA/python/JsMVA/DataLoader.py | 36 +- bindings/pyroot/JsMVA/python/JsMVA/Factory.py | 103 ++- .../pyroot/JsMVA/python/JsMVA/JPyInterface.py | 75 +- .../JsMVA/python/JsMVA/OutputTransformer.py | 344 ++++++++ 12 files changed, 1626 insertions(+), 18 deletions(-) create mode 100644 bindings/pyroot/JsMVA/css/NetworkDesigner.css create mode 100644 bindings/pyroot/JsMVA/css/NetworkDesigner.min.css create mode 100644 bindings/pyroot/JsMVA/css/TMVAHTMLOutput.css create mode 100644 bindings/pyroot/JsMVA/css/TMVAHTMLOutput.min.css create mode 100644 bindings/pyroot/JsMVA/js/NetworkDesigner.js create mode 100644 bindings/pyroot/JsMVA/js/NetworkDesigner.min.js create mode 100644 bindings/pyroot/JsMVA/python/JsMVA/OutputTransformer.py diff --git a/bindings/pyroot/JsMVA/css/NetworkDesigner.css b/bindings/pyroot/JsMVA/css/NetworkDesigner.css new file mode 100644 index 0000000000000..d9c0dd846a79d --- /dev/null +++ b/bindings/pyroot/JsMVA/css/NetworkDesigner.css @@ -0,0 +1,167 @@ +/** + * Created by Attila Bagoly on 7/9/16. + */ + + +#nd_menu_div { + position: relative; + margin: 0; +} + +#nd_menu_div ul#nd_menu { + list-style-type: none; + margin: 0; + padding: 0; + overflow: hidden; + background-color: #e7e7e7; + position: clear; + cursor: pointer; +} + +#nd_menu_div ul#nd_menu ul { + list-style-type: none; + margin: 0; + padding: 0; +} + +.nd_menu_element, .nd_menu_dropdown { + float: left; +} + +.nd_menu_element div, .nd_menu_dropdown div { + display: inline-block; + color: #333333;//#777; + text-align: center; + padding: 6px 16px; + text-decoration: none; +} + +.nd_menu_element:hover, .nd_menu_dropdown:hover { + background-color: #337ab7; +} + +.nd_menu_dropdown { + display: inline-block; +} +.nd_menu_dropdown_content { + display: none; + position: absolute; + background-color: #f1f1f1; + //min-width: 100px; + box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); + z-index: 99; + overflow: hidden; +} + +.nd_menu_dropdown_content li div { + text-align: left; + width: 100%; +} + +.nd_menu_dropdown_content li div:hover { + background-color: #fafafa; +} + +.nd_menu_dropdown:hover .nd_menu_dropdown_content { + display: block; +} + +.connection { + border:3px solid #051; + opacity: 0.5; + z-index:1; + border-radius:100%; + pointer-events:none; +} + +#drawConnectionHelper { + position: absolute; + text-indent: -9999px; + width: 0px; + height: 0px; + border-top: 16px solid transparent; + border-bottom: 16px solid transparent; + border-left: 16px solid #051; + z-index: 95; +} + +.layer_box { + border: 4px solid #051; + -webkit-border-radius: 20%; + -moz-border-radius: 20%; + border-radius: 20%; + width: 140px; + height: 70px; + cursor: pointer; +} + +.layer_box span { + position: absolute; + width:100%; + bottom: 0px; + text-align:center; + font-size: 22px; + font-weight: bold; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.layer_box input.button_layer{ + position: absolute; + top: 5px; + left: 23%; + font-weight: bold; + font-size: 16px; +} + +.layer_box input.button_layer_output { + position: absolute; + bottom: 3px; + left: 23%; + font-weight: bold; + font-size: 16px; +} + +.layer_box center { + font-size: 20px; + font-weight: bold; + padding: 5px; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +#ts_link { + margin-top: 10px; +} + +#neuronnum_layer_dialog input:not(.ui-button) { + width: 100px; +} + +#neuronnum_layer_dialog label { + padding-right: 5px; +} + +#trainingstrategy_dialog tr td:first-child { + float: right; +} + +#globopts_dialog tr td:first-child { + float: right; +} + +#trainingstrategy_dialog input { + width: 150px; +} + +#trainingstrategy_dialog label { + padding-right: 10px; +} \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/css/NetworkDesigner.min.css b/bindings/pyroot/JsMVA/css/NetworkDesigner.min.css new file mode 100644 index 0000000000000..d911c05faabeb --- /dev/null +++ b/bindings/pyroot/JsMVA/css/NetworkDesigner.min.css @@ -0,0 +1 @@ +#nd_menu_div{position:relative;margin:0}#nd_menu_div ul#nd_menu{list-style-type:none;margin:0;padding:0;overflow:hidden;background-color:#e7e7e7;position:clear;cursor:pointer}#nd_menu_div ul#nd_menu ul{list-style-type:none;margin:0;padding:0}.nd_menu_element,.nd_menu_dropdown{float:left}.nd_menu_element div,.nd_menu_dropdown div{display:inline-block;color:#333;//#777;text-align:center;padding:6px 16px;text-decoration:none}.nd_menu_element:hover,.nd_menu_dropdown:hover{background-color:#337ab7}.nd_menu_dropdown{display:inline-block}.nd_menu_dropdown_content{display:none;position:absolute;background-color:#f1f1f1;//min-width:100px;box-shadow:0 8px 16px 0 rgba(0,0,0,0.2);z-index:99;overflow:hidden}.nd_menu_dropdown_content li div{text-align:left;width:100%}.nd_menu_dropdown_content li div:hover{background-color:#fafafa}.nd_menu_dropdown:hover .nd_menu_dropdown_content{display:block}.connection{border:3px solid #051;opacity:.5;z-index:1;border-radius:100%;pointer-events:none}#drawConnectionHelper{position:absolute;text-indent:-9999px;width:0;height:0;border-top:16px solid transparent;border-bottom:16px solid transparent;border-left:16px solid #051;z-index:95}.layer_box{border:4px solid #051;-webkit-border-radius:20%;-moz-border-radius:20%;border-radius:20%;width:140px;height:70px;cursor:pointer}.layer_box span{position:absolute;width:100%;bottom:0;text-align:center;font-size:22px;font-weight:bold;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.layer_box input.button_layer{position:absolute;top:5px;left:23%;font-weight:bold;font-size:16px}.layer_box input.button_layer_output{position:absolute;bottom:3px;left:23%;font-weight:bold;font-size:16px}.layer_box center{font-size:20px;font-weight:bold;padding:5px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}#ts_link{margin-top:10px}#neuronnum_layer_dialog input:not(.ui-button){width:100px}#neuronnum_layer_dialog label{padding-right:5px}#trainingstrategy_dialog tr td:first-child{float:right}#globopts_dialog tr td:first-child{float:right}#trainingstrategy_dialog input{width:150px}#trainingstrategy_dialog label{padding-right:10px} \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/css/TMVAHTMLOutput.css b/bindings/pyroot/JsMVA/css/TMVAHTMLOutput.css new file mode 100644 index 0000000000000..e2a67d17c0c59 --- /dev/null +++ b/bindings/pyroot/JsMVA/css/TMVAHTMLOutput.css @@ -0,0 +1,120 @@ +.tmva_output_table { + width: 100%; + border-collapse: collapse; + border: 1px solid white !important; +} + +.tmva_output_table td { + border-top: 1px solid white; + border-left: 1px solid white; + border-right: 1px solid white; + border-bottom: 1px solid #595959; + word-break: break-all; +} + +.tmva_output_table tbody:last-child tr:first-child td:first-child, .tmva_output_table tbody:last-child tr:last-child td { + border-bottom: 1px solid white; +} + +.tmva_output_table tbody tr:first-child td:first-child { + border-right: 1px solid #595959; +} + +.tmva_output_table tbody.tmva_output_tbody_multiple_row tr:not(:last-child) td{ + border-bottom: 1px solid #a6a6a6; +} + +.tmva_output_table tbody:not(:last-child).tmva_output_tbody_multiple_row tr:first-child td:first-child { + border-bottom: 1px solid #595959; +} + +.tmva_output_table tbody:hover { + background-color:#f5f5f5; +} + +.tmva_output_table tbody:hover tr:hover td:last-child { + background-color: rgba(0 ,179, 0, 0.15); +} + +.tmva_output_table tr td.tmva_output_header { + vertical-align: top; + width: 160px !important; + font-family: Arial; + font-size: 18px; + font-weight: bold; +} + +.tmva_output_dataset { + border-collapse: collapse; +} + +.tmva_output_dataset tr:first-child td:first-child { + font-size: 16px; + font-weight: bold; + color: rgb(0, 0, 179); +} + +.tmva_output_dataset td { + border: 1px solid #ddd !important; + vertical-align: top; +} + +.tmva_output_dataset tbody:hover { + //background-color: rgba(0, 150, 179, 0.4); + background-color: rgba(0, 179, 0, 0.4); +} + +.tmva_output_dataset tbody:hover tr:hover td { + background-color: rgba(0, 0, 179, 0.1); +} + +.tmva_output_traintestevents td { + border-top: 1px solid white !important; + border-left: 1px solid white !important; + border-right: 1px solid white !important; + border-bottom: 1px solid #ddd !important; +} + +.tmva_output_traintestevents tbody:hover { + background-color: rgba(0, 179, 0, 0.4); +} + +.tmva_output_varmeanrms td { + border: 1px solid #000066 !important; +} + +.tmva_output_varmeanrms tbody:hover tr { + background-color: rgba(0, 179, 0, 0.4); +} + +.tmva_output_hidden_td { + display: none; +} + +.tmva_output_corrmat_link { + cursor: pointer; +} + +.tmva_output_warning { + background-color: rgba(179, 0, 0, 0.2); +} + +.tmva_output_error { + background-color: rgba(179, 0, 0, 0.5); +} + +.tmva_output_fatal { + background-color: rgba(179, 0, 0, 0.9); +} + +.tmva_output_silent { + background-color: rgba(0, 179, 0, 0.2); +} + +.tmva_output_debug { + background-color: rgba(0, 0, 179, 0.2); +} + +.tmva_output_imphead { + background-color: rgba(0, 179, 0, 0.3); +} diff --git a/bindings/pyroot/JsMVA/css/TMVAHTMLOutput.min.css b/bindings/pyroot/JsMVA/css/TMVAHTMLOutput.min.css new file mode 100644 index 0000000000000..99c058f4d027b --- /dev/null +++ b/bindings/pyroot/JsMVA/css/TMVAHTMLOutput.min.css @@ -0,0 +1 @@ +.tmva_output_table{width:100%;border-collapse:collapse;border:1px solid white !important}.tmva_output_table td{border-top:1px solid white;border-left:1px solid white;border-right:1px solid white;border-bottom:1px solid #595959;word-break:break-all}.tmva_output_table tbody:last-child tr:first-child td:first-child,.tmva_output_table tbody:last-child tr:last-child td{border-bottom:1px solid white}.tmva_output_table tbody tr:first-child td:first-child{border-right:1px solid #595959}.tmva_output_table tbody.tmva_output_tbody_multiple_row tr:not(:last-child) td{border-bottom:1px solid #a6a6a6}.tmva_output_table tbody:not(:last-child).tmva_output_tbody_multiple_row tr:first-child td:first-child{border-bottom:1px solid #595959}.tmva_output_table tbody:hover{background-color:#f5f5f5}.tmva_output_table tbody:hover tr:hover td:last-child{background-color:rgba(0,179,0,0.15)}.tmva_output_table tr td.tmva_output_header{vertical-align:top;width:160px !important;font-family:Arial;font-size:18px;font-weight:bold}.tmva_output_dataset{border-collapse:collapse}.tmva_output_dataset tr:first-child td:first-child{font-size:16px;font-weight:bold;color:#0000b3}.tmva_output_dataset td{border:1px solid #ddd !important;vertical-align:top}.tmva_output_dataset tbody:hover{//background-color:rgba(0,150,179,0.4);background-color:rgba(0,179,0,0.4)}.tmva_output_dataset tbody:hover tr:hover td{background-color:rgba(0,0,179,0.1)}.tmva_output_traintestevents td{border-top:1px solid white !important;border-left:1px solid white !important;border-right:1px solid white !important;border-bottom:1px solid #ddd !important}.tmva_output_traintestevents tbody:hover{background-color:rgba(0,179,0,0.4)}.tmva_output_varmeanrms td{border:1px solid #006 !important}.tmva_output_varmeanrms tbody:hover tr{background-color:rgba(0,179,0,0.4)}.tmva_output_hidden_td{display:none}.tmva_output_corrmat_link{cursor:pointer}.tmva_output_warning{background-color:rgba(179,0,0,0.2)}.tmva_output_error{background-color:rgba(179,0,0,0.5)}.tmva_output_fatal{background-color:rgba(179,0,0,0.9)}.tmva_output_silent{background-color:rgba(0,179,0,0.2)}.tmva_output_debug{background-color:rgba(0,0,179,0.2)}.tmva_output_imphead{background-color:rgba(0,179,0,0.3)} \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/js/JsMVA.js b/bindings/pyroot/JsMVA/js/JsMVA.js index f62dc36edf7a0..1d0ec477f8861 100644 --- a/bindings/pyroot/JsMVA/js/JsMVA.js +++ b/bindings/pyroot/JsMVA/js/JsMVA.js @@ -21,10 +21,10 @@ require.config({ paths: { 'd3': JSROOT_source_dir+'d3.v3.min', - 'JsRootCore': JSROOT_source_dir+'JSRootCore', + 'JsRootCore': JSROOT_source_dir+'JSRootCore.min', 'nn': url+'NeuralNetwork.min', 'dtree': url+'DecisionTree.min', - 'IChart': url+'IChart' + 'NetworkDesigner': url+'NetworkDesigner.min' } }); @@ -39,6 +39,11 @@ JSROOT.draw(divid, obj, "colz;PAL50;text"); }; + JsMVA.drawDNNMap = function(divid, dat_json){ + var obj = JSROOT.parse(dat_json); + JSROOT.draw(divid, obj, "colz;PAL50"); + }; + JsMVA.draw = function(divid, dat_json){ var obj = JSROOT.parse(dat_json); JSROOT.draw(divid, obj); @@ -68,7 +73,7 @@ .attr("height", "50px") .style({"position":"absolute", "top": "8px", "right": "8px"}); var attr = { - "pos": {"x": 150, "y": 10}, + "pos": {"x": 150, "y": 0}, "rect": {"width": 10, "height":10}, "dy": 20, "padding": 10 @@ -119,5 +124,34 @@ drawLabel(divid, obj); }; + JsMVA.NetworkDesigner = function(divid, dat_json){ + require(['NetworkDesigner'], function (nd) { + nd.draw(divid); + }); + }; + + JsMVA.outputShowCorrelationMatrix = function(divid){ + require(['jquery', 'jquery-ui'], function($){ + var th2 = JSROOT.parse($("#"+divid).html()); + if (!$("#dialog_"+divid).length || $("#dialog_"+divid).length < 1) { + $("#" + divid).parent().append("
"); + JSROOT.draw("dialog_" + divid, th2, "colz;PAL50;text"); + } + $("#dialog_" + divid).dialog({ + autoOpen: true, + width: 600, + show: { + effect: "blind", + duration: 1000 + }, + hide: { + effect: "explode", + duration: 500 + } + }); + + }); + }; + return JsMVA; })); diff --git a/bindings/pyroot/JsMVA/js/JsMVA.min.js b/bindings/pyroot/JsMVA/js/JsMVA.min.js index 615517f7957a4..e1bdebef015b0 100644 --- a/bindings/pyroot/JsMVA/js/JsMVA.min.js +++ b/bindings/pyroot/JsMVA/js/JsMVA.min.js @@ -1 +1 @@ -(function(t){var e="https://root.cern.ch/js/notebook/scripts/";var r="";if(requirejs.s.contexts.hasOwnProperty("_")){r=requirejs.s.contexts._.config.paths["JsMVA"].replace("JsMVA.min","")}if(console!==undefined&&typeof console.log=="function"){if(r!=""){console.log("JsMVA source dir:"+r.substring(0,r.length-1))}else{console.log("JsMVA source dir can't be resolved, requireJS doesn't have context '_', this will be a problem!")}}require.config({paths:{d3:e+"d3.v3.min",JsRootCore:e+"JSRootCore",nn:r+"NeuralNetwork.min",dtree:r+"DecisionTree.min",IChart:r+"IChart"}});define(["JsRootCore"],function(e){return t({},e)})})(function(t,e){t.drawTH2=function(t,r){var n=e.parse(r);e.draw(t,n,"colz;PAL50;text")};t.draw=function(t,r){var n=e.parse(r);e.draw(t,n)};t.drawNeuralNetwork=function(t,e){var r=JSON.parse(e);require(["nn"],function(e){e.draw(t,r)})};t.drawDecisionTree=function(t,e){require(["dtree"],function(r){var n=JSON.parse(e);r.draw(t,n)})};var r=function(t,r){require(["d3"],function(n){var i=n.select("#"+t+">.interactivePlot_Labels")[0][0];if(i!=null)return;var o=n.select("#"+t).style("position","relative");var a=o.append("svg").attr("class","interactivePlot_Labels").attr("width","200px").attr("height","50px").style({position:"absolute",top:"8px",right:"8px"});var s={pos:{x:150,y:10},rect:{width:10,height:10},dy:20,padding:10};canvas={width:160,height:70};var c=a.append("g").attr("id","legend");c.selectAll("g").data(r.fGraphs.arr).enter().append("g").each(function(t,r){var i=n.select(this);i.append("rect").attr("x",canvas.width-s.pos.x).attr("y",s.pos.y+r*s.dy).attr("width",s.rect.width).attr("height",s.rect.height).style("fill",function(t){return e.Painter.root_colors[t.fFillColor]});i.append("text").attr("x",canvas.width-s.pos.x+s.rect.width+s.padding).attr("y",s.pos.y+r*s.dy+s.rect.height).text(function(t){return t.fTitle}).style("fill",function(t){return e.Painter.root_colors[t.fFillColor]})});o.append("svg").attr("width","55px").attr("height","20px").style({position:"absolute",bottom:"15px",right:"40px"}).append("text").attr("x","5px").attr("y","15px").text(r.fGraphs.arr[0].fTitle.indexOf("Error on training set")!=-1?"Epoch":"#tree").style({"font-size":"16px"})})};t.drawTrainingTestingErrors=function(t,n){var i=e.parse(n);e.draw(t,i);r(t,i)};t.updateTrainingTestingErrors=function(t,n){var i=e.parse(n);e.redraw(t,i);r(t,i)};return t}); \ No newline at end of file +(function(t){var e="https://root.cern.ch/js/notebook/scripts/";var r="";if(requirejs.s.contexts.hasOwnProperty("_")){r=requirejs.s.contexts._.config.paths["JsMVA"].replace("JsMVA.min","")}if(console!==undefined&&typeof console.log=="function"){if(r!=""){console.log("JsMVA source dir:"+r.substring(0,r.length-1))}else{console.log("JsMVA source dir can't be resolved, requireJS doesn't have context '_', this will be a problem!")}}require.config({paths:{d3:e+"d3.v3.min",JsRootCore:e+"JSRootCore.min",nn:r+"NeuralNetwork.min",dtree:r+"DecisionTree.min",NetworkDesigner:r+"NetworkDesigner.min"}});define(["JsRootCore"],function(e){return t({},e)})})(function(t,e){t.drawTH2=function(t,r){var i=e.parse(r);e.draw(t,i,"colz;PAL50;text")};t.drawDNNMap=function(t,r){var i=e.parse(r);e.draw(t,i,"colz;PAL50")};t.draw=function(t,r){var i=e.parse(r);e.draw(t,i)};t.drawNeuralNetwork=function(t,e){var r=JSON.parse(e);require(["nn"],function(e){e.draw(t,r)})};t.drawDecisionTree=function(t,e){require(["dtree"],function(r){var i=JSON.parse(e);r.draw(t,i)})};var r=function(t,r){require(["d3"],function(i){var n=i.select("#"+t+">.interactivePlot_Labels")[0][0];if(n!=null)return;var o=i.select("#"+t).style("position","relative");var a=o.append("svg").attr("class","interactivePlot_Labels").attr("width","200px").attr("height","50px").style({position:"absolute",top:"8px",right:"8px"});var s={pos:{x:150,y:0},rect:{width:10,height:10},dy:20,padding:10};canvas={width:160,height:70};var d=a.append("g").attr("id","legend");d.selectAll("g").data(r.fGraphs.arr).enter().append("g").each(function(t,r){var n=i.select(this);n.append("rect").attr("x",canvas.width-s.pos.x).attr("y",s.pos.y+r*s.dy).attr("width",s.rect.width).attr("height",s.rect.height).style("fill",function(t){return e.Painter.root_colors[t.fFillColor]});n.append("text").attr("x",canvas.width-s.pos.x+s.rect.width+s.padding).attr("y",s.pos.y+r*s.dy+s.rect.height).text(function(t){return t.fTitle}).style("fill",function(t){return e.Painter.root_colors[t.fFillColor]})});o.append("svg").attr("width","55px").attr("height","20px").style({position:"absolute",bottom:"15px",right:"40px"}).append("text").attr("x","5px").attr("y","15px").text(r.fGraphs.arr[0].fTitle.indexOf("Error on training set")!=-1?"Epoch":"#tree").style({"font-size":"16px"})})};t.drawTrainingTestingErrors=function(t,i){var n=e.parse(i);e.draw(t,n);r(t,n)};t.updateTrainingTestingErrors=function(t,i){var n=e.parse(i);e.redraw(t,n);r(t,n)};t.NetworkDesigner=function(t,e){require(["NetworkDesigner"],function(e){e.draw(t)})};t.outputShowCorrelationMatrix=function(t){require(["jquery","jquery-ui"],function(r){var i=e.parse(r("#"+t).html());if(!r("#dialog_"+t).length||r("#dialog_"+t).length<1){r("#"+t).parent().append("
");e.draw("dialog_"+t,i,"colz;PAL50;text")}r("#dialog_"+t).dialog({autoOpen:true,width:600,show:{effect:"blind",duration:1e3},hide:{effect:"explode",duration:500}})})};return t}); \ No newline at end of file diff --git a/bindings/pyroot/JsMVA/js/NetworkDesigner.js b/bindings/pyroot/JsMVA/js/NetworkDesigner.js new file mode 100644 index 0000000000000..1b7a7830b9a2e --- /dev/null +++ b/bindings/pyroot/JsMVA/js/NetworkDesigner.js @@ -0,0 +1,754 @@ +/** + * Created by qati on 8/3/2016. + */ + +(function (factory) { + var baseURL = require.toUrl("./NetworkDesigner.js"); + baseURL = baseURL.substr(0, baseURL.lastIndexOf("/")+1); + require.config({ + paths: { + "jquery-connections": baseURL + "jquery.connections.min", + "jquery-timing": baseURL + "jquery-timing.min", + "d3": "https://root.cern.ch/js/notebook/scripts/d3.v3.min" + }, + shim: { + "jquery-ui": { + exports: "jquery.ui", + deps: ['jquery'] + }, + "jquery-connections":{ + deps: ['jquery'] + }, + "jquery-timing": { + deps: ['jquery'] + } + } + }); + define(['jquery', 'd3', 'jquery-ui', 'jquery-connections', 'jquery-timing'], function (jQ, d3) { + return factory({}, jQ, d3); + }) +})(function (NetworkDesigner, $, d3) { + + var containerID; + + var globalOptions = { + H: false, + V: false, + VerbosityLevel: "Default", + VarTransform: "Normalize", + ErrorStrategy: "CROSSENTROPY", + WeightInitialization: "XAVIER", + CreateMVAPdfs: false, + IgnoreNegWeightsInTraining: false, + SignalWeightsSum: 1000.0, + BackgroundWeightsSum: 1000.0 + }; + + var layers = Array(); + var layersID; + var layer_ids = { + ReLU: "layer_relu", + TANH: "layer_tanh", + SYMMReLU: "layer_symmrelu", + SOFTSIGN: "layer_SOFTSIGN", + Sigmoid: "layer_sigmoid", + LINEAR: "layer_linear", + GAUSS: "layer_gauss" + }; + + var connection_queue = []; + + var helpMessages = []; + + var colors = { + layer: { + input: "#00A000", + output: "#F6BD00", + hidden: ["steelblue", "red"] + } + }; + + var layer_color = d3.scale.linear() + .domain([0, 100]).range(colors.layer.hidden) + .interpolate(d3.interpolateRgb); + + + var getText = function(key){ + for(var k in layer_ids){ + if (key==layer_ids[k]) return k; + } + }; + + var drawConnection = function(e, id){ + if ($("#drawConnectionHelper")[0]!==undefined){ + $("#drawConnectionHelper").remove(); + } + connection_queue = []; + $("#"+containerID).append("
div_"+id+"
"); + var helper = $("#drawConnectionHelper"); + var offset = $("#"+containerID).offset(); + helper.css({ + top: (e.pageY -offset.top) + "px", + left: (e.pageX -offset.left)+ "px" + }); + helper.draggable(); + var counter = 0; + helper.on("click", function () { + if (counter==2) { + connection_queue.push($(this).text()); + $(this).remove(); + counter = 0; + } else { + counter++; + } + }); + $("#div_"+id).connections({to: "#drawConnectionHelper"}); + }; + + var initLayer = function(i, type){ + layers[ i ] = { + type: type, + neurons: 0, + connected: { + before: null, + after: null + }, + trainingStrategy: { + LearningRate: 1e-5, + Momentum: 0.3, + Repetitions: 3, + ConvergenceSteps: 100, + BatchSize: 30, + TestRepetitions: 7, + WeightDecay: 0.0, + Regularization: "NONE", + DropConfig: "", + DropRepetitions: 3, + Multithreading: true + } + }; + if (type.toLowerCase().indexOf("output")!=-1){ + layers[i]["func"] + } + }; + + var connectLayers = function(target){ + var source; + if (connection_queue.length!=1) { + var helper = $("#drawConnectionHelper"); + source = String(helper.text()); + helper.remove(); + } else { + source = String(connection_queue.pop()); + } + $("#"+source).connections({to: "#"+target}); + updateLayer(source, false, false, target); + }; + + var updateLayer = function (id, neuron_num, connected_before, connected_after) { + var arr = id.split("_"); + var idx = Number(arr[arr.length-1]); + if (neuron_num) layers[idx].neurons = neuron_num; + if (connected_before){ + arr = connected_before.split("_"); + var other_idx = Number(arr[arr.length-1]); + layers[idx].connected.before = other_idx; + layers[other_idx].connected.after = idx; + } + if (connected_after){ + arr = connected_after.split("_"); + var other_idx = Number(arr[arr.length-1]); + layers[other_idx].connected.before = idx; + layers[idx].connected.after = other_idx; + } + }; + + var addLayer = function(id, addButton, connectionSource, connectionTarget, addText){ + var lid = id + "_" + layersID++; + $("#"+containerID).append("
"); + var layer = $("#div_"+lid); + var bkg_color = id.indexOf("input")!=-1 ? colors.layer.input + : id.indexOf("output")!=-1 ? colors.layer.output + : colors.layer.hidden[0]; + layer.css({ + position: "relative", + top: "100px", + left: "100px", + "z-index": 90, + "background-color": bkg_color + }); + layer.draggable(); + if (addButton) { + layer.html("" + + "" + + "" + getText(id) + ""); + layer.on("click", "#options_" + lid, function () { + $("#neuronnum_layer_dialog").data('buttonID', lid); + $("#neuronnum_layer_dialog").dialog("open"); + }); + } + if (connectionSource){ + var counter = 0; + layer.on("click",function(e){ + if (counter>=2) { + drawConnection(e, lid); + counter = 0; + } else { + counter += 1; + } + }); + } + if (connectionTarget) { + layer.droppable({ + drop: function () { + connectLayers("div_" + lid); + } + }); + } + if (addText){ + layer.append(addText); + if (addText.toLowerCase().indexOf("output")!=-1){ + layer.append(""); + layer.on("click", "#options_" + lid, function () { + $("#output_layer_dialog").data('buttonID', lid); + $("#output_layer_dialog").dialog("open"); + }); + } + } + var arr = lid.split("_"); + initLayer( Number(arr[arr.length-1]), arr[arr.length-2]); + }; + + var addNeuronsToLayer = function(){ + $("#"+containerID).append(""); + $("#neuronnum_layer_dialog").dialog({ + autoOpen: false, + show: { + effect: "fade", + duration: 500 + }, + hide: { + effect: "fade", + duration: 500 + }, + open: function(){ + $("#neuronnum_layer_dialog form input").val($("#button_"+$(this).data("buttonID")).val()); + }, + buttons: { + "OK": function(){ + var neuron_num = $("#neuronnum_layer_dialog form input").val(); + var button = $("#button_"+$(this).data("buttonID")); + //button.css({left: String(40-neuron_num.length*5)+"%"}); + neuron_num = Number(neuron_num); + button.val(neuron_num); + button.parent().css({ "background": layer_color(neuron_num)}); + updateLayer($(this).data("buttonID"), neuron_num); + $(this).dialog("close"); + }, + "Close": function() { + $(this).dialog("close"); + } + } + }).data('buttonID', '-1'); + $("#neuronnum_layer_dialog").on("click", "#training_strategy_button", function () { + var d = $("#trainingstrategy_dialog"); + d.data("formID", $("#neuronnum_layer_dialog").data("buttonID")); + d.dialog("open"); + }); + $("#neuronnum_layer_dialog form input").on("keypress keydown keyup", function(e) { + e.stopPropagation(); + }); + }; + + var outputLayerOptionsDialog = function(){ + var layertype = ""; + $("#"+containerID).append(""); + $("#output_layer_dialog").dialog({ + autoOpen: false, + show: { + effect: "fade", + duration: 500 + }, + hide: { + effect: "fade", + duration: 500 + }, + open: function(){ + var arr = $(this).data("buttonID").split("_"); + arr = Number(arr[arr.length-1]); + if (layers[arr].func!==undefined) { + $("#output_layer_type").val(layers[arr].func); + } + }, + buttons: { + "OK": function(){ + var arr = $(this).data("buttonID").split("_"); + arr = Number(arr[arr.length-1]); + layers[arr]["func"] = $("#output_layer_type").val(); + $(this).dialog("close"); + }, + "Close": function() { + $(this).dialog("close"); + } + } + }).data('buttonID', '-1'); + $("#output_layer_dialog").on("click", "#training_strategy_button", function () { + var d = $("#trainingstrategy_dialog"); + d.data("formID", $("#output_layer_dialog").data("buttonID")); + d.dialog("open"); + }); + }; + + var createForm = function(id, form){ + var string = ""; + form.forEach(function(opts, optID){ + string += ""; + if ("title" in opts){ + string += ""; + } else { + string += ""; + } + string += ""; + string += ""; + }); + string += "
"; + if (opts["type"]=="select"){ + string += ""; + } else { + string += ""; + } + string += "
"; + return string; + }; + + var syncForm = function(id, form, options){ + for(var opt in options){ + if (options[opt]===true || options[opt]===false){ + $("#"+id+"_"+opt).prop("checked", options[opt]); + $("#"+id+"_"+opt).change(function(){ + $(this).attr("value", this.checked ? 1 : 0); + }); + if (options[opt]==true){ + $("#"+id+"_" + opt).val(1); + } else { + $("#" + id + "_" + opt).val(0); + } + } else { + $("#"+id+"_" + opt).val(options[opt]); + } + } + }; + + var trainingStrategyForm = function(){ + var form = new Map(); + form.set("LearningRate", { + type: "text" + }); + form.set("Momentum", { + type: "text" + }); + form.set("Repetitions", { + type: "text" + }); + form.set("ConvergenceSteps", { + type: "text" + }); + form.set("BatchSize", { + type: "text" + }); + form.set("TestRepetitions", { + type: "text" + }); + form.set("WeightDecay", { + type: "text" + }); + form.set("Regularization", { + type: "select", + options: ["NONE", "L1", "L2", "L1MAX"] + }); + form.set("DropConfig", { + type: "text" + }); + form.set("DropRepetitions", { + type: "text" + }); + form.set("Multithreading", { + type: "checkbox" + }); + $("#"+containerID).append(""); + $("#trainingstrategy_dialog").dialog({ + autoOpen: false, + width: 400, + show: { + effect: "fade", + duration: 500 + }, + hide: { + effect: "fade", + duration: 500 + }, + open: function(){ + var arr = $(this).data("formID").split("_"); + var i = Number(arr[arr.length-1]); + syncForm("trainingstrategy", form, layers[i].trainingStrategy); + }, + buttons: { + "OK": function(){ + var arr = $(this).data("formID").split("_"); + var idx = Number(arr[arr.length-1]); + arr = $("#trainingstrategy_dialog input, #trainingstrategy_dialog select"); + var id; + for(var i=0;i