1 #include "MUQ/Utilities/MultiIndices/MultiIndexSet.h"
2 
3 #include <algorithm>
4 
5 using namespace muq::Utilities;
6 using namespace std;
7 
operator +=(std::shared_ptr<MultiIndexSet> x,std::shared_ptr<MultiIndexSet> y)8 std::shared_ptr<MultiIndexSet> muq::Utilities::operator+=( std::shared_ptr<MultiIndexSet> x,
9                                                            std::shared_ptr<MultiIndexSet> y)
10 {
11   (*x)+=(*y);
12   return x;
13 }
14 
operator +=(std::shared_ptr<MultiIndexSet> x,std::shared_ptr<MultiIndex> y)15 std::shared_ptr<MultiIndexSet> muq::Utilities::operator+=( std::shared_ptr<MultiIndexSet> x,
16                                                            std::shared_ptr<MultiIndex>    y)
17 {
18   (*x)+=y;
19   return x;
20 }
21 
MultiIndexSet(const unsigned dimIn,shared_ptr<MultiIndexLimiter> limiterIn)22 MultiIndexSet::MultiIndexSet(const unsigned dimIn,
23                              shared_ptr<MultiIndexLimiter> limiterIn) : maxOrders(Eigen::VectorXi::Zero(dimIn)),
24                                                                         dim(dimIn),
25                                                                         limiter(limiterIn)
26 {
27 };
28 
SetLimiter(std::shared_ptr<MultiIndexLimiter> const & limiterIn)29 void MultiIndexSet::SetLimiter(std::shared_ptr<MultiIndexLimiter> const& limiterIn){
30 
31   // first, make sure no active terms in the set currently obey the new limiter.
32   //  If a term is inactive, remove all edges tied to it
33   for(int globalInd=0; globalInd<allMultis.size(); ++globalInd)
34   {
35     if(IsActive(globalInd)){
36       if(!limiterIn->IsFeasible(allMultis.at(globalInd))){
37         std::stringstream msg;
38         msg << "Invalid limiter passed to MultiIndexSet::SetLimiter.  The active multi-index, ";
39         msg << allMultis.at(globalInd)->GetVector() << ", is not valid with the new limiter.\n";
40         throw std::invalid_argument(msg.str());
41       }
42     }else{
43 
44       if(!limiterIn->IsFeasible(allMultis.at(globalInd))){
45         for(int inNode : inEdges[globalInd])
46           outEdges[inNode].erase(globalInd);
47         inEdges[globalInd].clear();
48       }
49     }
50   }
51 
52   // copy the limiter
53   limiter = limiterIn;
54 }
55 
CloneExisting(std::shared_ptr<MultiIndexSet> const & original)56 std::shared_ptr<MultiIndexSet> MultiIndexSet::CloneExisting(std::shared_ptr<MultiIndexSet> const& original)
57 {
58   auto output = make_shared<MultiIndexSet>(original->dim, original->limiter);
59 
60   output->active2global  = original->active2global;
61   output->outEdges       = original->outEdges;
62   output->inEdges        = original->inEdges;
63   output->maxOrders      = original->maxOrders;
64   output->allMultis      = original->allMultis;
65 
66   return output;
67 }
68 
69 // Eigen::MatrixXu MultiIndexSet::GetAllMultiIndices() const
70 // {
71 //
72 //   Eigen::MatrixXu output(active2local.size(), dim);
73 //   for(int i=0; i<active2local.size(); ++i){
74 //     auto multi = pool->at(local2global[active2local[i]]);
75 //     int multiDim = multi->GetDimension();
76 //     output.row(i).head(multiDim) = multi->GetMulti();
77 //   }
78 //
79 //   return output;
80 // }
81 
MultiToIndex(std::shared_ptr<MultiIndex> const & input) const82 int MultiIndexSet::MultiToIndex(std::shared_ptr<MultiIndex> const& input) const{
83 
84   auto localIter = multi2global.find(input);
85 
86   if(localIter!=multi2global.end()){
87     return global2active[localIter->second];
88   }else{
89     return -1;
90   }
91 }
92 
93 
AddMulti(std::shared_ptr<MultiIndex> const & newMulti)94 int MultiIndexSet::AddMulti(std::shared_ptr<MultiIndex> const& newMulti)
95 {
96   allMultis.push_back(newMulti);
97 
98   int globalInd = allMultis.size() - 1;
99   multi2global[newMulti] = globalInd;
100 
101   global2active.push_back(-1);
102 
103   inEdges.push_back(std::set<int>());
104   outEdges.push_back(std::set<int>());
105 
106   assert(allMultis.size() == global2active.size());
107 
108   AddForwardNeighbors(globalInd,false);
109   AddBackwardNeighbors(globalInd, false);
110 
111   return globalInd;
112 }
113 
114 ///////////////////////////
115 //////// FINISHED UP HERE
116 ///////////////////////////
AddActive(std::shared_ptr<MultiIndex> const & newNode)117 int MultiIndexSet::AddActive(std::shared_ptr<MultiIndex> const& newNode)
118 {
119   int globalInd = AddInactive(newNode);
120 
121   if(globalInd>=0){
122 
123     Activate(globalInd);
124     return global2active[globalInd];
125 
126   }else{
127     return -1;
128   }
129 }
130 
131 
132 
AddInactive(std::shared_ptr<MultiIndex> const & newNode)133 int MultiIndexSet::AddInactive(std::shared_ptr<MultiIndex> const& newNode)
134 {
135   auto iter = multi2global.find(newNode);
136 
137   if(iter!=multi2global.end()){
138 
139     return iter->second;
140 
141   }else if(limiter->IsFeasible(newNode)){
142 
143     return AddMulti(newNode);
144 
145   }else{
146 
147     return -1;
148   }
149 }
150 
IsActive(std::shared_ptr<MultiIndex> const & multiIndex) const151 bool MultiIndexSet::IsActive(std::shared_ptr<MultiIndex> const& multiIndex) const
152 {
153   auto iter = multi2global.find(multiIndex);
154 
155   if(iter!=multi2global.end()){
156     return IsActive(iter->second);
157   }else{
158     return false;
159   }
160 }
161 
IsActive(unsigned int globalIndex) const162 bool MultiIndexSet::IsActive(unsigned int globalIndex) const
163 {
164   return global2active[globalIndex] >= 0;
165 }
166 
IsAdmissible(unsigned int globalIndex) const167 bool MultiIndexSet::IsAdmissible(unsigned int globalIndex) const
168 {
169   auto& multi = allMultis.at(globalIndex);
170 
171   if(!limiter->IsFeasible(multi))
172     return false;
173 
174   if(IsActive(globalIndex))
175     return true;
176 
177   // count the number of input edges that are coming from active indices
178   int numAdmiss = 0;
179   for(int inNode : inEdges.at(globalIndex)){
180     if(IsActive(inNode))
181       numAdmiss++;
182   }
183 
184   if(numAdmiss==multi->nzInds.size()){
185     return true;
186   }else{
187     return false;
188   }
189 }
190 
IsAdmissible(std::shared_ptr<MultiIndex> const & multiIndex) const191 bool MultiIndexSet::IsAdmissible(std::shared_ptr<MultiIndex> const& multiIndex) const
192 {
193   auto iter = multi2global.find(multiIndex);
194   if(iter==multi2global.end()){
195     return false;
196   }else{
197     return IsAdmissible(iter->second);
198   }
199 }
200 
201 
IsExpandable(unsigned int activeIndex) const202 bool MultiIndexSet::IsExpandable(unsigned int activeIndex) const
203 {
204   // an index is expandable when at least one forward neighbor is admissible but not active (i.e. outedge)
205 
206   // loop through the outgoing edges for this node
207   for(int nextInd : outEdges[active2global.at(activeIndex)]){
208     if(!IsActive(nextInd)&&IsAdmissible(nextInd))
209       return true;
210   }
211   return false;
212 }
213 
Activate(int globalIndex)214 void MultiIndexSet::Activate(int globalIndex)
215 {
216 
217   // the index is already in the global set, if the value is non-negative, it is also active and we don't need to do anything
218   if(global2active.at(globalIndex)<0)
219   {
220     auto& newNode = allMultis.at(globalIndex);
221 
222     // now add the index to the active set
223     active2global.push_back(globalIndex);
224 
225     int newActiveInd = active2global.size()-1;
226 
227     global2active.at(globalIndex) = newActiveInd;
228 
229     // update the maximum order
230     for(auto pair : newNode->nzInds)
231       maxOrders(pair.first) = std::max<unsigned>(maxOrders(pair.first),pair.second);
232 
233     AddForwardNeighbors(globalIndex,true);
234     AddBackwardNeighbors(globalIndex,true);
235   }
236 }
237 
Activate(std::shared_ptr<MultiIndex> const & multiIndex)238 void MultiIndexSet::Activate(std::shared_ptr<MultiIndex> const& multiIndex)
239 {
240   auto iter = multi2global.find(multiIndex);
241 
242   assert(iter!=multi2global.end());
243   assert(IsAdmissible(iter->second));
244 
245   Activate(iter->second);
246 }
247 
AddForwardNeighbors(unsigned int globalIndex,bool addInactive)248 void MultiIndexSet::AddForwardNeighbors(unsigned int globalIndex, bool addInactive)
249 {
250 
251   Eigen::RowVectorXi base = allMultis.at(globalIndex)->GetVector();
252 
253   shared_ptr<MultiIndex> newNode;
254   for(unsigned int i=0; i<base.size(); ++i)
255   {
256     base(i)++;
257 
258     newNode = make_shared<MultiIndex>(base);
259 
260     if(limiter->IsFeasible(newNode)){
261 
262       auto iter = multi2global.find(newNode);
263 
264       if(iter!=multi2global.end()){
265         inEdges.at(iter->second).insert(globalIndex);
266         outEdges.at(globalIndex).insert(iter->second);
267       }else if(addInactive){
268         AddInactive(newNode);
269       }
270     }
271     base(i)--;
272   }
273 }
274 
GetAdmissibleForwardNeighbors(unsigned int activeIndex)275 std::vector<shared_ptr<MultiIndex>>  MultiIndexSet::GetAdmissibleForwardNeighbors(unsigned int activeIndex)
276 {
277   unsigned int globalInd = active2global.at(activeIndex);
278 
279   vector<shared_ptr<MultiIndex>> output;
280   for( auto neighbor : outEdges[globalInd])
281   {
282     if(IsAdmissible(neighbor))
283       output.push_back(allMultis.at(neighbor));
284   }
285 
286   return output;
287 }
288 
GetFrontier() const289 std::vector<unsigned int> MultiIndexSet::GetFrontier() const {
290 
291   std::vector<unsigned int> frontierInds;
292 
293   for(unsigned int activeInd = 0; activeInd<active2global.size(); ++activeInd) {
294     if(IsExpandable(activeInd))
295       frontierInds.push_back(activeInd);
296   }
297 
298   return frontierInds;
299 }
300 
GetStrictFrontier() const301 std::vector<unsigned int> MultiIndexSet::GetStrictFrontier() const
302 {
303   std::vector<unsigned int> frontierInds;
304 
305   for(unsigned int activeInd = 0; activeInd<active2global.size(); ++activeInd) {
306     // loop over all the forward neighbors
307     unsigned int globalInd = active2global.at(activeInd);
308 
309     bool isStrict = true;
310     for( auto neighbor : outEdges[globalInd])
311       isStrict = isStrict && (!IsActive(neighbor));
312 
313     if(isStrict)
314       frontierInds.push_back(activeInd);
315   }
316 
317   return frontierInds;
318 }
319 
GetBackwardNeighbors(unsigned int activeIndex) const320 std::vector<unsigned int> MultiIndexSet::GetBackwardNeighbors(unsigned int activeIndex) const
321 {
322   unsigned int globalInd = active2global.at(activeIndex);
323 
324   std::vector<unsigned int> output;
325   for(auto neighbor : inEdges[globalInd])
326     output.push_back(global2active.at(neighbor));
327 
328   return output;
329 }
330 
GetBackwardNeighbors(std::shared_ptr<MultiIndex> const & multiIndex) const331 std::vector<unsigned int> MultiIndexSet::GetBackwardNeighbors(std::shared_ptr<MultiIndex> const& multiIndex) const
332 {
333   auto iter = multi2global.find(multiIndex);
334 
335   assert(iter!=multi2global.end());
336 
337   unsigned int globalInd = iter->second;
338   std::vector<unsigned int> output;
339   for(auto neighbor : inEdges[globalInd])
340     output.push_back(global2active.at(neighbor));
341 
342   return output;
343 }
344 
345 
NumActiveForward(unsigned int activeInd) const346 unsigned int MultiIndexSet::NumActiveForward(unsigned int activeInd) const
347 {
348   unsigned int globalInd = active2global.at(activeInd);
349 
350   unsigned int numActive = 0;
351   for( auto neighbor : outEdges[globalInd])
352   {
353     if(IsActive(neighbor))
354       numActive++;
355   }
356   return numActive;
357 }
358 
NumForward(unsigned int activeInd) const359 unsigned int MultiIndexSet::NumForward(unsigned int activeInd) const
360 {
361   unsigned int globalInd = active2global.at(activeInd);
362   return outEdges[globalInd].size();
363 }
364 
AddBackwardNeighbors(unsigned int globalIndex,bool addInactive)365 void MultiIndexSet::AddBackwardNeighbors(unsigned int globalIndex, bool addInactive)
366 {
367   Eigen::RowVectorXi base = allMultis.at(globalIndex)->GetVector();
368 
369   shared_ptr<MultiIndex> newNode;
370   for(unsigned int i=0; i<base.size(); ++i)
371   {
372     if(base(i)>0){
373 
374       base(i)--;
375 
376       newNode = make_shared<MultiIndex>(base);
377 
378       if(limiter->IsFeasible(newNode)){
379         auto iter = multi2global.find(newNode);
380 
381         if(iter!=multi2global.end()){
382           outEdges.at(iter->second).insert(globalIndex);
383           inEdges.at(globalIndex).insert(iter->second);
384         }else if(addInactive){
385           AddInactive(newNode);
386         }
387       }
388       base(i)++;
389     }
390   }
391 }
392 
Expand(unsigned int activeIndex)393 std::vector<unsigned> MultiIndexSet::Expand(unsigned int activeIndex)
394 {
395   if(activeIndex >= active2global.size()){
396     std::stringstream msg;
397     msg << "Invalid index passed to MultiIndexSet::Expand.  A value of " << activeIndex << " was passed to the function, but only " << active2global.size() << " active components exist in the set.\n";
398     throw std::out_of_range(msg.str());
399   }
400 
401   vector<unsigned> newIndices;
402   unsigned globalIndex = active2global.at(activeIndex);
403 
404   // loop through the forward neighbors of this index
405   std::set<int> tempSet = outEdges.at(globalIndex);
406   for(int neighbor : tempSet)
407   {
408     if(IsAdmissible(neighbor)&&(!IsActive(neighbor))){
409       Activate(neighbor);
410       newIndices.push_back(global2active.at(neighbor));
411     }
412   }
413 
414   // return the vector of newly activated indices
415   return newIndices;
416 }
417 
ForciblyExpand(unsigned int const activeIndex)418 std::vector<unsigned> MultiIndexSet::ForciblyExpand(unsigned int const activeIndex)
419 {
420   assert(activeIndex<active2global.size());
421 
422   vector<unsigned> newIndices;
423   unsigned globalIndex = active2global.at(activeIndex);
424 
425   // loop through the forward neighbors of this index
426   std::set<int>& tempSet = outEdges.at(globalIndex);
427   for(int neighbor : tempSet)
428     ForciblyActivate(neighbor,newIndices);
429 
430   // return the vector of newly activated indices
431   return newIndices;
432 
433 }
434 
ForciblyActivate(int globalIndex,std::vector<unsigned> & newIndices)435 void MultiIndexSet::ForciblyActivate(int globalIndex, std::vector<unsigned> &newIndices){
436 
437 
438   if(!IsActive(globalIndex)){
439 
440     // make the node active and add inactive neighbors if necessary, this also updates the edges and enables the loop below
441     Activate(globalIndex);
442     newIndices.push_back(global2active.at(globalIndex));
443 
444     // now, fill in all of the previous neighbors
445     std::set<int>& tempSet = inEdges.at(globalIndex);
446     for(int ind : tempSet)
447       ForciblyActivate(ind,newIndices);
448 
449   }
450 }
451 
ForciblyActivate(std::shared_ptr<MultiIndex> const & multiIndex)452 std::vector<unsigned> MultiIndexSet::ForciblyActivate(std::shared_ptr<MultiIndex> const& multiIndex){
453 
454   assert(limiter->IsFeasible(multiIndex));
455 
456   auto iter = multi2global.find(multiIndex);
457   vector<unsigned int> newIndices;
458 
459   // if we found the multiindex and it is active, there is nothing to do
460   if(iter!=multi2global.end()){
461     ForciblyActivate(iter->second,newIndices);
462   }else{
463     // Add the new index as an active node
464     int newGlobalInd = AddInactive(multiIndex);
465     ForciblyActivate(newGlobalInd,newIndices);
466   }
467 
468   return newIndices;
469 }
470 
operator +=(const MultiIndexSet & rhs)471 MultiIndexSet& MultiIndexSet::operator+=(const MultiIndexSet& rhs)
472 {
473   Union(rhs);
474   return *this;
475 }
476 
Union(const MultiIndexSet & rhs)477 int MultiIndexSet::Union(const MultiIndexSet& rhs)
478 {
479   int oldTerms = Size();
480 
481   for(int i = 0; i < rhs.allMultis.size(); ++i) {
482 
483     auto newMulti = rhs.allMultis.at(i);
484     if(limiter->IsFeasible(newMulti)){
485       if(rhs.global2active[i]<0){
486         AddInactive(newMulti);
487       }else{
488         AddActive(newMulti);
489       }
490     }
491   }
492 
493   return Size() - oldTerms;
494 }
495 
operator +=(std::shared_ptr<MultiIndex> const & rhs)496 MultiIndexSet& MultiIndexSet::operator+=(std::shared_ptr<MultiIndex> const& rhs)
497 {
498   AddActive(rhs);
499   return *this;
500 }
501