libatomprobe
Library for Atom Probe Tomography (APT) computation
massTool.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2017 D Haley
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
19 
20 #include <cmath>
21 #include <limits>
22 #include <algorithm>
23 #include <numeric>
24 #include <utility>
25 #include <map>
26 
27 
28 #include "atomprobe/helper/misc.h"
30 
31 using std::vector;
32 using std::make_pair;
33 using std::map;
34 using std::pair;
35 
36 using namespace AtomProbe;
37 
38 //Holder that allows us to track position in weight stack when solving
39 // knapsack problem
41 {
42  public:
43  vector<Weight> curWeights,allowableWeights;
44  size_t offset;
46 
47 };
48 
49 //Simple template class to create a stack of known maximum size
50 template<class T>
52 {
53  T *array;
54  size_t curSize;
55 public:
56  FixedStack(size_t size);
57  FixedStack(const FixedStack &f); //Not implemented
58  ~FixedStack();
59 
60  T &top();
61  void pop();
62  void push(const T &t);
63 
64  bool empty() const {return !curSize;}
65 };
66 
67 template<class T>
69 {
70  array = new T[size];
71  curSize=0;
72 }
73 
74 template<class T>
75 void FixedStack<T>::push(const T &t)
76 {
77  curSize++;
78  array[curSize]=t;
79 }
80 
81 template<class T>
83 {
84  ASSERT(curSize);
85  curSize--;
86 }
87 
88 template<class T>
90 {
91  return array[curSize];
92 }
93 
94 template<class T>
96 {
97  delete[] array;
98 }
99 
100 void MassTool::preprocessKnapsack(vector<Weight> &weights, float totalWeight, float tolerance, unsigned int maxCombine)
101 {
102 
103  //Delete weights that cannot possibly be part of the solution
104  // * Weight is over the target + tolerance
105  vector<bool> badWeight(weights.size(),false);
106  for(size_t ui=0;ui<weights.size();ui++)
107  {
108  if(weights[ui].mass > totalWeight+tolerance)
109  badWeight[ui]=true;
110  }
111 
112 
113  vectorMultiErase(weights,badWeight);
114 
115 }
116 
117 void MassTool::bruteKnapsack(vector<Weight> &weights, float targetWeight,
118  float tolerance, unsigned int maxObjects, vector<vector<Weight> > &solutions)
119 {
120  using std::pair;
121 
122 
123  if(!weights.size())
124  return;
125 
126  //Reject weights that are not part of the solution, to reduce
127  // the search space
128  preprocessKnapsack(weights,targetWeight,tolerance,maxObjects);
129 
130  //Loop over all weights, adding objects until we are within |tolerant weight + targetweight|
131  const float upperWeight = targetWeight+tolerance;
132  const float lowerWeight = targetWeight-tolerance;
133 
134  std::sort(weights.begin(),weights.end());
135 
136  WEIGHT_SEARCH_ENTRY weightEntry;
137  weightEntry.offset=0;
138  weightEntry.cumulativeWeight=0;
139  weightEntry.allowableWeights=weights;
140 
141  FixedStack<WEIGHT_SEARCH_ENTRY> searchStack(maxObjects+1);
142  searchStack.push(weightEntry);
143 
144  //---
145 
146  while(!searchStack.empty())
147  {
148  vector<Weight> allowableWeights,curWeights;
149 
150  unsigned insertOffset;
151 
152  //Check the top of the stack
153  //--
154  insertOffset=searchStack.top().offset;
155 
156 
157  //Don't proceed if we have used all the child weights
158  if(insertOffset >=searchStack.top().allowableWeights.size())
159  {
160  //We have run out of allowable weights to use
161  searchStack.pop();
162  continue;
163  }
164 
165  //Tell current stack to move to next child
166  searchStack.top().offset++;
167 
168  //Copy the top of the stack
169  curWeights=searchStack.top().curWeights;
170  allowableWeights=searchStack.top().allowableWeights;
171 
172  //Insert a weight into the sack
173  float remainingWeight,currentWeight;
174  curWeights.push_back(allowableWeights[insertOffset]);
175  currentWeight=searchStack.top().cumulativeWeight +allowableWeights[insertOffset].mass;
176 
177  if( currentWeight <=upperWeight && currentWeight >=lowerWeight)
178  {
179  //TODO: Use solution tree, to more efficiently store possible solutions?
180  solutions.push_back(curWeights);
181  }
182 
183 
184  //Erase disallowed weights.
185  //the weights are sorted in increasing size, so just
186  // cut once we hit the right threshold
187  //--------
188  remainingWeight=upperWeight-currentWeight;
189  if(remainingWeight > 0)
190  {
191  for(size_t ui=0;ui<allowableWeights.size();ui++)
192  {
193  if(allowableWeights[ui].mass > remainingWeight)
194  {
195  allowableWeights.resize(ui+1);
196  break;
197  }
198  }
199  }
200  else
201  allowableWeights.clear();
202  //--------
203 
204  //Add the next weight to the stack, if we have not run out of number of objects
205  if(allowableWeights.size() && curWeights.size() != maxObjects)
206  {
207  weightEntry.curWeights.swap(curWeights);
208  weightEntry.allowableWeights.swap(allowableWeights);
209  weightEntry.cumulativeWeight=currentWeight;
210  //Search in decreasing weight only, to not generate duplicates
211  weightEntry.offset=insertOffset;
212  searchStack.push(weightEntry);
213  }
214 
215 
216  }
217 
218 }
219 
220 void MassTool::bruteKnapsack(vector<Weight> &weights, const vector<float> &targetWeight,
221  float tolerance, unsigned int maxObjects, vector<vector<Weight> > &solutions)
222 {
223  using std::pair;
224 
225 
226  if(weights.empty() || targetWeight.empty())
227  return;
228 
229  float maxWeight=*(std::max_element(targetWeight.begin(),targetWeight.end()));
230 
231  //Reject weights that are not part of the solution, to reduce
232  // the search space
233  preprocessKnapsack(weights,maxWeight,tolerance,maxObjects);
234 
235  //Loop over all weights, adding objects until we are within |tolerant weight + targetweight|
236  const float upperWeight = maxWeight+tolerance;
237 
238  std::sort(weights.begin(),weights.end());
239 
240  WEIGHT_SEARCH_ENTRY weightEntry;
241  weightEntry.offset=0;
242  weightEntry.cumulativeWeight=0;
243  weightEntry.allowableWeights=weights;
244 
245  FixedStack<WEIGHT_SEARCH_ENTRY> searchStack(maxObjects+1);
246  searchStack.push(weightEntry);
247 
248  //---
249 
250  while(!searchStack.empty())
251  {
252  vector<Weight> allowableWeights,curWeights;
253 
254  unsigned insertOffset;
255 
256  //Check the top of the stack
257  //--
258  insertOffset=searchStack.top().offset;
259 
260 
261  //Don't proceed if we have used all the child weights
262  if(insertOffset >=searchStack.top().allowableWeights.size())
263  {
264  //We have run out of allowable weights to use
265  searchStack.pop();
266  continue;
267  }
268 
269  //Tell current stack to move to next child
270  searchStack.top().offset++;
271 
272  //Copy the top of the stack
273  curWeights=searchStack.top().curWeights;
274  allowableWeights=searchStack.top().allowableWeights;
275 
276  //Insert a weight into the sack
277  float remainingWeight,currentWeight;
278  curWeights.push_back(allowableWeights[insertOffset]);
279  currentWeight=searchStack.top().cumulativeWeight +allowableWeights[insertOffset].mass;
280 
281  for(unsigned int ui=0;ui<targetWeight.size();ui++)
282  {
283  if( currentWeight <= targetWeight[ui]+tolerance &&
284  currentWeight >=targetWeight[ui]-tolerance)
285  {
286  //TODO: Use solution tree, to more efficiently store possible solutions?
287  solutions.push_back(curWeights);
288  }
289  }
290 
291 
292  //Erase disallowed weights.
293  //the weights are sorted in increasing size, so just
294  // cut once we hit the right threshold
295  //--------
296  remainingWeight=upperWeight-currentWeight;
297  if(remainingWeight > 0)
298  {
299  for(size_t ui=0;ui<allowableWeights.size();ui++)
300  {
301  if(allowableWeights[ui].mass > remainingWeight)
302  {
303  allowableWeights.resize(ui+1);
304  break;
305  }
306  }
307  }
308  else
309  allowableWeights.clear();
310  //--------
311 
312  //Add the next weight to the stack, if we have not run out of number of objects
313  if(allowableWeights.size() && curWeights.size() != maxObjects)
314  {
315  weightEntry.curWeights.swap(curWeights);
316  weightEntry.allowableWeights.swap(allowableWeights);
317  weightEntry.cumulativeWeight=currentWeight;
318  //Search in decreasing weight only, to not generate duplicates
319  weightEntry.offset=insertOffset;
320  searchStack.push(weightEntry);
321  }
322 
323 
324  }
325 
326 }
327 
328 #ifdef DEBUG
329 bool MassTool::runUnitTests()
330 {
331  vector<Weight> weights;
332  weights.push_back(1.0);
333  weights.push_back(3.0);
334  weights.push_back(10.0);
335 
336 
337  //Test the knapsack algorithm. Remember some input
338  // weights may be removed during this calculation
339  vector<vector<Weight> > solutions;
340  bruteKnapsack(weights,4.0f,0.01f,4,solutions);
341 
342  TEST(solutions.size() ==2,"Brute-force knapsack test");
343 
344  solutions.clear();
345  weights.clear();
346 
347  //set up the problem again
348  weights.push_back(1.0);
349  weights.push_back(3.0);
350  weights.push_back(10.0);
351 
352  vector<float> targetWeights;
353  targetWeights.push_back(4.0f);
354  targetWeights.push_back(10.0f);
355  bruteKnapsack(weights,targetWeights,
356  0.01f,4,solutions);
357 
358  TEST(solutions.size() ==4,"Brute-force knapsack test with multiple targets");
359  return true;
360 }
361 
362 #endif
size_t offset
Definition: massTool.cpp:44
FixedStack(size_t size)
Definition: massTool.cpp:68
Definition: massTool.cpp:40
void pop()
Definition: massTool.cpp:82
void vectorMultiErase(std::vector< T > &vec, const std::vector< bool > &wantKill)
Remove elements from the vector, without preserving order.
Definition: misc.h:112
#define TEST(f, g)
Definition: aptAssert.h:49
static void bruteKnapsack(std::vector< Weight > &weights, float targetWeight, float tolerance, unsigned int maxObjects, std::vector< std::vector< Weight > > &solutions)
Depth-First-Search based brute force solve the knapsack problem,.
Definition: massTool.cpp:117
bool empty() const
Definition: massTool.cpp:64
T & top()
Definition: massTool.cpp:89
vector< Weight > allowableWeights
Definition: massTool.cpp:43
void push(const T &t)
Definition: massTool.cpp:75
#define ASSERT(f)
vector< Weight > curWeights
Definition: massTool.cpp:43
float cumulativeWeight
Definition: massTool.cpp:45