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