MUQ  0.4.3
ModGraphPiece.cpp
Go to the documentation of this file.
2 
9 
10 #include <boost/range/adaptor/reversed.hpp>
11 #include <boost/graph/topological_sort.hpp>
12 
13 #include <Eigen/Core>
14 
15 using namespace muq::Modeling;
16 using namespace muq::Utilities;
17 
18 
19 
20 ModGraphPiece::ModGraphPiece(std::shared_ptr<WorkGraph> graph,
21  std::vector<std::shared_ptr<ConstantVector> > const& constantPiecesIn,
22  std::vector<std::string> const& inputNames,
23  std::shared_ptr<ModPiece> outputPieceIn) : ModPiece(ConstructInputSizes(constantPiecesIn),
24  outputPieceIn->outputSizes),
25  wgraph(graph),
26  outputID(outputPieceIn->ID()),
27  outputPiece(outputPieceIn),
28  constantPieces(constantPiecesIn) {
29 
30  // build the run order
31  assert(graph);
32  boost::topological_sort(wgraph->graph, std::front_inserter(runOrder));
33 
34  // each input only needs to loop over its downstream nodes when computing derivatives
35  adjointRunOrders.resize(inputSizes.size());
36  filtered_graphs.resize(inputSizes.size());
37 
38  // compute a run order for each of the inputs so we only have to loop over their downstream nodes
39  assert(numInputs==inputNames.size());
40 
41  for( unsigned int i=0; i<numInputs; ++i ) { // loop through the inputs
42 
43  // get iterators to the begining and end of the graph
44  boost::graph_traits<Graph>::vertex_iterator v, v_end;
45  boost::tie(v, v_end) = vertices(wgraph->graph);
46 
47  // determine the downstream nodes of this input
48  DependentPredicate nFilt(*std::find_if(v, v_end, NodeNameFinder(inputNames[i], wgraph->graph)), wgraph->graph);
49  DependentEdgePredicate eFilt(nFilt, wgraph->graph);
50 
51  // filter the graph, we only care about downstream nodes of this input
52  filtered_graphs[i] = std::make_shared<boost::filtered_graph<Graph, DependentEdgePredicate, DependentPredicate> >(wgraph->graph, eFilt, nFilt);
53 
54  // build specialized run order for each input dimension
55  boost::topological_sort(*filtered_graphs[i], std::back_inserter(adjointRunOrders[i]));
56  }
57 }
58 
59 int ModGraphPiece::GetInputIndex(std::shared_ptr<WorkPiece> const& piece) const
60 {
61  for(int i = 0; i<constantPieces.size(); ++i){
62  if(piece == constantPieces.at(i))
63  return i;
64  }
65 
66  return -1;
67 }
68 
69 std::shared_ptr<ModGraphPiece> ModGraphPiece::GradientGraph(unsigned int const outputDimWrt,
70  unsigned int const inputDimWrt)
71 {
72  assert(outputDimWrt<outputSizes.size());
73  assert(inputDimWrt<inputSizes.size());
74 
75  WorkGraph gradGraph;
76  auto& filtGraph = *filtered_graphs[inputDimWrt];
77 
78  std::vector<std::string> inputNames(constantPieces.size()+1);
79  std::vector<boost::graph_traits<Graph>::vertex_descriptor> inputNodes(constantPieces.size());
80 
81  std::string outputName = filtGraph[adjointRunOrders[inputDimWrt][0]]->name;
82  inputNames.at(constantPieces.size()) = outputName + "_Sensitivity";
83 
84  bool needsFinalSum = false;
85 
86  // Add the forward model
87  for(auto node = runOrder.begin(); node != runOrder.end()-1; ++node){
88  auto piece = wgraph->graph[*node]->piece;
89 
90  int placeholderInd = GetInputIndex(piece);
91  if(placeholderInd>=0){
92  auto pieceMod = std::dynamic_pointer_cast<ModPiece>(piece);
93  assert(pieceMod);
94  gradGraph.AddNode(std::make_shared<IdentityOperator>(pieceMod->outputSizes(0)), wgraph->graph[*node]->name);
95  inputNames.at(placeholderInd) = wgraph->graph[*node]->name;
96  inputNodes.at(placeholderInd) = *node;
97  }else{
98  gradGraph.AddNode(piece, wgraph->graph[*node]->name);
99 
100  // Add the input edges
101  for(auto es = in_edges(*node, wgraph->graph); es.first!=es.second; ++es.first){
102  auto source = boost::source(*es.first, wgraph->graph);
103  gradGraph.AddEdge(wgraph->graph[source]->name, wgraph->graph[*es.first]->outputDim, wgraph->graph[*node]->name, wgraph->graph[*es.first]->inputDim);
104  }
105  }
106  }
107 
108  // Add a node in the graph to hold the sensitivity input
109  gradGraph.AddNode(std::make_shared<IdentityOperator>(outputSizes(outputDimWrt)),
110  outputName+"_Sensitivity");
111 
112 
113  // Add the adjoint components
114  for(auto node : adjointRunOrders[inputDimWrt]){
115  std::string baseName = wgraph->graph[node]->name;
116 
117  auto piece = std::dynamic_pointer_cast<ModPiece>(filtGraph[node]->piece);
118  assert(piece);
119 
120  // outEdges stores information about where information for each output of this piece goes
121  // outEdges[node output index][edge index]
122  std::vector<std::vector<std::pair<boost::graph_traits<boost::filtered_graph<Graph, DependentEdgePredicate, DependentPredicate>>::vertex_descriptor, unsigned int>>> outEdges(piece->outputSizes.size()); // Pairs are node name and input id.
123  for(auto eout = out_edges(node, filtGraph); eout.first!=eout.second; ++eout.first){
124  auto target = boost::target(*eout.first, filtGraph);
125  outEdges.at( filtGraph[*eout.first]->outputDim ).push_back(std::make_pair(target, filtGraph[*eout.first]->inputDim ));
126  }
127 
128  // Figure out if the output of the node is used by more than one downstream node, which means we'll have to add up the gradients wrt this nodes output
129  std::vector<unsigned int> edgeCounts(outEdges.size());
130  for(unsigned int outInd=0; outInd<outEdges.size(); ++outInd){
131 
132  // Get the total number of gradient pieces that might be flowing into this sum
133  edgeCounts[outInd] = 0;
134  for(unsigned int i = 0; i<outEdges.at(outInd).size(); ++i){
135  auto mod = std::dynamic_pointer_cast<ModPiece>(wgraph->graph[outEdges.at(outInd).at(i).first]->piece);
136  assert(mod);
137  edgeCounts[outInd] += mod->outputSizes.size();
138  }
139 
140  // If there is more than one downstream node, we need to add up their sensitivities to this node
141  if(edgeCounts[outInd]>1){
142 
143  std::stringstream sumName;
144  sumName << baseName << "[" << outInd << "]_SensitivitySum";
145  gradGraph.AddNode(std::make_shared<SumPiece>(piece->outputSizes(outInd), edgeCounts[outInd]), sumName.str());
146 
147  if(baseName==inputNames.at(inputDimWrt)){
148  needsFinalSum = true;
149  }
150  }
151  } // end of ouput index loop for counting edges
152 
153  // for each combination of incominging and outgoing edges for the forward node in the filtered graph, add a Gradient piece.
154  for( auto e =in_edges(node, filtGraph); e.first!=e.second; ++e.first ) {
155  auto source = boost::source(*e.first,filtGraph);
156 
157  // If this is the first node in the adjoint run order, then it is the last node in the forward graph
158  if(node==adjointRunOrders[inputDimWrt][0]){
159  std::stringstream nodeName;
160 
161  unsigned int inDim = filtGraph[*e.first]->inputDim;
162  auto gradPiece = std::make_shared<GradientPiece>(piece, outputDimWrt, inDim);
163  nodeName << filtGraph[adjointRunOrders[inputDimWrt][0]]->name << "_Gradient[" << outputDimWrt << "," << inDim << "]";
164  try{
165  gradGraph.AddNode(gradPiece, nodeName.str());
166  }catch(const std::logic_error&){}
167 
168  gradGraph.AddEdge(outputName +"_Sensitivity", 0 , nodeName.str(), gradPiece->inputSizes.size()-1);
169 
170  // Add edges for the inputs
171  for( auto e2 =in_edges(node, wgraph->graph); e2.first!=e2.second; ++e2.first ) {
172  auto source = boost::source(*e2.first,wgraph->graph);
173  gradGraph.AddEdge(wgraph->graph[source]->name, wgraph->graph[*e2.first]->outputDim,
174  nodeName.str(), wgraph->graph[*e2.first]->inputDim);
175  }
176 
177  }else{
178 
179  // Loop through all the edges and add the adjoint nodes and edges
180  for(unsigned int outInd=0; outInd<outEdges.size(); ++outInd){
181 
182  unsigned int inDim = filtGraph[*e.first]->inputDim;
183  auto gradPiece = std::make_shared<GradientPiece>(piece, outInd, inDim);
184 
185  std::stringstream nodeName;
186  nodeName << filtGraph[node]->name << "_Gradient[" << outInd << "," << inDim << "]";
187  gradGraph.AddNode(gradPiece, nodeName.str());
188 
189  // Add edges for the inputs
190  for( auto e2 =in_edges(node, wgraph->graph); e2.first!=e2.second; ++e2.first ) {
191  auto source = boost::source(*e2.first,wgraph->graph);
192  gradGraph.AddEdge(wgraph->graph[source]->name, wgraph->graph[*e2.first]->outputDim,
193  nodeName.str(), wgraph->graph[*e2.first]->inputDim);
194  }
195 
196  // Add edges for the sensitivities.
197  /* If the output of any node is used in more than one other ModPiece, to
198  compute the gradient, we will need to accumulate the sensitivities from
199  all outputs using a SumPiece. This loop creates the SumPieces
200  */
201 
202  // If there is more than one downstream node, we need to add up their sensitivities to this node
203  if(edgeCounts[outInd]>1){
204  std::stringstream sumName;
205  sumName << baseName << "[" << outInd << "]_SensitivitySum";
206 
207  // Add edges for the gradient pieces that flow into the sum
208  unsigned int edgeInd = 0;
209  for(unsigned int i = 0; i<outEdges.at(outInd).size(); ++i){
210  auto nextNode = filtGraph[outEdges.at(outInd).at(i).first];
211  auto nextMod = std::dynamic_pointer_cast<ModPiece>(nextNode->piece);
212  int nextNumOutputs = nextMod->outputSizes.size();
213 
214  for(unsigned int nextOutInd=0; nextOutInd<nextNumOutputs; ++nextOutInd){
215  std::stringstream otherNodeName;
216  otherNodeName << nextNode->name << "_Gradient[" << nextOutInd << "," << outEdges.at(outInd).at(i).second << "]";
217  gradGraph.AddEdge(otherNodeName.str(),0, sumName.str(), edgeInd);
218  edgeInd++;
219  }
220  }
221 
222  // Connect the SensitivitySum to the gradient pice
223  gradGraph.AddEdge(sumName.str(), 0, nodeName.str(), gradPiece->inputSizes.size()-1);
224 
225 
226  }else if(outEdges.at(outInd).size()>0){ // There is only one edge flowing into the gradient
227  auto es=out_edges(outEdges.at(outInd).at(0).first, filtGraph);
228  if(outEdges.at(outInd).at(0).first == adjointRunOrders[inputDimWrt][0]){
229  std::stringstream otherNodeName;
230  otherNodeName << filtGraph[adjointRunOrders[inputDimWrt][0]]->name << "_Gradient[" << outputDimWrt << "," << outEdges.at(outInd).at(0).second << "]";
231 
232  gradGraph.AddEdge(otherNodeName.str(), 0, nodeName.str(), gradPiece->inputSizes.size()-1);
233 
234  }else if(es.first != es.second){
235  auto nextNode = filtGraph[outEdges.at(outInd).at(0).first];
236 
237  std::stringstream otherNodeName;
238  otherNodeName << nextNode->name << "_Gradient[" << filtGraph[*es.first]->outputDim << "," << outEdges.at(outInd).at(0).second << "]";
239 
240  gradGraph.AddEdge(otherNodeName.str(), 0, nodeName.str(), gradPiece->inputSizes.size()-1);
241  }
242  }
243 
244  } // end of outgoing edge loop
245  }
246  } // end of incoming edge
247 
248  } // end of adjoint run order loop
249 
250  // Add any necessary edges for the final gradient
251  std::string outNodeName;
252  if(needsFinalSum){
253 
254  outNodeName = inputNames.at(inputDimWrt) + "[0]_SensitivitySum";
255 
256  for( auto eout=out_edges(inputNodes.at(inputDimWrt), filtGraph); eout.first!=eout.second; ++eout.first )
257  {
258  auto target = boost::target(*eout.first,filtGraph);
259  auto targetPiece = std::dynamic_pointer_cast<ModPiece>(wgraph->graph[target]->piece);
260  if(targetPiece){
261  // Loop over all the output edges
262  for(unsigned int otherOutInd=0; otherOutInd<targetPiece->outputSizes.size(); ++otherOutInd){
263  std::stringstream otherName;
264  otherName << wgraph->graph[target]->name << "_Gradient[" << otherOutInd << "," << wgraph->graph[*eout.first]->inputDim << "]";
265  gradGraph.AddEdge(otherName.str(), 0, outNodeName, otherOutInd);
266  }
267  }
268 
269  }
270 
271  }else{
272 
273  // At this point, there should only be one node with an unconnected output.
274  for(auto vs = boost::vertices(gradGraph.graph); vs.first!=vs.second; ++vs.first){
275  auto pieceMod = std::dynamic_pointer_cast<ModPiece>(gradGraph.graph[*vs.first]->piece);
276  if(pieceMod){
277  if(out_degree(*vs.first, gradGraph.graph) <pieceMod->outputSizes.size()){
278  outNodeName = gradGraph.graph[*vs.first]->name;
279  break;
280  }
281  }
282  }
283  }
284 
285  return gradGraph.CreateModPiece(outNodeName, inputNames);
286 }
287 
288 std::shared_ptr<ModGraphPiece> ModGraphPiece::JacobianGraph(unsigned int const outputDimWrt,
289  unsigned int const inputDimWrt)
290 {
291  assert(outputDimWrt<outputSizes.size());
292  assert(inputDimWrt<inputSizes.size());
293 
294  WorkGraph jacGraph;
295  auto& filtGraph = *filtered_graphs[inputDimWrt];
296 
297  std::vector<std::string> inputNames(constantPieces.size()+1);
298 
299  // Add the forward model
300  for(auto node = runOrder.begin(); node != runOrder.end()-1; ++node){
301  auto piece = wgraph->graph[*node]->piece;
302 
303  int placeholderInd = GetInputIndex(piece);
304  if(placeholderInd>=0){
305  auto pieceMod = std::dynamic_pointer_cast<ModPiece>(piece);
306  assert(pieceMod);
307  jacGraph.AddNode(std::make_shared<IdentityOperator>(pieceMod->outputSizes(0)), wgraph->graph[*node]->name);
308  inputNames.at(placeholderInd) = wgraph->graph[*node]->name;
309 
310  }else{
311  jacGraph.AddNode(piece, wgraph->graph[*node]->name);
312 
313  // Add the input edges
314  for(auto es = in_edges(*node, wgraph->graph); es.first!=es.second; ++es.first){
315  auto source = boost::source(*es.first, wgraph->graph);
316  jacGraph.AddEdge(wgraph->graph[source]->name, wgraph->graph[*es.first]->outputDim, wgraph->graph[*node]->name, wgraph->graph[*es.first]->inputDim);
317  }
318  }
319  }
320 
321  // Add a node in the graph to hold the vector input
322  jacGraph.AddNode(std::make_shared<IdentityOperator>(inputSizes(inputDimWrt)), inputNames.at(inputDimWrt) + "_Vec");
323 
324  // Add the Jacobian components
325  std::unordered_map<std::string, std::vector<std::string>> cumNames;
326 
327  for(auto node = adjointRunOrders[inputDimWrt].rbegin(); node!=adjointRunOrders[inputDimWrt].rend(); ++node){
328  std::string baseName = wgraph->graph[*node]->name;
329 
330  auto piece = std::dynamic_pointer_cast<ModPiece>(filtGraph[*node]->piece);
331 
332  int inDegree = boost::in_degree(*node, filtGraph);
333  int outDegree = boost::out_degree(*node, filtGraph);
334 
335  auto modCast = std::dynamic_pointer_cast<ModPiece>(filtGraph[*node]->piece);
336  assert(modCast);
337  //std::cout << "Working on node " << baseName << ", " << outDegree << ", " << inDegree << std::endl;
341  cumNames[baseName] = std::vector<std::string>(out_degree(*node,filtGraph));
342  for(auto eout = boost::out_edges(*node, filtGraph); eout.first!=eout.second; ++eout.first){
343  if(inDegree>1){
344  std::stringstream jacName;
345  jacName << baseName << "_CumulativeJacobian[" << filtGraph[*eout.first]->outputDim << "]";
346  cumNames[baseName].at(filtGraph[*eout.first]->outputDim) = jacName.str();
347 
348  if(!jacGraph.HasNode(jacName.str()))
349  jacGraph.AddNode(std::make_shared<SumPiece>(modCast->outputSizes(filtGraph[*eout.first]->outputDim),inDegree), jacName.str());
350  }else if(inDegree==1){
351  std::stringstream jacName;
352  jacName << baseName << "_Jacobian[" << filtGraph[*eout.first]->outputDim << ",0]";
353  cumNames[baseName].at(filtGraph[*eout.first]->outputDim) = jacName.str();
354  }
355  }
356 
357  // For each output in the filtered graph, add a jacobian term for each input in the filtered graph
358  for(auto ein=boost::in_edges(*node, filtGraph); ein.first!=ein.second; ++ein.first){
359  // If we're the last node....
360  if(*node == adjointRunOrders[inputDimWrt][0]){
361  std::stringstream jacName;
362  jacName << baseName << "_Jacobian[" << outputDimWrt << "," << filtGraph[*ein.first]->inputDim << "]";
363  auto jacPiece = std::make_shared<JacobianPiece>(piece, outputDimWrt, filtGraph[*ein.first]->inputDim);
364 
365  jacGraph.AddNode(jacPiece, jacName.str());
366 
367  // Add input edges from the original forward graph
368  for(auto es=boost::in_edges(*node, wgraph->graph); es.first!=es.second; ++es.first){
369  auto source = boost::source(*es.first, wgraph->graph);
370  jacGraph.AddEdge(wgraph->graph[source]->name, wgraph->graph[*es.first]->outputDim, jacName.str(), wgraph->graph[*es.first]->inputDim);
371  }
372 
373  auto source = boost::source(*ein.first,filtGraph);
374  if(filtGraph[source]->name == inputNames.at(inputDimWrt)){
375  jacGraph.AddEdge(inputNames.at(inputDimWrt)+ "_Vec", 0, jacName.str(), jacPiece->inputSizes.size()-1);
376  }else if(boost::in_degree(source,filtGraph)>0){
377  jacGraph.AddEdge(cumNames[filtGraph[source]->name].at(filtGraph[*ein.first]->outputDim), 0, jacName.str(), jacPiece->inputSizes.size()-1);
378  }
379 
380  // If there are multiple inputs, we have to add them up
381 
382  }else{
383 
384  for(auto eout = boost::out_edges(*node, filtGraph); eout.first!=eout.second; ++eout.first){
385 
386  std::stringstream jacName;
387  jacName << baseName << "_Jacobian[" << filtGraph[*eout.first]->outputDim << "," << filtGraph[*ein.first]->inputDim << "]";
388 
389  // Try statement needed because we might end up adding the same piece multiple times
390  try{
391  auto jacPiece = std::make_shared<JacobianPiece>(piece, filtGraph[*eout.first]->outputDim, filtGraph[*ein.first]->inputDim);
392  jacGraph.AddNode(jacPiece, jacName.str());
393 
394  // Add input edges from the original forward graph
395  for(auto es=boost::in_edges(*node, wgraph->graph); es.first!=es.second; ++es.first){
396  auto source = boost::source(*es.first, wgraph->graph);
397  jacGraph.AddEdge(wgraph->graph[source]->name, wgraph->graph[*es.first]->outputDim, jacName.str(), wgraph->graph[*es.first]->inputDim);
398  }
399 
400  // Add an input edge for the upstream Jacobian
401  auto source = boost::source(*ein.first,filtGraph);
402  if(filtGraph[source]->name == inputNames.at(inputDimWrt)){
403  jacGraph.AddEdge(inputNames.at(inputDimWrt)+ "_Vec", 0, jacName.str(), jacPiece->inputSizes.size()-1);
404  }else if(boost::in_degree(source,filtGraph)>0){
405  jacGraph.AddEdge(cumNames[filtGraph[source]->name].at(filtGraph[*ein.first]->outputDim), 0, jacName.str(), jacPiece->inputSizes.size()-1);
406  }
407  }catch(const std::logic_error& e){
408  //std::cout << "duplicate..." << std::endl;
409  }
410  } // Loop over output edges
411  }
412  } // Loop over input edges
413 
414  // Potentially add up the Jacobians from multiple inputs
415  for(auto eout = boost::out_edges(*node, filtGraph); eout.first!=eout.second; ++eout.first){
416  if(inDegree>1){
417  std::stringstream cumName;
418  cumName << baseName << "_CumulativeJacobian[" << filtGraph[*eout.first]->outputDim << "]";
419 
420  // For each input ...
421  unsigned int tempInd = 0;
422  for(auto ein = boost::in_edges(*node, filtGraph); ein.first!=ein.second; ++ein.first){
423  std::stringstream jacName;
424  jacName << baseName << "_Jacobian[" << filtGraph[*eout.first]->outputDim << "," << filtGraph[*ein.first]->inputDim << "]";
425  jacGraph.AddEdge(jacName.str(),0, cumName.str(), tempInd);
426  tempInd++;
427  }
428  }// if(inDegree>1)
429  }// loop over outputs
430 
431  // If this is the last node and it has multiple inputs, then we'll need to sum them up
432  if(*node == adjointRunOrders[inputDimWrt][0]){
433  unsigned int tempInd = 0;
434 
435  if(inDegree>1){
436  std::stringstream jacName;
437  jacName << baseName << "_CumulativeJacobian[" << outputDimWrt << "]";
438  jacGraph.AddNode(std::make_shared<SumPiece>(modCast->outputSizes(outputDimWrt), inDegree), jacName.str());
439 
440  for(auto ein = boost::in_edges(*node, filtGraph); ein.first!=ein.second; ++ein.first){
441  std::stringstream otherName;
442  otherName << baseName << "_Jacobian[" << outputDimWrt << "," << filtGraph[*ein.first]->inputDim << "]";
443  jacGraph.AddEdge(otherName.str(), 0, jacName.str(), tempInd);
444  tempInd++;
445  }
446  }
447  }
448 
449  } // Loop over adjoint run order
450 
451  // At this point, there should only be one node with an unconnected output.
452  std::string outNodeName;
453  for(auto vs = boost::vertices(jacGraph.graph); vs.first!=vs.second; ++vs.first){
454  auto pieceMod = std::dynamic_pointer_cast<ModPiece>(jacGraph.graph[*vs.first]->piece);
455  if(pieceMod){
456  if(out_degree(*vs.first, jacGraph.graph) <pieceMod->outputSizes.size()){
457  outNodeName = jacGraph.graph[*vs.first]->name;
458  break;
459  }
460  }
461  }
462 
463  inputNames.at(constantPieces.size()) = inputNames.at(inputDimWrt) + "_Vec";
464  return jacGraph.CreateModPiece(outNodeName, inputNames);
465 }
466 
467 Eigen::VectorXi ModGraphPiece::ConstructInputSizes(std::vector<std::shared_ptr<ConstantVector> > const& constantPiecesIn)
468 {
469  Eigen::VectorXi sizes(constantPiecesIn.size());
470  for(int i=0; i<constantPiecesIn.size(); ++i){
471  sizes(i) = constantPiecesIn.at(i)->outputSizes(0);
472  assert(constantPiecesIn.at(i)->outputSizes.size()==1);
473  }
474  return sizes;
475 }
476 
477 void ModGraphPiece::ApplyHessianImpl(unsigned int const outWrt,
478  unsigned int const inWrt1,
479  unsigned int const inWrt2,
480  ref_vector<Eigen::VectorXd> const& input,
481  Eigen::VectorXd const& sens,
482  Eigen::VectorXd const& vec)
483 {
484  ref_vector<Eigen::VectorXd> newInputs(input.begin(),input.end());
485  newInputs.push_back( std::cref(sens));
486  newInputs.push_back( std::cref(vec));
487 
488  std::shared_ptr<ModGraphPiece> hessGraph;
489  std::tuple<unsigned int, unsigned int, unsigned int> wrts = std::make_tuple(outWrt,inWrt1,inWrt2);
490 
491  auto iter = hessianPieces.find(wrts);
492  if(iter==hessianPieces.end())
493  hessianPieces[wrts] = GradientGraph(outWrt,inWrt1)->JacobianGraph(0,inWrt2);
494 
495  hessAction = hessianPieces[wrts]->Evaluate(newInputs).at(0);
496 }
497 
498 void ModGraphPiece::ApplyJacobianImpl(unsigned int const outputDimWrt,
499  unsigned int const inputDimWrt,
500  ref_vector<Eigen::VectorXd> const& input,
501  Eigen::VectorXd const& vec)
502 {
503  ref_vector<Eigen::VectorXd> newInputs(input.begin(),input.end());
504  newInputs.push_back( std::cref(vec));
505 
506  // Check to see if this input-output pair has been computed before. If so, we don't need to recomptue the gradient graph
507  std::pair<unsigned int, unsigned int> indexPair = std::make_pair(outputDimWrt, inputDimWrt);
508  auto iter = jacobianPieces.find(indexPair);
509  if(iter==jacobianPieces.end())
510  jacobianPieces[indexPair] = JacobianGraph(outputDimWrt,inputDimWrt);
511 
512  // Evaluate the gradient
513  jacobianAction = jacobianPieces[indexPair]->Evaluate(newInputs).at(0);
514 }
515 
517  // set the inputs
518  SetInputs(inputs);
519 
520  // fill the map from the WorkPiece ID to its outputs
521  FillOutputMap();
522 
523  // store the result in the output vector
524  outputs.resize(valMap[outputID].size());
525 
526  for(int i=0; i<outputs.size(); ++i) {
527  outputs.at(i) = valMap[outputID].at(i).get();
528  }
529 }
530 
531 void ModGraphPiece::GradientImpl(unsigned int const outputDimWrt,
532  unsigned int const inputDimWrt,
533  ref_vector<Eigen::VectorXd> const& input,
534  Eigen::VectorXd const& sensitivity) {
535 
536  ref_vector<Eigen::VectorXd> newInputs(input.begin(),input.end());
537  newInputs.push_back( std::cref(sensitivity));
538 
539  // Check to see if this input-output pair has been computed before. If so, we don't need to recomptue the gradient graph
540  std::pair<unsigned int, unsigned int> indexPair = std::make_pair(outputDimWrt, inputDimWrt);
541  auto iter = gradientPieces.find(indexPair);
542  if(iter==gradientPieces.end()){
543  gradientPieces[indexPair] = GradientGraph(outputDimWrt,inputDimWrt);
544  }
545 
546  // Evaluate the gradient
547  gradient = gradientPieces[indexPair]->Evaluate(newInputs).at(0);
548 }
549 
550 void ModGraphPiece::JacobianImpl(unsigned int const wrtOut,
551  unsigned int const wrtIn,
552  ref_vector<Eigen::VectorXd> const& inputs) {
553 
554  // set the inputs
555  SetInputs(inputs);
556 
557  // fill the map from the WorkPiece ID to its outputs
558  FillOutputMap();
559 
560  // a map from the WorkPiece ID to a vector holding the cumulative jacobians of that output wrt the specified input
561  std::map<unsigned int, std::vector<Eigen::MatrixXd> > jacMap;
562 
563  // loop through each downstream node
564  for( auto node : boost::adaptors::reverse(adjointRunOrders[wrtIn]) ) {
565 
566  std::shared_ptr<ModPiece> nodePiece = std::dynamic_pointer_cast<ModPiece>(filtered_graphs[wrtIn]->operator[](node)->piece);
567  assert(nodePiece);
568 
569  // the ID of the current node
570  const unsigned int nodeID = nodePiece->ID();
571 
572  // Initialize the jacobian map for this node
573  jacMap[nodeID] = std::vector<Eigen::MatrixXd>(nodePiece->outputSizes.size());
574 
575  // get the outputs of this node that impact the specified output node
576  const std::vector<std::tuple<unsigned int, unsigned int, unsigned int> > & requiredOutNodes = RequiredOutputs(node, wrtIn, wrtOut);
577 
578  // remove duplicates
579  std::vector<unsigned int> requiredOuts;
580  requiredOuts.reserve(requiredOutNodes.size());
581  for( auto out : requiredOutNodes ) {
582  auto it = std::find(requiredOuts.begin(), requiredOuts.end(), std::get<1>(out));
583  if( it==requiredOuts.end() ) {
584  requiredOuts.push_back(std::get<1>(out));
585  }
586  }
587 
588  // Initialize the jacobians that will be stored
589  for(int i=0; i<requiredOuts.size(); ++i)
590  jacMap[nodeID].at(requiredOuts.at(i)) = Eigen::MatrixXd::Zero(nodePiece->outputSizes(requiredOuts.at(i)), inputSizes(wrtIn));
591 
592  // get the inputs for this node --- the input WorkPiece ID, the output number, and the input number
593  const std::vector<std::tuple<unsigned int, unsigned int, unsigned int> >& requiredIns = RequiredInputs(node, wrtIn);
594 
595  // if there are no inputs, it is an input node, so set the Jacobian to the identity
596  if(requiredIns.size()==0){
597  jacMap[nodeID][0] = Eigen::MatrixXd::Identity(nodePiece->outputSizes(0), nodePiece->outputSizes(0));
598  assert(jacMap[nodeID].size()==1);
599  }else{
600 
601  // the inputs to this WorkPiece
602  const ref_vector<Eigen::VectorXd>& ins = GetNodeInputs(node);
603 
604  // compute the jacobian of each required output wrt each input
605  for( auto out : requiredOuts ) {
606  // To compute the Jacobian of out, we need to add the add the combination from each input
607  for( auto in : requiredIns )
608  jacMap[nodeID][out] += nodePiece->Jacobian(out, std::get<2>(in), ins) * jacMap[std::get<0>(in)][std::get<1>(in)];
609  }
610  }
611 
612  } // loop over run order
613 
614  // set the Jacobian for this WorkPiece
615  jacobian = jacMap[outputID][wrtOut];
616 }
617 //
618 // void WorkGraphPiece::JacobianActionImpl(unsigned int const wrtIn, unsigned int const wrtOut, boost::any const& vec, ref_vector<boost::any> const& inputs) {
619 // // set the inputs
620 // SetInputs(inputs);
621 //
622 // // fill the map from the WorkPiece ID to its outputs
623 // FillOutputMap();
624 //
625 // // a map from the WorkPiece ID a map from the output number to the action of the jacobian of that output wrt the specified input
626 // std::map<unsigned int, std::map<unsigned int, boost::any> > jacActionMap;
627 //
628 // // loop through each downstream node
629 // for( auto node : boost::adaptors::reverse(derivRunOrders[wrtIn]) ) {
630 // // the ID of the current node
631 // const unsigned int nodeID = filtered_graphs[wrtIn]->operator[](node)->piece->ID();
632 //
633 // // get the outputs of this node that depend on the specified input
634 // const std::vector<std::tuple<unsigned int, unsigned int, unsigned int> >& requiredOutNodes = RequiredOutputs(node, wrtIn, wrtOut);
635 // // remove duplicates
636 // std::vector<unsigned int> requiredOuts;
637 // requiredOuts.reserve(requiredOutNodes.size());
638 // for( auto out : requiredOutNodes ) {
639 // auto it = std::find(requiredOuts.begin(), requiredOuts.end(), std::get<1>(out));
640 // if( it==requiredOuts.end() ) {
641 // requiredOuts.push_back(std::get<1>(out));
642 // }
643 // }
644 //
645 // // get the inputs for this node --- the input WorkPiece ID, the output number, and the input number
646 // const std::vector<std::tuple<unsigned int, unsigned int, unsigned int> >& requiredIns = RequiredInputs(node, wrtIn);
647 //
648 // // the inputs to this WorkPiece
649 // const ref_vector<boost::any>& ins = Inputs(node);
650 //
651 // // compute the jacobian of each required output wrt each input
652 // for( auto out : requiredOuts ) {
653 // if( requiredIns.size()==0 ) {
654 // // if there are no inputs, it is the input so set the Jacobian to the identity
655 // jacActionMap[nodeID][out] = vec;
656 // } else {
657 // // initize the jacobian to nothing
658 // jacActionMap[nodeID][out] = boost::none;
659 //
660 // for( auto in : requiredIns ) {
661 // // compute the Jacobian with respect to each required input
662 // graph->operator[](node)->piece->JacobianAction(std::get<2>(in), out, jacActionMap[std::get<0>(in)][std::get<1>(in)], ins);
663 //
664 // // use chain rule to get the jacobian wrt to the required input
665 // jacActionMap[nodeID][out] = algebra->Add(jacActionMap[nodeID][out], *(graph->operator[](node)->piece->jacobianAction));
666 // }
667 // }
668 // }
669 // }
670 //
671 // // set the action of the Jacobian for this WorkPiece
672 // jacobianAction = jacActionMap[outputID][wrtOut];
673 // }
674 //
675 // void WorkGraphPiece::JacobianTransposeActionImpl(unsigned int const wrtIn, unsigned int const wrtOut, boost::any const& vec, ref_vector<boost::any> const& inputs) {
676 // // set the inputs
677 // SetInputs(inputs);
678 //
679 // // fill the map from the WorkPiece ID to its outputs
680 // FillOutputMap();
681 //
682 // // a map from the WorkPiece ID a map from the output number to the action of the jacobian of that output wrt the specified input
683 // std::map<unsigned int, std::map<unsigned int, boost::any> > jacTransActionMap;
684 //
685 // // loop through each downstream node
686 // for( auto node : derivRunOrders[wrtIn] ) {
687 // // the ID of the current node
688 // const unsigned int nodeID = filtered_graphs[wrtIn]->operator[](node)->piece->ID();
689 //
690 // // get the outputs of this node that depend on the specified input
691 // const std::vector<std::tuple<unsigned int, unsigned int, unsigned int> >& requiredOuts = RequiredOutputs(node, wrtIn, wrtOut);
692 //
693 // // get the inputs for this node --- the input WorkPiece ID, the output number, and the input number
694 // const std::vector<std::tuple<unsigned int, unsigned int, unsigned int> >& requiredIns = RequiredInputs(node, wrtIn);
695 //
696 // // the inputs to this WorkPiece
697 // const ref_vector<boost::any>& ins = Inputs(node);
698 //
699 // for( auto in : requiredIns ) {
700 // if( nodeID==outputID ) {
701 // assert(requiredOuts.size()==1);
702 // assert(std::get<1>(requiredOuts[0])==wrtOut);
703 //
704 // // compute the Jacobian transpose action of the output node
705 // graph->operator[](node)->piece->JacobianTransposeAction(std::get<2>(in), wrtOut, vec, ins);
706 // jacTransActionMap[nodeID][std::get<2>(in)] = *(graph->operator[](node)->piece->jacobianTransposeAction);
707 // } else {
708 // // initialize the jacobian transpose action to nothing
709 // jacTransActionMap[nodeID][std::get<2>(in)] = boost::none;
710 //
711 // // loop through the outputs
712 // for( auto out : requiredOuts ) {
713 // // compute the jacobian transpose action for this output
714 // graph->operator[](node)->piece->JacobianTransposeAction(std::get<2>(in), std::get<1>(out), jacTransActionMap[std::get<0>(out)][std::get<2>(out)], ins);
715 // // add it (chain rule)
716 // jacTransActionMap[nodeID][std::get<2>(in)] = algebra->Add(jacTransActionMap[nodeID][std::get<2>(in)], *(graph->operator[](node)->piece->jacobianTransposeAction));
717 // }
718 // }
719 // }
720 //
721 // // if this is the input node
722 // if( requiredIns.size()==0 ) {
723 // // loop though the outputs
724 // for( auto out : requiredOuts ) {
725 // if( jacobianTransposeAction ) { // if the jacobian transpose action has not be initilized ...
726 // // it is equal to the action of the output
727 // *jacobianTransposeAction = algebra->Add(*jacobianTransposeAction, jacTransActionMap[std::get<0>(out)][std::get<1>(out)]);
728 // } else {
729 // // add it to the existing jacobian transpose action (chain rule)
730 // jacobianTransposeAction = jacTransActionMap[std::get<0>(out)][std::get<1>(out)];
731 // }
732 // }
733 // }
734 // }
735 // }
736 
738  // get the inputs and set them to the ConstantPiece nodes
739  assert(inputs.size()==constantPieces.size());
740  for( unsigned int i=0; i<inputs.size(); ++i ) {
741  constantPieces[i]->SetValue(inputs.at(i));
742  }
743 }
744 
745 std::map<unsigned int, std::vector<std::pair<unsigned int, unsigned int> > > ModGraphPiece::InputNodes(boost::graph_traits<Graph>::vertex_descriptor const& node) const {
746  // the map of input nodes
747  std::map<unsigned int, std::vector<std::pair<unsigned int, unsigned int> > > inMap;
748 
749  // loop though the input nodes
750  boost::graph_traits<Graph>::in_edge_iterator e, e_end;
751  for( tie(e, e_end)=boost::in_edges(node, wgraph->graph); e!=e_end; ++e ) {
752  // get the WorkPiece id number, the output that it supplies, and the input that receives it
753  const unsigned int id = wgraph->graph[boost::source(*e, wgraph->graph)]->piece->ID();
754  const unsigned int inNum = wgraph->graph[*e]->inputDim;
755  const unsigned int outNum = wgraph->graph[*e]->outputDim;
756 
757  // try to find the WorkPiece in the other upstream nodes
758  auto it = inMap.find(id);
759 
760  if( it==inMap.end() ) { // if we have not yet needed this WorkPiece ...
761  // ... add it to the list and store the input/output pair
762  inMap[id] = std::vector<std::pair<unsigned int, unsigned int> >(1, std::pair<unsigned int, unsigned int>(inNum, outNum));
763  } else { // we have needed this WorkPiece
764  // ... add the input/output pair
765  inMap[id].push_back(std::pair<unsigned int, unsigned int>(inNum, outNum));
766  }
767  }
768 
769  return inMap;
770 }
771 
773  // clear the map
774  valMap.clear();
775 
776  // loop over the run order
777  for( auto it : runOrder ) {
778 
779  // the inputs to this WorkPiece
781 
782  // evaluate the current map and store a vector of references to its output
783  auto wPiece = wgraph->GetPiece(it);
784  auto currPiece = std::dynamic_pointer_cast<ModPiece>(wPiece);
785 
786  if(!currPiece){
787 
788  // If it can't be cast to a ModPiece, check to see if the output can be cast to an Eigen vector
790  std::vector<boost::any> anyIns(ins.size());
791  for(int i=0; i<ins.size(); ++i)
792  anyIns.at(i) = boost::any(ins.at(i));
793 
794  std::vector<boost::any> const& anyOut = wPiece->Evaluate(anyIns);
795 
796  for(int i=0; i<anyOut.size(); ++i){
797  Eigen::VectorXd const& temp = AnyConstCast(anyOut.at(i));
798  output.push_back(std::cref(temp));
799  }
800  valMap[wgraph->GetPiece(it)->ID()] = output;
801 
802  }else{
803  assert(currPiece);
804  valMap[wgraph->GetPiece(it)->ID()] = ToRefVector(currPiece->Evaluate(ins));
805  }
806  }
807 }
808 
809 ref_vector<Eigen::VectorXd> ModGraphPiece::GetNodeInputs(boost::graph_traits<Graph>::vertex_descriptor node) const {
810 
811  // how many inputs does this node require?
812  const int numIns = wgraph->GetPiece(node)->numInputs;
813 
814  // get the inputs for this node
815  const std::map<unsigned int, std::vector<std::pair<unsigned int, unsigned int> > >& inMap = InputNodes(node);
816 
817  Eigen::VectorXd empty;
818  ref_vector<Eigen::VectorXd> ins(numIns, std::cref(empty));
819 
820  // loop through the edges again, now we know which outputs supply which inputs
821  for( auto edge : inMap ) {
822  // loop over the input/output pairs supplied by this input
823  for( auto in_out : edge.second ) {
824  ins[in_out.first] = valMap.at(edge.first)[in_out.second];
825  }
826  }
827 
828  return ins;
829 }
830 
831 std::vector<std::tuple<unsigned int, unsigned int, unsigned int> > ModGraphPiece::RequiredOutputs(boost::graph_traits<FilteredGraph>::vertex_descriptor const& node, unsigned int const wrtIn, unsigned int const wrtOut) const {
832  // the ID of the current node
833  const unsigned int nodeID = filtered_graphs[wrtIn]->operator[](node)->piece->ID();
834 
835  // get the outputs of this node that depend on the specified input
836  std::vector<std::tuple<unsigned int, unsigned int, unsigned int> > requiredOuts;
837 
838  if( nodeID==outputID ) { // if it is the output node ...
839  // ... the user specifies the output derivative
840  requiredOuts.push_back(std::tuple<unsigned int, unsigned int, unsigned int>(nodeID, wrtOut, wrtIn));
841 
842  return requiredOuts;
843  }
844 
845  // loop though the output nodes
846  boost::graph_traits<FilteredGraph>::out_edge_iterator eout, eout_end;
847  for( tie(eout, eout_end)=boost::out_edges(node, *filtered_graphs[wrtIn]); eout!=eout_end; ++eout ) {
848  // get the output number
849  const unsigned int id = wgraph->GetPiece(boost::target(*eout, *filtered_graphs[wrtIn]))->ID();
850  const unsigned int outNum = filtered_graphs[wrtIn]->operator[](*eout)->outputDim;
851  const unsigned int inNum = filtered_graphs[wrtIn]->operator[](*eout)->inputDim;
852 
853  // if we have not already required this output, save it
854  auto it = std::find(requiredOuts.begin(), requiredOuts.end(), std::tuple<unsigned int, unsigned int, unsigned int>(id, outNum, inNum));
855  if( it==requiredOuts.end() ) {
856  requiredOuts.push_back(std::tuple<unsigned int, unsigned int, unsigned int>(id, outNum, inNum));
857  }
858  }
859 
860  return requiredOuts;
861 }
862 
863 std::vector<std::tuple<unsigned int, unsigned int, unsigned int> > ModGraphPiece::RequiredInputs(boost::graph_traits<FilteredGraph>::vertex_descriptor const& node, unsigned int const wrtIn) const {
864  // how many inputs does this node require?
865  const int numIns = filtered_graphs[wrtIn]->operator[](node)->piece->numInputs;
866 
867  std::vector<std::tuple<unsigned int, unsigned int, unsigned int> > requiredIns;
868  requiredIns.reserve(numIns);
869 
870  // loop though the output nodes
871  boost::graph_traits<FilteredGraph>::in_edge_iterator ein, ein_end;
872  for( tie(ein, ein_end)=boost::in_edges(node, *filtered_graphs[wrtIn]); ein!=ein_end; ++ein ) {
873  // get the WorkPiece id number, the output that it supplies, and the input that receives it
874  const unsigned int id = wgraph->GetPiece(boost::source(*ein, *filtered_graphs[wrtIn]))->ID();
875  const unsigned int outNum = filtered_graphs[wrtIn]->operator[](*ein)->outputDim;
876  const unsigned int inNum = filtered_graphs[wrtIn]->operator[](*ein)->inputDim;
877 
878  // store the requried input
879  requiredIns.push_back(std::tuple<unsigned int, unsigned int, unsigned int>(id, outNum, inNum));
880  }
881 
882  return requiredIns;
883 }
884 
885 std::shared_ptr<ModGraphPiece> ModGraphPiece::GetSubModel(std::string const& nodeName) const
886 {
887 
888  // Create a new workgraph wit the nodes necessary to evaluate nodeName
889  auto subGraph = wgraph->Clone();
890  for(auto piece : constantPieces)
891  subGraph->RemoveNode(wgraph->GetName(piece));
892 
893  return subGraph->CreateModPiece(nodeName);
894 }
895 
896 std::vector<int> ModGraphPiece::MatchInputs(std::shared_ptr<ModGraphPiece> otherPiece) const
897 {
898 
899  std::vector<std::shared_ptr<ConstantVector>> otherIns = otherPiece->GetConstantPieces();
900  std::shared_ptr<WorkGraph> otherGraph = otherPiece->GetGraph();
901 
902  std::vector<int> outputs(otherIns.size());
903 
904  for(int i=0; i<otherIns.size(); ++i)
905  {
906  // get the downstream node and input index corresponding to this constant piece
907  std::string constName = otherGraph->GetName( otherIns.at(i) );
908  std::string sharedName = otherGraph->GetChildren( constName ).at(0);
909 
910  // Now try to find the same node and input in *this graph
911  if(wgraph->HasNode(sharedName)){
912  int inputIndex = otherGraph->GetEdges( constName, sharedName ).at(0).second;
913 
914  std::string upstreamName = wgraph->GetParent( sharedName, inputIndex);
915  assert(upstreamName.size()>0);
916 
917  // Now, get the parent piece and check it against all of the constant pieces
918  auto iter = std::find(constantPieces.begin(), constantPieces.end(), wgraph->GetPiece(upstreamName));
919  if(iter != constantPieces.end()){
920  outputs.at(i) = std::distance(constantPieces.begin(), iter);
921  }else{
922  outputs.at(i) = -1;
923  }
924 
925  }else{
926  outputs.at(i) = -1;
927  }
928  }
929 
930  return outputs;
931 }
Determine if the source of an edge is downstream of an input.
This class keeps track of which nodes are downstream of a specified input.
std::unordered_map< unsigned int, ref_vector< Eigen::VectorXd > > valMap
A the map from each node's muq::Modeling::WorkPiece::ID to its outputs.
virtual void ApplyHessianImpl(unsigned int const outWrt, unsigned int const inWrt1, unsigned int const inWrt2, ref_vector< Eigen::VectorXd > const &input, Eigen::VectorXd const &sens, Eigen::VectorXd const &vec) override
virtual void JacobianImpl(unsigned int const outputDimWrt, unsigned int const inputDimWrt, ref_vector< Eigen::VectorXd > const &input) override
Compute the Jacobian for this muq::Modeling::WorkGraphPiece using the chain rule.
void SetInputs(ref_vector< Eigen::VectorXd > const &inputs)
Set the inputs.
unsigned int outputID
The ID of the WorkPiece corresponding to the output node.
std::vector< std::tuple< unsigned int, unsigned int, unsigned int > > RequiredOutputs(boost::graph_traits< FilteredGraph >::vertex_descriptor const &node, unsigned int const wrtIn, unsigned int wrtOut) const
Get the required outputs for a node in one of the filtered graphs.
virtual void EvaluateImpl(ref_vector< Eigen::VectorXd > const &inputs) override
Evaluate each muq::Modeling::WorkPiece in the graph.
std::vector< std::shared_ptr< ConstantVector > > constantPieces
The muq::Modeling::ConstantVector's that store the inputs.
void FillOutputMap()
Fill the map from each node's muq::Modeling::WorkPiece::ID to its outputs.
std::shared_ptr< WorkGraph > wgraph
The WorkGraph associated with this WorkGraphPiece.
virtual void GradientImpl(unsigned int const outputDimWrt, unsigned int const inputDimWrt, ref_vector< Eigen::VectorXd > const &input, Eigen::VectorXd const &sensitivity) override
Compute the action of the Jacobian transpose for this muq::Modeling::WorkGraphPiece using the chain r...
int GetInputIndex(std::shared_ptr< WorkPiece > const &piece) const
std::shared_ptr< ModGraphPiece > JacobianGraph(unsigned int const outputDimWrt, unsigned int const inputDimWrt)
std::map< std::pair< unsigned int, unsigned int >, std::shared_ptr< ModGraphPiece > > gradientPieces
Definition: ModGraphPiece.h:97
std::deque< boost::graph_traits< Graph >::vertex_descriptor > runOrder
Run order computed during construction (input->output order)
static Eigen::VectorXi ConstructInputSizes(std::vector< std::shared_ptr< ConstantVector > > const &constantPiecesIn)
std::vector< std::tuple< unsigned int, unsigned int, unsigned int > > RequiredInputs(boost::graph_traits< FilteredGraph >::vertex_descriptor const &node, unsigned int const wrtIn) const
Get the required inputs for a node in one of the filtered graphs.
std::map< std::pair< unsigned int, unsigned int >, std::shared_ptr< ModGraphPiece > > jacobianPieces
std::vector< std::deque< boost::graph_traits< Graph >::vertex_descriptor > > adjointRunOrders
ref_vector< Eigen::VectorXd > GetNodeInputs(boost::graph_traits< Graph >::vertex_descriptor node) const
Get the inputs from muq::Modeling::WorkGraphPiece::valMap to a specified node in the graph.
virtual void ApplyJacobianImpl(unsigned int const outputDimWrt, unsigned int const inputDimWrt, ref_vector< Eigen::VectorXd > const &input, Eigen::VectorXd const &vec) override
Compute the action of the Jacobian for this muq::Modeling::WorkGraphPiece using the chain rule.
std::shared_ptr< ModGraphPiece > GetSubModel(std::string const &nodeName) const
Returns another ModGraphPiece containing only part of the graph making up this model.
std::vector< int > MatchInputs(std::shared_ptr< ModGraphPiece > otherPiece) const
Matches inputs with another ModGraphPiece by looking at node names and edges.
std::shared_ptr< ModGraphPiece > GradientGraph(unsigned int const outputDimWrt, unsigned int const inputDimWrt)
std::map< unsigned int, std::vector< std::pair< unsigned int, unsigned int > > > InputNodes(boost::graph_traits< Graph >::vertex_descriptor const &node) const
Get a the input nodes for a node.
ModGraphPiece(std::shared_ptr< WorkGraph > graph, std::vector< std::shared_ptr< ConstantVector > > const &constantPieces, std::vector< std::string > const &inputNames, std::shared_ptr< ModPiece > outputNode)
Construct a muq::Modeling::ModGraphPiece.
std::vector< std::shared_ptr< FilteredGraph > > filtered_graphs
std::map< std::tuple< unsigned int, unsigned int, unsigned int >, std::shared_ptr< ModGraphPiece > > hessianPieces
Provides an abstract interface for defining vector-valued model components.
Definition: ModPiece.h:148
Eigen::MatrixXd jacobian
Definition: ModPiece.h:506
const Eigen::VectorXi inputSizes
Definition: ModPiece.h:469
std::vector< Eigen::VectorXd > outputs
Definition: ModPiece.h:503
Eigen::VectorXd gradient
Definition: ModPiece.h:504
const Eigen::VectorXi outputSizes
Definition: ModPiece.h:472
Eigen::VectorXd hessAction
Definition: ModPiece.h:507
Eigen::VectorXd jacobianAction
Definition: ModPiece.h:505
A graph of connected muq::Modeling::WorkPiece's.
Definition: WorkGraph.h:20
void AddNode(std::shared_ptr< WorkPiece > input, std::string const &name)
Add a new node to the graph.
Definition: WorkGraph.cpp:195
void AddEdge(std::string const &nameFrom, unsigned int const outputDim, std::string const &nameTo, unsigned int const inputDim)
Add a new edge to the graph.
Definition: WorkGraph.cpp:206
Graph graph
The directed graph that represents this muq::Modeling::Core::WorkGraph.
Definition: WorkGraph.h:245
std::shared_ptr< ModGraphPiece > CreateModPiece(std::string const &node, std::vector< std::string > const &inNames=std::vector< std::string >()) const
Create a muq::Modeling::ModPiece whose output matches a given node.
Definition: WorkGraph.cpp:591
bool HasNode(std::string const &name) const
Is the given node in the graph?
Definition: WorkGraph.cpp:177
const unsigned int id
A unique ID number assigned by the constructor.
Definition: WorkPiece.h:580
static ref_vector< const boost::any > ToRefVector(std::vector< boost::any > const &anyVec)
Create vector of references from a vector of boost::any's.
Definition: WorkPiece.cpp:675
unsigned int ID() const
Get the unique ID number.
Definition: WorkPiece.cpp:651
std::string name
A unique name for this WorkPiece. Defaults to <ClassName>_<id>
Definition: WorkPiece.h:583
int numInputs
The number of inputs.
Definition: WorkPiece.h:501
std::vector< std::reference_wrapper< const T > > ref_vector
A vector of references to something ...
Definition: WorkPiece.h:37
int int diyfp diyfp v
Definition: json.h:15163
A helper struct that determines if a node in the graph has a given name.