twoD_diffusion_ME::
twoD_diffusion_ME(
  const Teuchos::RCP<const Epetra_Comm>& comm, int n, int d,
  double s, double mu,
  const Teuchos::RCP<const Stokhos::OrthogPolyBasis<int,double> >& basis_,
  bool log_normal_,
  bool eliminate_bcs_,
  const Teuchos::RCP<Teuchos::ParameterList>& precParams_) :
  mesh(n*n),
  basis(basis_),
  log_normal(log_normal_),
  eliminate_bcs(eliminate_bcs_),
  precParams(precParams_)
{
  //////////////////////////////////////////////////////////////////////////////
  // Construct the mesh.
  // The mesh is uniform and the nodes are numbered
  // LEFT to RIGHT, DOWN to UP.
  //
  // 5-6-7-8-9
  // | | | | |
  // 0-1-2-3-4
  /////////////////////////////////////////////////////////////////////////////
  double xyLeft = -.5;
  double xyRight = .5;
  h = (xyRight - xyLeft)/((double)(n-1));
  Teuchos::Array<int> global_dof_indices;
  for (int j=0; j<n; j++) {
    double y = xyLeft + j*h;
    for (int i=0; i<n; i++) {
      double x = xyLeft + i*h;
      int idx = j*n+i;
      mesh[idx].x = x;
      mesh[idx].y = y;
      if (i == 0 || i == n-1 || j == 0 || j == n-1)
        mesh[idx].boundary = true;
      if (i != 0)
        mesh[idx].left = idx-1;
      if (i != n-1)
        mesh[idx].right = idx+1;
      if (j != 0)
        mesh[idx].down = idx-n;
      if (j != n-1)
        mesh[idx].up = idx+n;
      if (!(eliminate_bcs && mesh[idx].boundary))
        global_dof_indices.push_back(idx);
    }
  }

  // Solution vector map
  int n_global_dof = global_dof_indices.size();
  int n_proc = comm->NumProc();
  int proc_id = comm->MyPID();
  int n_my_dof = n_global_dof / n_proc;
  if (proc_id == n_proc-1)
    n_my_dof += n_global_dof % n_proc;
  int *my_dof = global_dof_indices.getRawPtr() + proc_id*(n_global_dof / n_proc);
  x_map =
    Teuchos::rcp(new Epetra_Map(n_global_dof, n_my_dof, my_dof, 0, *comm));

  // Initial guess, initialized to 0.0
  x_init = Teuchos::rcp(new Epetra_Vector(*x_map));
  x_init->PutScalar(0.0);

  // Parameter vector map
  p_map = Teuchos::rcp(new Epetra_LocalMap(d, 0, *comm));

  // Response vector map
  g_map = Teuchos::rcp(new Epetra_LocalMap(1, 0, *comm));

  // Initial parameters
  p_init = Teuchos::rcp(new Epetra_Vector(*p_map));
  p_init->PutScalar(0.0);

  // Parameter names
  p_names = Teuchos::rcp(new Teuchos::Array<std::string>(d));
  for (int i=0;i<d;i++) {
    std::stringstream ss;
    ss << "KL Random Variable " << i+1;
    (*p_names)[i] = ss.str();
  }

  // Build Jacobian graph
  int NumMyElements = x_map->NumMyElements();
  int *MyGlobalElements = x_map->MyGlobalElements();
  graph = Teuchos::rcp(new Epetra_CrsGraph(Copy, *x_map, 5));
  for (int i=0; i<NumMyElements; ++i ) {

    // Center
    int global_idx = MyGlobalElements[i];
    graph->InsertGlobalIndices(global_idx, 1, &global_idx);

    if (!mesh[global_idx].boundary) {
      // Down
      if (!(eliminate_bcs && mesh[mesh[global_idx].down].boundary))
        graph->InsertGlobalIndices(global_idx, 1, &mesh[global_idx].down);

      // Left
      if (!(eliminate_bcs && mesh[mesh[global_idx].left].boundary))
        graph->InsertGlobalIndices(global_idx, 1, &mesh[global_idx].left);

      // Right
      if (!(eliminate_bcs && mesh[mesh[global_idx].right].boundary))
        graph->InsertGlobalIndices(global_idx, 1, &mesh[global_idx].right);

      // Up
      if (!(eliminate_bcs && mesh[mesh[global_idx].up].boundary))
        graph->InsertGlobalIndices(global_idx, 1, &mesh[global_idx].up);
    }
  }
  graph->FillComplete();
  graph->OptimizeStorage();

  KL_Diffusion_Func klFunc(xyLeft, xyRight, mu, s, 1.0, d);
  if (!log_normal) {
    // Fill coefficients of KL expansion of operator
    if (basis == Teuchos::null) {
      fillMatrices(klFunc, d+1);
    }
    else {
      Normalized_KL_Diffusion_Func<KL_Diffusion_Func> nklFunc(klFunc, *basis);
      fillMatrices(nklFunc, d+1);
    }
  }
  else {
    // Fill coefficients of PC expansion of operator
    int sz = basis->size();
    Teuchos::RCP<const Stokhos::ProductBasis<int, double> > prodbasis =
      Teuchos::rcp_dynamic_cast<const Stokhos::ProductBasis<int, double> >(
        basis, true);
    LogNormal_Diffusion_Func<KL_Diffusion_Func> lnFunc(mu, klFunc, prodbasis);
    fillMatrices(lnFunc, sz);
  }

  // Construct deterministic operator
  A = Teuchos::rcp(new Epetra_CrsMatrix(Copy, *graph));

  // Construct the RHS vector.
  b = Teuchos::rcp(new Epetra_Vector(*x_map));
  for( int i=0 ; i<NumMyElements; ++i ) {
    int global_idx = MyGlobalElements[i];
    if (mesh[global_idx].boundary)
      (*b)[i] = 0;
    else
      (*b)[i] = 1;
  }

  if (basis != Teuchos::null) {
    point.resize(d);
    basis_vals.resize(basis->size());
  }

  if (precParams != Teuchos::null) {
    std::string name = precParams->get("Preconditioner Type", "Ifpack");
    Teuchos::RCP<Teuchos::ParameterList> p =
      Teuchos::rcp(&(precParams->sublist("Preconditioner Parameters")), false);
    precFactory =
      Teuchos::rcp(new Stokhos::PreconditionerFactory(name, p));
  }
}
	void ExRandomTrees::buildTree(){
		unsigned int treeId;
		m_chooseIdLock->lock();
			treeId = m_choiceId++;
		m_chooseIdLock->unlock();

		ExTree& tree = m_trees[treeId];
		tree.m_nodes.reserve(m_evaluation->getNumTrainingInstances()*2);
		tree.m_nodes.push_back(ExTreeNode(0,m_evaluation->getNumTrainingInstances()));
		for(unsigned int i=0; i<m_evaluation->getNumTrainingInstances(); ++i){
			++tree.m_nodes[0].classProbs[m_classSetTrain[i]];
		}

		unsigned int depth = 0;
		unsigned int unprocessedNodes = 1, newNodes = 0;
		unsigned int numInstances;
		boost::random::uniform_int_distribution<> attRand(0,m_document->getNumAttributes()-1);
		double split;
		std::vector<std::vector<unsigned int>> dist,bestDist;
		int k;
		bool sensibleSplit = false, priorDone;
		double prior, posterior;
		double bestVal = -1000;
		unsigned int attribute;
		double splitPoint;

		while(unprocessedNodes != 0){
			// Iterate through unprocessed nodes
			while(unprocessedNodes != 0){
				ExTreeNode& currentNode = tree.m_nodes[tree.m_nodes.size()-(unprocessedNodes+newNodes)];

				// Check if node is a leaf
				numInstances = currentNode.instIndEnd - currentNode.instIndStart;
				if((numInstances > 0  &&  numInstances < max(2, m_minNumInst))  // small
					|| (abs((currentNode.classProbs[0] > currentNode.classProbs[1] ? currentNode.classProbs[0] : currentNode.classProbs[1]) - (currentNode.classProbs[0]+currentNode.classProbs[1])) < 1e-6)      // pure
					|| ((m_maxDepth > 0)  &&  (depth >= m_maxDepth))                           // deep
					){
				}
				else{
					boost::random::uniform_int_distribution<> instRand(0,numInstances-1);
					priorDone = false;
					k = m_numFeatures;
					sensibleSplit = false;
					bestVal = -1000;
					unsigned int numIter = 0;
					while(numIter < m_document->getNumAttributes() && (k-- > 0 || !sensibleSplit)){
						++numIter;
						attribute = attRand(tree.m_rng);

						split = 0;
						for(unsigned int i=0; i<10; ++i){
							split += m_evaluation->getTrainingInstance(tree.m_instIndices[tree.m_bufferId][currentNode.instIndStart+instRand(tree.m_rng)])->getValue(attribute);
						}
						splitPoint = split/10;

						// Calculate distribution
						dist = std::vector<std::vector<unsigned int>>(2,std::vector<unsigned int>(2,0));
						unsigned int instId;
						for(unsigned int i=0; i<numInstances; ++i){
							instId = tree.m_instIndices[tree.m_bufferId][currentNode.instIndStart+i];
							++dist[m_dataSetTrain[attribute][instId] < splitPoint ? 0 : 1][m_classSetTrain[instId]];
						}

						if(!priorDone){ // needs to be computed only once per branch
							// Entropy over collumns
							prior = 0;
							double sumForColumn, total = 0;
							for (size_t j = 0; j < dist[0].size(); j++) {
								sumForColumn = 0;
								for (size_t i = 0; i < dist.size(); i++) {
									sumForColumn += dist[i][j];
								}
								prior -= lnFunc(sumForColumn);
								total += sumForColumn;
							}
							prior = (prior + lnFunc(total)); 

							priorDone = true;
						}
      
						// Entropy over rows
						posterior = 0;
						double sumForBranch;
						for (size_t branchNum = 0; branchNum < dist.size(); branchNum++) {
							sumForBranch = 0;
							for(size_t classNum = 0; classNum < dist[0].size(); classNum++) {
								posterior = posterior + lnFunc(dist[branchNum][classNum]);
								sumForBranch += dist[branchNum][classNum];
							}
							posterior = posterior - lnFunc(sumForBranch);
						}
						posterior = -posterior;

						if(bestVal < prior - posterior){
							bestVal = prior - posterior;
							currentNode.m_attribute = attribute;
							currentNode.m_splitPoint = splitPoint;
							bestDist = dist;
						}

						if(prior - posterior > 1e-2)   // we allow some leeway here to compensate
							sensibleSplit = true;   // for imprecision in entropy computation
					}

					if(sensibleSplit){
						// Split node
						unsigned int cpyBuffer = (tree.m_bufferId == 0 ? 1 : 0);
						unsigned int instId;
						
						tree.m_nodes.push_back(ExTreeNode(currentNode.instIndStart,currentNode.instIndStart+bestDist[0][0]+bestDist[0][1]));
						tree.m_nodes.back().classProbs[0] = bestDist[0][0];
						tree.m_nodes.back().classProbs[1] = bestDist[0][1];
						currentNode.m_children.push_back(&tree.m_nodes.back());

						tree.m_nodes.push_back(ExTreeNode(currentNode.instIndStart+bestDist[0][0]+bestDist[0][1],currentNode.instIndEnd));
						tree.m_nodes.back().classProbs[0] = bestDist[1][0];
						tree.m_nodes.back().classProbs[1] = bestDist[1][1];
						currentNode.m_children.push_back(&tree.m_nodes.back());

						unsigned int left = currentNode.instIndStart, right = currentNode.instIndEnd-1;
						for(unsigned int i=0; i<numInstances; ++i){
							instId = tree.m_instIndices[tree.m_bufferId][currentNode.instIndStart+i];
							if(m_dataSetTrain[currentNode.m_attribute][instId] < currentNode.m_splitPoint){
								tree.m_instIndices[cpyBuffer][left] = instId;
								++left;
							}
							else{
								tree.m_instIndices[cpyBuffer][right] = instId;
								--right;
							}
						}

						newNodes += 2;
					}
				}
				unprocessedNodes--;
			}

			tree.m_bufferId = (tree.m_bufferId == 0 ? 1 : 0);

			depth++;
			unprocessedNodes = newNodes;
			newNodes = 0;
		}
	}