... | ... |
@@ -1,5 +1,5 @@ |
1 | 1 |
#include "AtomicDomain.h" |
2 |
-#include "GapsAssert.h" |
|
2 |
+#include "utils/GapsAssert.h" |
|
3 | 3 |
|
4 | 4 |
#include <vector> |
5 | 5 |
|
... | ... |
@@ -136,34 +136,6 @@ uint64_t AtomicDomain::size() const |
136 | 136 |
return mAtoms.size(); |
137 | 137 |
} |
138 | 138 |
|
139 |
-void AtomicDomain::cacheInsert(uint64_t pos, float mass) const |
|
140 |
-{ |
|
141 |
- unsigned ndx = 0; |
|
142 |
- #pragma omp critical(atomicInsert) |
|
143 |
- { |
|
144 |
- ndx = mInsertCacheIndex++; |
|
145 |
- } |
|
146 |
- mInsertCache[ndx] = Atom(pos, mass); |
|
147 |
-} |
|
148 |
- |
|
149 |
-void AtomicDomain::cacheErase(uint64_t pos) const |
|
150 |
-{ |
|
151 |
- unsigned ndx = 0; |
|
152 |
- #pragma omp critical(atomicErase) |
|
153 |
- { |
|
154 |
- ndx = mEraseCacheIndex++; |
|
155 |
- } |
|
156 |
- mEraseCache[ndx] = pos; |
|
157 |
-} |
|
158 |
- |
|
159 |
-void AtomicDomain::resetCache(unsigned n) |
|
160 |
-{ |
|
161 |
- mInsertCacheIndex = 0; |
|
162 |
- mEraseCacheIndex = 0; |
|
163 |
- mInsertCache.resize(n); |
|
164 |
- mEraseCache.resize(n); |
|
165 |
-} |
|
166 |
- |
|
167 | 139 |
void AtomicDomain::erase(uint64_t pos) |
168 | 140 |
{ |
169 | 141 |
GAPS_ASSERT(size() > 0); |
... | ... |
@@ -181,22 +153,6 @@ void AtomicDomain::insert(uint64_t pos, float mass) |
181 | 153 |
mAtoms.insert(it, Atom(pos, mass)); |
182 | 154 |
} |
183 | 155 |
|
184 |
-void AtomicDomain::flushCache() |
|
185 |
-{ |
|
186 |
- for (unsigned i = 0; i < mEraseCacheIndex; ++i) |
|
187 |
- { |
|
188 |
- erase(mEraseCache[i]); |
|
189 |
- } |
|
190 |
- |
|
191 |
- for (unsigned i = 0; i < mInsertCacheIndex; ++i) |
|
192 |
- { |
|
193 |
- insert(mInsertCache[i].pos, mInsertCache[i].mass); |
|
194 |
- } |
|
195 |
- |
|
196 |
- mInsertCache.clear(); |
|
197 |
- mEraseCache.clear(); |
|
198 |
-} |
|
199 |
- |
|
200 | 156 |
Archive& operator<<(Archive &ar, AtomicDomain &domain) |
201 | 157 |
{ |
202 | 158 |
ar << domain.mDomainLength << domain.mRng << domain.mAtoms.size(); |
... | ... |
@@ -1,7 +1,7 @@ |
1 | 1 |
#ifndef __COGAPS_ATOMIC_DOMAIN_H__ |
2 | 2 |
#define __COGAPS_ATOMIC_DOMAIN_H__ |
3 | 3 |
|
4 |
-#include "Archive.h" |
|
4 |
+#include "utils/Archive.h" |
|
5 | 5 |
#include "math/Random.h" |
6 | 6 |
|
7 | 7 |
#include <vector> |
... | ... |
@@ -47,11 +47,8 @@ public: |
47 | 47 |
uint64_t randomFreePosition() const; |
48 | 48 |
uint64_t size() const; |
49 | 49 |
|
50 |
- // modify domain |
|
51 |
- void cacheInsert(uint64_t pos, float mass) const; |
|
52 |
- void cacheErase(uint64_t pos) const; |
|
53 |
- void flushCache(); |
|
54 |
- void resetCache(unsigned n); |
|
50 |
+ void erase(uint64_t pos); |
|
51 |
+ void insert(uint64_t pos, float mass); |
|
55 | 52 |
|
56 | 53 |
// serialization |
57 | 54 |
friend Archive& operator<<(Archive &ar, AtomicDomain &domain); |
... | ... |
@@ -67,23 +64,8 @@ private: |
67 | 64 |
// domain storage, sorted vector |
68 | 65 |
std::vector<Atom> mAtoms; |
69 | 66 |
|
70 |
- // holds cache of operations |
|
71 |
- mutable std::vector<Atom> mInsertCache; |
|
72 |
- mutable std::vector<uint64_t> mEraseCache; |
|
73 |
- |
|
74 |
- // current index in the operation cache |
|
75 |
- mutable unsigned mInsertCacheIndex; |
|
76 |
- mutable unsigned mEraseCacheIndex; |
|
77 |
- |
|
78 | 67 |
// random number generator |
79 | 68 |
mutable GapsRng mRng; |
80 |
- |
|
81 |
- void erase(uint64_t pos); |
|
82 |
- void insert(uint64_t pos, float mass); |
|
83 |
- |
|
84 |
- // serialization |
|
85 |
- friend Archive& operator<<(Archive &ar, AtomicDomain &domain); |
|
86 |
- friend Archive& operator>>(Archive &ar, AtomicDomain &domain); |
|
87 | 69 |
}; |
88 | 70 |
|
89 | 71 |
#endif |
90 | 72 |
\ No newline at end of file |
6 | 7 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,54 @@ |
1 |
+#include "GapsResult.h" |
|
2 |
+ |
|
3 |
+#include "GapsStatistics.h" |
|
4 |
+#include "file_parser/FileParser.h" |
|
5 |
+ |
|
6 |
+GapsResult::GapsResult(const GapsStatistics &stat) |
|
7 |
+ : |
|
8 |
+Amean(stat.Amean()), Asd(stat.Asd()), Pmean(stat.Pmean()), |
|
9 |
+Psd(stat.Psd()), meanChiSq(0.f), seed(0) |
|
10 |
+{} |
|
11 |
+ |
|
12 |
+void GapsResult::writeToFile(const std::string &fullPath) |
|
13 |
+{ |
|
14 |
+ std::size_t pos = fullPath.find_last_of('.'); |
|
15 |
+ std::string base = fullPath.substr(0, pos); |
|
16 |
+ |
|
17 |
+ switch (FileParser::fileType(fullPath)) |
|
18 |
+ { |
|
19 |
+ case GAPS_CSV: return writeCsv(base); |
|
20 |
+ case GAPS_TSV: return writeTsv(base); |
|
21 |
+ case GAPS_GCT: return writeGct(base); |
|
22 |
+ default : GAPS_ERROR("Invalid File Type"); |
|
23 |
+ } |
|
24 |
+} |
|
25 |
+ |
|
26 |
+void GapsResult::writeCsv(const std::string &path) |
|
27 |
+{ |
|
28 |
+ unsigned nPatterns = Amean.nCol(); |
|
29 |
+ std::string label("_" + gaps::to_string(nPatterns) + "_"); |
|
30 |
+ FileParser::writeToCsv(path + label + "Amean.csv", Amean); |
|
31 |
+ FileParser::writeToCsv(path + label + "Pmean.csv", Pmean); |
|
32 |
+ FileParser::writeToCsv(path + label + "Asd.csv", Asd); |
|
33 |
+ FileParser::writeToCsv(path + label + "Psd.csv", Psd); |
|
34 |
+} |
|
35 |
+ |
|
36 |
+void GapsResult::writeTsv(const std::string &path) |
|
37 |
+{ |
|
38 |
+ unsigned nPatterns = Amean.nCol(); |
|
39 |
+ std::string label("_" + gaps::to_string(nPatterns) + "_"); |
|
40 |
+ FileParser::writeToCsv(path + label + "Amean.tsv", Amean); |
|
41 |
+ FileParser::writeToCsv(path + label + "Pmean.tsv", Pmean); |
|
42 |
+ FileParser::writeToCsv(path + label + "Asd.tsv", Asd); |
|
43 |
+ FileParser::writeToCsv(path + label + "Psd.tsv", Psd); |
|
44 |
+} |
|
45 |
+ |
|
46 |
+void GapsResult::writeGct(const std::string &path) |
|
47 |
+{ |
|
48 |
+ unsigned nPatterns = Amean.nCol(); |
|
49 |
+ std::string label("_" + gaps::to_string(nPatterns) + "_"); |
|
50 |
+ FileParser::writeToCsv(path + label + "Amean.gct", Amean); |
|
51 |
+ FileParser::writeToCsv(path + label + "Pmean.gct", Pmean); |
|
52 |
+ FileParser::writeToCsv(path + label + "Asd.gct", Asd); |
|
53 |
+ FileParser::writeToCsv(path + label + "Psd.gct", Psd); |
|
54 |
+} |
|
0 | 55 |
\ No newline at end of file |
1 | 56 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,28 @@ |
1 |
+#ifndef __COGAPS_GAPS_RESULT__ |
|
2 |
+#define __COGAPS_GAPS_RESULT__ |
|
3 |
+ |
|
4 |
+#include "GapsStatistics.h" |
|
5 |
+#include "data_structures/Matrix.h" |
|
6 |
+ |
|
7 |
+#include <stdint.h> |
|
8 |
+#include <string> |
|
9 |
+ |
|
10 |
+struct GapsResult |
|
11 |
+{ |
|
12 |
+ ColMatrix Amean; |
|
13 |
+ ColMatrix Asd; |
|
14 |
+ ColMatrix Pmean; |
|
15 |
+ ColMatrix Psd; |
|
16 |
+ |
|
17 |
+ float meanChiSq; |
|
18 |
+ uint32_t seed; |
|
19 |
+ |
|
20 |
+ GapsResult(const GapsStatistics &stat); |
|
21 |
+ |
|
22 |
+ void writeToFile(const std::string &fullPath); |
|
23 |
+ void writeCsv(const std::string &path); |
|
24 |
+ void writeTsv(const std::string &path); |
|
25 |
+ void writeGct(const std::string &path); |
|
26 |
+}; |
|
27 |
+ |
|
28 |
+#endif // __COGAPS_GAPS_RESULT__ |
|
0 | 29 |
\ No newline at end of file |
... | ... |
@@ -1,6 +1,7 @@ |
1 | 1 |
#ifndef __COGAPS_GAPS_RUNNER_H__ |
2 | 2 |
#define __COGAPS_GAPS_RUNNER_H__ |
3 | 3 |
|
4 |
+#include "GapsResult.h" |
|
4 | 5 |
#include "GapsStatistics.h" |
5 | 6 |
#include "GibbsSampler.h" |
6 | 7 |
|
... | ... |
@@ -11,66 +12,6 @@ |
11 | 12 |
namespace bpt = boost::posix_time; |
12 | 13 |
#define bpt_now() bpt::microsec_clock::local_time() |
13 | 14 |
|
14 |
-struct GapsResult |
|
15 |
-{ |
|
16 |
- ColMatrix Amean; |
|
17 |
- ColMatrix Asd; |
|
18 |
- RowMatrix Pmean; |
|
19 |
- RowMatrix Psd; |
|
20 |
- |
|
21 |
- float meanChiSq; |
|
22 |
- uint32_t seed; |
|
23 |
- |
|
24 |
- GapsResult(const GapsStatistics &stat) : |
|
25 |
- Amean(stat.Amean()), Asd(stat.Asd()), Pmean(stat.Pmean()), |
|
26 |
- Psd(stat.Psd()), meanChiSq(0.f), seed(0) |
|
27 |
- {} |
|
28 |
- |
|
29 |
- void writeToFile(const std::string &fullPath) |
|
30 |
- { |
|
31 |
- std::size_t pos = fullPath.find_last_of('.'); |
|
32 |
- std::string base = fullPath.substr(0, pos); |
|
33 |
- |
|
34 |
- switch (FileParser::fileType(fullPath)) |
|
35 |
- { |
|
36 |
- case GAPS_CSV: return writeCsv(base); |
|
37 |
- case GAPS_TSV: return writeTsv(base); |
|
38 |
- case GAPS_GCT: return writeGct(base); |
|
39 |
- default: GAPS_ERROR("Invalid file type\n"); |
|
40 |
- } |
|
41 |
- } |
|
42 |
- |
|
43 |
- void writeCsv(const std::string &path) |
|
44 |
- { |
|
45 |
- unsigned nPatterns = Amean.nCol(); |
|
46 |
- std::string label("_" + gaps::to_string(nPatterns) + "_"); |
|
47 |
- FileParser::writeToCsv(path + label + "Amean.csv", Amean); |
|
48 |
- FileParser::writeToCsv(path + label + "Pmean.csv", Pmean); |
|
49 |
- FileParser::writeToCsv(path + label + "Asd.csv", Asd); |
|
50 |
- FileParser::writeToCsv(path + label + "Psd.csv", Psd); |
|
51 |
- } |
|
52 |
- |
|
53 |
- void writeTsv(const std::string &path) |
|
54 |
- { |
|
55 |
- unsigned nPatterns = Amean.nCol(); |
|
56 |
- std::string label("_" + gaps::to_string(nPatterns) + "_"); |
|
57 |
- FileParser::writeToCsv(path + label + "Amean.tsv", Amean); |
|
58 |
- FileParser::writeToCsv(path + label + "Pmean.tsv", Pmean); |
|
59 |
- FileParser::writeToCsv(path + label + "Asd.tsv", Asd); |
|
60 |
- FileParser::writeToCsv(path + label + "Psd.tsv", Psd); |
|
61 |
- } |
|
62 |
- |
|
63 |
- void writeGct(const std::string &path) |
|
64 |
- { |
|
65 |
- unsigned nPatterns = Amean.nCol(); |
|
66 |
- std::string label("_" + gaps::to_string(nPatterns) + "_"); |
|
67 |
- FileParser::writeToCsv(path + label + "Amean.gct", Amean); |
|
68 |
- FileParser::writeToCsv(path + label + "Pmean.gct", Pmean); |
|
69 |
- FileParser::writeToCsv(path + label + "Asd.gct", Asd); |
|
70 |
- FileParser::writeToCsv(path + label + "Psd.gct", Psd); |
|
71 |
- } |
|
72 |
-}; |
|
73 |
- |
|
74 | 15 |
class GapsRunner |
75 | 16 |
{ |
76 | 17 |
private: |
... | ... |
@@ -1,9 +1,9 @@ |
1 | 1 |
#ifndef __COGAPS_GIBBS_SAMPLER_H__ |
2 | 2 |
#define __COGAPS_GIBBS_SAMPLER_H__ |
3 | 3 |
|
4 |
-#include "Archive.h" |
|
4 |
+#include "utils/Archive.h" |
|
5 | 5 |
#include "AtomicDomain.h" |
6 |
-#include "GapsAssert.h" |
|
6 |
+#include "utils/GapsAssert.h" |
|
7 | 7 |
#include "ProposalQueue.h" |
8 | 8 |
#include "data_structures/Matrix.h" |
9 | 9 |
#include "math/Algorithms.h" |
... | ... |
@@ -43,28 +43,33 @@ protected: |
43 | 43 |
MatA mMatrix; |
44 | 44 |
MatB* mOtherMatrix; |
45 | 45 |
|
46 |
- ProposalQueue mQueue; |
|
46 |
+ GapsRng mPropRng; |
|
47 | 47 |
AtomicDomain mDomain; |
48 | 48 |
|
49 |
+ float mAlpha; |
|
49 | 50 |
float mLambda; |
50 | 51 |
float mMaxGibbsMass; |
51 | 52 |
float mAnnealingTemp; |
52 | 53 |
|
53 | 54 |
unsigned mNumRows; |
54 | 55 |
unsigned mNumCols; |
56 |
+ |
|
57 |
+ uint64_t mNumBins; |
|
55 | 58 |
uint64_t mBinSize; |
59 |
+ uint64_t mDomainLength; |
|
56 | 60 |
|
57 | 61 |
float mAvgQueue; |
58 | 62 |
float mNumQueues; |
59 | 63 |
|
60 | 64 |
T* impl(); |
61 | 65 |
|
62 |
- void processProposal(AtomicProposal *prop); |
|
66 |
+ float deathProb(uint64_t nAtoms) const; |
|
67 |
+ void makeAndProcessProposal(); |
|
63 | 68 |
|
64 |
- void birth(AtomicProposal *prop); |
|
65 |
- void death(AtomicProposal *prop); |
|
66 |
- void move(AtomicProposal *prop); |
|
67 |
- void exchange(AtomicProposal *prop); |
|
69 |
+ void birth(GapsRng *rng); |
|
70 |
+ void death(GapsRng *rng); |
|
71 |
+ void move(GapsRng *rng); |
|
72 |
+ void exchange(GapsRng *rng); |
|
68 | 73 |
|
69 | 74 |
bool updateAtomMass(Atom *atom, float delta); |
70 | 75 |
void acceptExchange(AtomicProposal *prop, float d1, unsigned r1, |
... | ... |
@@ -199,15 +204,19 @@ mDMatrix(data, transposeData, partitionRows, indices), |
199 | 204 |
mSMatrix(mDMatrix.pmax(0.1f, 0.1f)), |
200 | 205 |
mAPMatrix(mDMatrix.nRow(), mDMatrix.nCol()), |
201 | 206 |
mMatrix(amp ? mDMatrix.nRow() : nPatterns, amp ? nPatterns : mDMatrix.nCol()), |
202 |
-mOtherMatrix(NULL), mQueue(amp ? mDMatrix.nRow() : mDMatrix.nCol(), nPatterns), |
|
203 |
-mDomain(mMatrix.nRow() * mMatrix.nCol()), mLambda(0.f), |
|
204 |
-mMaxGibbsMass(100.f), mAnnealingTemp(1.f), mNumRows(mMatrix.nRow()), |
|
205 |
-mNumCols(mMatrix.nCol()), mAvgQueue(0.f), mNumQueues(0.f) |
|
207 |
+mOtherMatrix(NULL), |
|
208 |
+mDomain(mMatrix.nRow() * mMatrix.nCol()), |
|
209 |
+mLambda(0.f), |
|
210 |
+mMaxGibbsMass(100.f), |
|
211 |
+mAnnealingTemp(1.f), |
|
212 |
+mNumRows(mMatrix.nRow()), |
|
213 |
+mNumCols(mMatrix.nCol()), |
|
214 |
+mAvgQueue(0.f), |
|
215 |
+mNumQueues(0.f), |
|
216 |
+mNumBins(mMatrix.nRow() * mMatrix.nCol()), |
|
217 |
+mBinSize(std::numeric_limits<uint64_t>::max() / mNumBins), |
|
218 |
+mDomainLength(mBinSize * mNumBins) |
|
206 | 219 |
{ |
207 |
- // calculate atomic domain size |
|
208 |
- mBinSize = std::numeric_limits<uint64_t>::max() |
|
209 |
- / static_cast<uint64_t>(mNumRows * mNumCols); |
|
210 |
- |
|
211 | 220 |
// default sparsity parameters |
212 | 221 |
setSparsity(0.01, false); |
213 | 222 |
} |
... | ... |
@@ -223,14 +232,13 @@ bool transpose, bool partitionRows, const std::vector<unsigned> &indices) |
223 | 232 |
template <class T, class MatA, class MatB> |
224 | 233 |
void GibbsSampler<T, MatA, MatB>::setSparsity(float alpha, bool singleCell) |
225 | 234 |
{ |
226 |
- mQueue.setAlpha(alpha); |
|
227 |
- |
|
228 | 235 |
float meanD = singleCell ? gaps::algo::nonZeroMean(mDMatrix) : |
229 | 236 |
gaps::algo::mean(mDMatrix); |
230 | 237 |
|
231 | 238 |
unsigned nPatterns = mDMatrix.nRow() == mMatrix.nRow() ? mMatrix.nCol() : |
232 | 239 |
mMatrix.nRow(); |
233 | 240 |
|
241 |
+ mAlpha = alpha; |
|
234 | 242 |
mLambda = alpha * std::sqrt(nPatterns / meanD); |
235 | 243 |
} |
236 | 244 |
|
... | ... |
@@ -288,104 +296,93 @@ void GibbsSampler<T, MatA, MatB>::update(unsigned nSteps, unsigned nCores) |
288 | 296 |
unsigned n = 0; |
289 | 297 |
while (n < nSteps) |
290 | 298 |
{ |
291 |
- // populate queue, prepare domain for this queue |
|
292 |
- mQueue.populate(mDomain, nSteps - n); |
|
293 |
- mDomain.resetCache(mQueue.size()); |
|
294 |
- n += mQueue.size(); |
|
295 |
- |
|
296 |
- // update average queue count |
|
297 |
- #ifdef GAPS_DEBUG |
|
298 |
- mNumQueues += 1.f; |
|
299 |
- mAvgQueue *= (mNumQueues - 1.f) / mNumQueues; |
|
300 |
- mAvgQueue += mQueue.size() / mNumQueues; |
|
301 |
- #endif |
|
302 |
- |
|
303 |
- // process all proposed updates |
|
304 |
- #pragma omp parallel for num_threads(nCores) |
|
305 |
- for (unsigned i = 0; i < mQueue.size(); ++i) |
|
306 |
- { |
|
307 |
- processProposal(&mQueue[i]); |
|
308 |
- } |
|
309 |
- |
|
310 |
- mDomain.flushCache(); |
|
311 |
- mQueue.clear(); |
|
299 |
+ makeAndProcessProposal(); |
|
300 |
+ ++n; |
|
312 | 301 |
} |
313 | 302 |
} |
314 | 303 |
|
315 | 304 |
template <class T, class MatA, class MatB> |
316 |
-void GibbsSampler<T, MatA, MatB>::processProposal(AtomicProposal *prop) |
|
305 |
+float GibbsSampler<T, MatA, MatB>::deathProb(uint64_t nAtoms) const |
|
317 | 306 |
{ |
318 |
- switch (prop->type) |
|
307 |
+ double size = static_cast<double>(mDomainLength); |
|
308 |
+ double term1 = (size - static_cast<double>(nAtoms)) / size; |
|
309 |
+ double term2 = mAlpha * static_cast<double>(mNumBins) * term1; |
|
310 |
+ return static_cast<double>(nAtoms) / (static_cast<double>(nAtoms) + term2); |
|
311 |
+} |
|
312 |
+ |
|
313 |
+template <class T, class MatA, class MatB> |
|
314 |
+void GibbsSampler<T, MatA, MatB>::makeAndProcessProposal() |
|
315 |
+{ |
|
316 |
+ GapsRng rng; // need to make this independent of nThreads (use streams?) |
|
317 |
+ if (mDomain.size() < 2) // always birth with 0 or 1 atoms |
|
319 | 318 |
{ |
320 |
- case 'B': |
|
321 |
- birth(prop); |
|
322 |
- break; |
|
323 |
- case 'D': |
|
324 |
- death(prop); |
|
325 |
- break; |
|
326 |
- case 'M': |
|
327 |
- move(prop); |
|
328 |
- break; |
|
329 |
- case 'E': |
|
330 |
- exchange(prop); |
|
331 |
- break; |
|
319 |
+ return birth(&rng); |
|
332 | 320 |
} |
321 |
+ |
|
322 |
+ //float u1 = rng.uniform(); |
|
323 |
+ float u1 = 0.f; |
|
324 |
+ if (u1 <= 0.5f) |
|
325 |
+ { |
|
326 |
+ return rng.uniform() < deathProb(mDomain.size()) ? death(&rng) : birth(&rng); |
|
327 |
+ } |
|
328 |
+ return (u1 < 0.75f) ? move(&rng) : exchange(&rng); |
|
333 | 329 |
} |
334 | 330 |
|
335 | 331 |
// add an atom at pos, calculate mass either with an exponential distribution |
336 | 332 |
// or with the gibbs mass calculation |
337 | 333 |
template <class T, class MatA, class MatB> |
338 |
-void GibbsSampler<T, MatA, MatB>::birth(AtomicProposal *prop) |
|
334 |
+void GibbsSampler<T, MatA, MatB>::birth(GapsRng *rng) |
|
339 | 335 |
{ |
340 |
- unsigned row = impl()->getRow(prop->birthPos); |
|
341 |
- unsigned col = impl()->getCol(prop->birthPos); |
|
336 |
+ uint64_t pos = mDomain.randomFreePosition(); |
|
337 |
+ AtomicProposal prop = AtomicProposal('B', pos); |
|
338 |
+ |
|
339 |
+ unsigned row = impl()->getRow(prop.birthPos); |
|
340 |
+ unsigned col = impl()->getCol(prop.birthPos); |
|
342 | 341 |
|
343 | 342 |
// calculate proposed mass |
344 | 343 |
float mass = 0.f; |
345 | 344 |
if (impl()->canUseGibbs(row, col)) |
346 | 345 |
{ |
347 | 346 |
AlphaParameters alpha = impl()->alphaParameters(row, col); |
348 |
- mass = gibbsMass(alpha, &(prop->rng)).value; // 0 if it fails |
|
347 |
+ mass = gibbsMass(alpha, &(prop.rng)).value; // 0 if it fails |
|
349 | 348 |
} |
350 | 349 |
else |
351 | 350 |
{ |
352 |
- mass = prop->rng.exponential(mLambda); |
|
351 |
+ mass = prop.rng.exponential(mLambda); |
|
353 | 352 |
} |
354 | 353 |
|
355 | 354 |
// accept mass as long as it's non-zero |
356 | 355 |
if (mass >= gaps::epsilon) |
357 | 356 |
{ |
358 |
- mDomain.cacheInsert(prop->birthPos, mass); |
|
357 |
+ mDomain.insert(prop.birthPos, mass); |
|
359 | 358 |
mMatrix(row, col) += mass; |
360 | 359 |
impl()->updateAPMatrix(row, col, mass); |
361 |
- mQueue.acceptBirth(); |
|
362 |
- } |
|
363 |
- else |
|
364 |
- { |
|
365 |
- mQueue.rejectBirth(); |
|
366 | 360 |
} |
367 | 361 |
} |
368 | 362 |
|
369 | 363 |
// automatically accept death, attempt a rebirth at the same position, using |
370 | 364 |
// the original mass or the gibbs mass calculation |
371 | 365 |
template <class T, class MatA, class MatB> |
372 |
-void GibbsSampler<T, MatA, MatB>::death(AtomicProposal *prop) |
|
366 |
+void GibbsSampler<T, MatA, MatB>::death(GapsRng *rng) |
|
373 | 367 |
{ |
368 |
+ Atom* a = mDomain.randomAtom(); |
|
369 |
+ AtomicProposal prop = AtomicProposal('D', a); |
|
370 |
+ |
|
374 | 371 |
// calculate bin for this atom |
375 |
- unsigned row = impl()->getRow(prop->atom1->pos); |
|
376 |
- unsigned col = impl()->getCol(prop->atom1->pos); |
|
372 |
+ unsigned row = impl()->getRow(prop.atom1->pos); |
|
373 |
+ unsigned col = impl()->getCol(prop.atom1->pos); |
|
377 | 374 |
|
378 | 375 |
// kill off atom |
379 |
- float newVal = gaps::max(mMatrix(row, col) - prop->atom1->mass, 0.f); |
|
376 |
+ float newVal = gaps::max(mMatrix(row, col) - prop.atom1->mass, 0.f); |
|
380 | 377 |
impl()->updateAPMatrix(row, col, newVal - mMatrix(row, col)); |
381 | 378 |
mMatrix(row, col) = newVal; |
382 | 379 |
|
383 | 380 |
// calculate rebirth mass |
384 |
- float rebirthMass = prop->atom1->mass; |
|
381 |
+ float rebirthMass = prop.atom1->mass; |
|
385 | 382 |
AlphaParameters alpha = impl()->alphaParameters(row, col); |
386 | 383 |
if (impl()->canUseGibbs(row, col)) |
387 | 384 |
{ |
388 |
- OptionalFloat gMass = gibbsMass(alpha, &(prop->rng)); |
|
385 |
+ OptionalFloat gMass = gibbsMass(alpha, &(prop.rng)); |
|
389 | 386 |
if (gMass.hasValue) |
390 | 387 |
{ |
391 | 388 |
rebirthMass = gMass.value; |
... | ... |
@@ -394,43 +391,48 @@ void GibbsSampler<T, MatA, MatB>::death(AtomicProposal *prop) |
394 | 391 |
|
395 | 392 |
// accept/reject rebirth |
396 | 393 |
float deltaLL = rebirthMass * (alpha.su - alpha.s * rebirthMass / 2.f); |
397 |
- if (deltaLL * mAnnealingTemp >= std::log(prop->rng.uniform())) |
|
394 |
+ if (deltaLL * mAnnealingTemp >= std::log(prop.rng.uniform())) |
|
398 | 395 |
{ |
399 |
- prop->atom1->mass = rebirthMass; |
|
396 |
+ prop.atom1->mass = rebirthMass; |
|
400 | 397 |
mMatrix(row, col) += rebirthMass; |
401 | 398 |
impl()->updateAPMatrix(row, col, rebirthMass); |
402 |
- mQueue.rejectDeath(); |
|
403 | 399 |
} |
404 | 400 |
else |
405 | 401 |
{ |
406 |
- mDomain.cacheErase(prop->atom1->pos); |
|
407 |
- mQueue.acceptDeath(); |
|
402 |
+ mDomain.erase(prop.atom1->pos); |
|
408 | 403 |
} |
409 | 404 |
} |
410 | 405 |
|
411 | 406 |
// move mass from src to dest in the atomic domain |
412 | 407 |
template <class T, class MatA, class MatB> |
413 |
-void GibbsSampler<T, MatA, MatB>::move(AtomicProposal *prop) |
|
408 |
+void GibbsSampler<T, MatA, MatB>::move(GapsRng *rng) |
|
414 | 409 |
{ |
415 |
- unsigned r1 = impl()->getRow(prop->atom1->pos); |
|
416 |
- unsigned c1 = impl()->getCol(prop->atom1->pos); |
|
417 |
- unsigned r2 = impl()->getRow(prop->moveDest); |
|
418 |
- unsigned c2 = impl()->getCol(prop->moveDest); |
|
419 |
- |
|
420 |
- GAPS_ASSERT(r1 != r2 || c1 != c2); // same bin handled during proposal |
|
421 |
- |
|
422 |
- float deltaLL = impl()->computeDeltaLL(r1, c1, -1.f * prop->atom1->mass, |
|
423 |
- r2, c2, prop->atom1->mass); |
|
424 |
- if (deltaLL * mAnnealingTemp > std::log(prop->rng.uniform())) |
|
410 |
+ AtomNeighborhood hood = mDomain.randomAtomWithNeighbors(); |
|
411 |
+ uint64_t lbound = hood.hasLeft() ? hood.left->pos : 0; |
|
412 |
+ uint64_t rbound = hood.hasRight() ? hood.right->pos : mDomainLength; |
|
413 |
+ uint64_t newLocation = mPropRng.uniform64(lbound + 1, rbound - 1); |
|
414 |
+ AtomicProposal prop = AtomicProposal('M', hood.center, newLocation); |
|
415 |
+ |
|
416 |
+ unsigned r1 = impl()->getRow(prop.atom1->pos); |
|
417 |
+ unsigned c1 = impl()->getCol(prop.atom1->pos); |
|
418 |
+ unsigned r2 = impl()->getRow(prop.moveDest); |
|
419 |
+ unsigned c2 = impl()->getCol(prop.moveDest); |
|
420 |
+ |
|
421 |
+ if (r1 != r2 || c1 != c2) |
|
425 | 422 |
{ |
426 |
- prop->atom1->pos = prop->moveDest; |
|
423 |
+ float deltaLL = impl()->computeDeltaLL(r1, c1, -1.f * prop.atom1->mass, |
|
424 |
+ r2, c2, prop.atom1->mass); |
|
425 |
+ if (deltaLL * mAnnealingTemp > std::log(prop.rng.uniform())) |
|
426 |
+ { |
|
427 |
+ prop.atom1->pos = prop.moveDest; |
|
427 | 428 |
|
428 |
- float newVal = gaps::max(mMatrix(r1, c1) - prop->atom1->mass, 0.f); |
|
429 |
- impl()->updateAPMatrix(r1, c1, newVal - mMatrix(r1, c1)); |
|
430 |
- mMatrix(r1, c1) = newVal; |
|
429 |
+ float newVal = gaps::max(mMatrix(r1, c1) - prop.atom1->mass, 0.f); |
|
430 |
+ impl()->updateAPMatrix(r1, c1, newVal - mMatrix(r1, c1)); |
|
431 |
+ mMatrix(r1, c1) = newVal; |
|
431 | 432 |
|
432 |
- mMatrix(r2, c2) += prop->atom1->mass; |
|
433 |
- impl()->updateAPMatrix(r2, c2, prop->atom1->mass); |
|
433 |
+ mMatrix(r2, c2) += prop.atom1->mass; |
|
434 |
+ impl()->updateAPMatrix(r2, c2, prop.atom1->mass); |
|
435 |
+ } |
|
434 | 436 |
} |
435 | 437 |
} |
436 | 438 |
|
... | ... |
@@ -438,53 +440,57 @@ void GibbsSampler<T, MatA, MatB>::move(AtomicProposal *prop) |
438 | 440 |
// for one of the atoms to be deleted if it's mass becomes too small after |
439 | 441 |
// the exchange |
440 | 442 |
template <class T, class MatA, class MatB> |
441 |
-void GibbsSampler<T, MatA, MatB>::exchange(AtomicProposal *prop) |
|
443 |
+void GibbsSampler<T, MatA, MatB>::exchange(GapsRng *rng) |
|
442 | 444 |
{ |
443 |
- unsigned r1 = impl()->getRow(prop->atom1->pos); |
|
444 |
- unsigned c1 = impl()->getCol(prop->atom1->pos); |
|
445 |
- unsigned r2 = impl()->getRow(prop->atom2->pos); |
|
446 |
- unsigned c2 = impl()->getCol(prop->atom2->pos); |
|
447 |
- |
|
448 |
- GAPS_ASSERT(r1 != r2 || c1 != c2); // same bin handled during proposal |
|
445 |
+ AtomNeighborhood hood = mDomain.randomAtomWithRightNeighbor(); |
|
446 |
+ Atom* a1 = hood.center; |
|
447 |
+ Atom* a2 = hood.hasRight() ? hood.right : mDomain.front(); |
|
448 |
+ AtomicProposal prop = AtomicProposal('E', a1, a2); |
|
449 | 449 |
|
450 |
- float m1 = prop->atom1->mass; |
|
451 |
- float m2 = prop->atom2->mass; |
|
450 |
+ unsigned r1 = impl()->getRow(prop.atom1->pos); |
|
451 |
+ unsigned c1 = impl()->getCol(prop.atom1->pos); |
|
452 |
+ unsigned r2 = impl()->getRow(prop.atom2->pos); |
|
453 |
+ unsigned c2 = impl()->getCol(prop.atom2->pos); |
|
452 | 454 |
|
453 |
- if (impl()->canUseGibbs(r1, c1, r2, c2)) |
|
455 |
+ if (r1 != r2 || c1 != c2) |
|
454 | 456 |
{ |
455 |
- AlphaParameters alpha = impl()->alphaParameters(r1, c1, r2, c2); |
|
456 |
- OptionalFloat gMass = gibbsMass(alpha, m1, m2, &(prop->rng)); |
|
457 |
- if (gMass.hasValue) |
|
457 |
+ float m1 = prop.atom1->mass; |
|
458 |
+ float m2 = prop.atom2->mass; |
|
459 |
+ |
|
460 |
+ if (impl()->canUseGibbs(r1, c1, r2, c2)) |
|
458 | 461 |
{ |
459 |
- acceptExchange(prop, gMass.value, r1, c1, r2, c2); |
|
460 |
- return; |
|
462 |
+ AlphaParameters alpha = impl()->alphaParameters(r1, c1, r2, c2); |
|
463 |
+ OptionalFloat gMass = gibbsMass(alpha, m1, m2, &(prop.rng)); |
|
464 |
+ if (gMass.hasValue) |
|
465 |
+ { |
|
466 |
+ acceptExchange(&prop, gMass.value, r1, c1, r2, c2); |
|
467 |
+ return; |
|
468 |
+ } |
|
461 | 469 |
} |
462 |
- } |
|
463 | 470 |
|
464 |
- float newMass = prop->rng.truncGammaUpper(m1 + m2, 2.f, 1.f / mLambda); |
|
471 |
+ float newMass = prop.rng.truncGammaUpper(m1 + m2, 2.f, 1.f / mLambda); |
|
465 | 472 |
|
466 |
- float delta = m1 > m2 ? newMass - m1 : m2 - newMass; // change larger mass |
|
467 |
- float pOldMass = 2.f * newMass > m1 + m2 ? gaps::max(m1, m2) : gaps::min(m1, m2); |
|
473 |
+ float delta = m1 > m2 ? newMass - m1 : m2 - newMass; // change larger mass |
|
474 |
+ float pOldMass = 2.f * newMass > m1 + m2 ? gaps::max(m1, m2) : gaps::min(m1, m2); |
|
468 | 475 |
|
469 |
- float pNew = gaps::d_gamma(newMass, 2.f, 1.f / mLambda); |
|
470 |
- float pOld = gaps::d_gamma(pOldMass, 2.f, 1.f / mLambda); |
|
476 |
+ float pNew = gaps::d_gamma(newMass, 2.f, 1.f / mLambda); |
|
477 |
+ float pOld = gaps::d_gamma(pOldMass, 2.f, 1.f / mLambda); |
|
471 | 478 |
|
472 |
- if (pOld == 0.f && pNew != 0.f) // special case |
|
473 |
- { |
|
474 |
- acceptExchange(prop, delta, r1, c1, r2, c2); |
|
475 |
- return; |
|
476 |
- } |
|
479 |
+ if (pOld == 0.f && pNew != 0.f) // special case |
|
480 |
+ { |
|
481 |
+ acceptExchange(&prop, delta, r1, c1, r2, c2); |
|
482 |
+ return; |
|
483 |
+ } |
|
477 | 484 |
|
478 |
- float deltaLL = impl()->computeDeltaLL(r1, c1, delta, r2, c2, -delta); |
|
479 |
- float priorLL = (pOld == 0.f) ? 1.f : pOld / pNew; |
|
480 |
- float u = std::log(prop->rng.uniform() * priorLL); |
|
481 |
- if (u < deltaLL * mAnnealingTemp) |
|
482 |
- { |
|
483 |
- acceptExchange(prop, delta, r1, c1, r2, c2); |
|
484 |
- return; |
|
485 |
+ float deltaLL = impl()->computeDeltaLL(r1, c1, delta, r2, c2, -delta); |
|
486 |
+ float priorLL = (pOld == 0.f) ? 1.f : pOld / pNew; |
|
487 |
+ float u = std::log(prop.rng.uniform() * priorLL); |
|
488 |
+ if (u < deltaLL * mAnnealingTemp) |
|
489 |
+ { |
|
490 |
+ acceptExchange(&prop, delta, r1, c1, r2, c2); |
|
491 |
+ return; |
|
492 |
+ } |
|
485 | 493 |
} |
486 |
- |
|
487 |
- mQueue.rejectDeath(); |
|
488 | 494 |
} |
489 | 495 |
|
490 | 496 |
// helper function for acceptExchange, used to conditionally update the mass |
... | ... |
@@ -495,8 +501,7 @@ bool GibbsSampler<T, MatA, MatB>::updateAtomMass(Atom *atom, float delta) |
495 | 501 |
if (atom->mass + delta < gaps::epsilon) |
496 | 502 |
{ |
497 | 503 |
DEBUG_PING // want to know if this ever happens |
498 |
- mDomain.cacheErase(atom->pos); |
|
499 |
- mQueue.acceptDeath(); |
|
504 |
+ mDomain.erase(atom->pos); |
|
500 | 505 |
return false; |
501 | 506 |
} |
502 | 507 |
atom->mass += delta; |
... | ... |
@@ -518,11 +523,6 @@ float d1, unsigned r1, unsigned c1, unsigned r2, unsigned c2) |
518 | 523 |
if (!b1) { d1 = -1.f * prop->atom1->mass; } |
519 | 524 |
if (!b2) { d2 = -1.f * prop->atom2->mass; } |
520 | 525 |
|
521 |
- if (b1 && b2) |
|
522 |
- { |
|
523 |
- mQueue.rejectDeath(); |
|
524 |
- } |
|
525 |
- |
|
526 | 526 |
// ensure matrix values don't go negative (truncation error at fault) |
527 | 527 |
float v1 = gaps::max(mMatrix(r1, c1) + d1, 0.f); |
528 | 528 |
impl()->updateAPMatrix(r1, c1, v1 - mMatrix(r1, c1)); |
... | ... |
@@ -601,7 +601,7 @@ bool GibbsSampler<T, MatA, MatB>::internallyConsistent() |
601 | 601 |
template <class T, class MatA, class MatB> |
602 | 602 |
Archive& operator<<(Archive &ar, GibbsSampler<T, MatA, MatB> &s) |
603 | 603 |
{ |
604 |
- ar << s.mMatrix << s.mAPMatrix << s.mQueue << s.mDomain << s.mLambda |
|
604 |
+ ar << s.mMatrix << s.mAPMatrix << s.mDomain << s.mLambda |
|
605 | 605 |
<< s.mMaxGibbsMass << s.mAnnealingTemp << s.mNumRows << s.mNumCols |
606 | 606 |
<< s.mBinSize << s.mAvgQueue << s.mNumQueues; |
607 | 607 |
return ar; |
... | ... |
@@ -610,7 +610,7 @@ Archive& operator<<(Archive &ar, GibbsSampler<T, MatA, MatB> &s) |
610 | 610 |
template <class T, class MatA, class MatB> |
611 | 611 |
Archive& operator>>(Archive &ar, GibbsSampler<T, MatA, MatB> &s) |
612 | 612 |
{ |
613 |
- ar >> s.mMatrix >> s.mAPMatrix >> s.mQueue >> s.mDomain >> s.mLambda |
|
613 |
+ ar >> s.mMatrix >> s.mAPMatrix >> s.mDomain >> s.mLambda |
|
614 | 614 |
>> s.mMaxGibbsMass >> s.mAnnealingTemp >> s.mNumRows >> s.mNumCols |
615 | 615 |
>> s.mBinSize >> s.mAvgQueue >> s.mNumQueues; |
616 | 616 |
return ar; |
... | ... |
@@ -1,12 +1,12 @@ |
1 | 1 |
#include "catch.h" |
2 |
-#include "../Archive.h" |
|
2 |
+#include "../utils/Archive.h" |
|
3 | 3 |
#include "../data_structures/Matrix.h" |
4 | 4 |
#include "../GibbsSampler.h" |
5 | 5 |
#include "../math/Random.h" |
6 | 6 |
|
7 | 7 |
#if 0 |
8 | 8 |
|
9 |
-TEST_CASE("Test Archive.h") |
|
9 |
+TEST_CASE("Test utils/Archive.h") |
|
10 | 10 |
{ |
11 | 11 |
SECTION("Reading/Writing to an Archive") |
12 | 12 |
{ |
... | ... |
@@ -1,7 +1,7 @@ |
1 | 1 |
#include "Matrix.h" |
2 | 2 |
#include "../file_parser/CsvParser.h" |
3 | 3 |
#include "../file_parser/MatrixElement.h" |
4 |
-#include "../GapsAssert.h" |
|
4 |
+#include "../utils/GapsAssert.h" |
|
5 | 5 |
|
6 | 6 |
template <class MatA, class MatB> |
7 | 7 |
inline void copyMatrix(MatA &dest, const MatB &source) |
... | ... |
@@ -1,8 +1,8 @@ |
1 | 1 |
#ifndef __COGAPS_MATRIX_H__ |
2 | 2 |
#define __COGAPS_MATRIX_H__ |
3 | 3 |
|
4 |
-#include "../Archive.h" |
|
5 |
-#include "../GapsAssert.h" |
|
4 |
+#include "../utils/Archive.h" |
|
5 |
+#include "../utils/GapsAssert.h" |
|
6 | 6 |
#include "../file_parser/FileParser.h" |
7 | 7 |
#include "../math/Math.h" |
8 | 8 |
#include "Vector.h" |
... | ... |
@@ -130,47 +130,5 @@ public: |
130 | 130 |
} // namespace simd |
131 | 131 |
} // namespace gaps |
132 | 132 |
|
133 |
-#if (defined(_M_AMD64) || defined(_M_X64) || defined(__amd64)) && ! defined(__x86_64__) |
|
134 |
- #define __x86_64__ 1 |
|
135 |
-#endif |
|
136 |
- |
|
137 |
-#ifdef _OPENMP |
|
138 |
- #define __GAPS_OPENMP__ |
|
139 |
-#endif |
|
140 |
- |
|
141 |
-// used to convert defined macro values into strings |
|
142 |
-#define STR_HELPER(x) #x |
|
143 |
-#define STR(x) STR_HELPER(x) |
|
144 |
- |
|
145 |
-inline std::string buildReport() |
|
146 |
-{ |
|
147 |
-#if defined( __clang__ ) |
|
148 |
- std::string compiler = "Compiled with Clang\n"; |
|
149 |
-#elif defined( __INTEL_COMPILER ) |
|
150 |
- std::string compiler = "Compiled with Intel ICC/ICPC\n"; |
|
151 |
-#elif defined( __GNUC__ ) |
|
152 |
- std::string compiler = "Compiled with GCC v" + std::string(STR( __GNUC__ )) |
|
153 |
- + "." + std::string(STR( __GNUC_MINOR__ )) + '\n'; |
|
154 |
-#elif defined( _MSC_VER ) |
|
155 |
- std::string compiler = "Compiled with Microsoft Visual Studio\n"; |
|
156 |
-#endif |
|
157 |
- |
|
158 |
-#if defined( __GAPS_AVX__ ) |
|
159 |
- std::string simd = "SIMD: AVX instructions enabled\n"; |
|
160 |
-#elif defined( __GAPS_SSE__ ) |
|
161 |
- std::string simd = "SIMD: SSE instructions enabled\n"; |
|
162 |
-#else |
|
163 |
- std::string simd = "SIMD not enabled\n"; |
|
164 |
-#endif |
|
165 |
- |
|
166 |
-#ifdef __GAPS_OPENMP__ |
|
167 |
- std::string openmp = "Compiled with OpenMP\n"; |
|
168 |
-#else |
|
169 |
- std::string openmp = "Compiler did not support OpenMP\n"; |
|
170 |
-#endif |
|
171 |
- |
|
172 |
- return compiler + simd + openmp; |
|
173 |
-} |
|
174 |
- |
|
175 | 133 |
#endif // __COGAPS_SIMD_H__ |
176 | 134 |
|
186 | 144 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,50 @@ |
1 |
+#ifndef __COGAPS_GLOBAL_CONFIG_H__ |
|
2 |
+#define __COGAPS_GLOBAL_CONFIG_H__ |
|
3 |
+ |
|
4 |
+#include "../math/SIMD.h" |
|
5 |
+ |
|
6 |
+#include <string> |
|
7 |
+ |
|
8 |
+#if (defined(_M_AMD64) || defined(_M_X64) || defined(__amd64)) && ! defined(__x86_64__) |
|
9 |
+ #define __x86_64__ 1 |
|
10 |
+#endif |
|
11 |
+ |
|
12 |
+#ifdef _OPENMP |
|
13 |
+ #define __GAPS_OPENMP__ |
|
14 |
+#endif |
|
15 |
+ |
|
16 |
+// used to convert defined macro values into strings |
|
17 |
+#define STR_HELPER(x) #x |
|
18 |
+#define STR(x) STR_HELPER(x) |
|
19 |
+ |
|
20 |
+inline std::string buildReport() |
|
21 |
+{ |
|
22 |
+#if defined( __clang__ ) |
|
23 |
+ std::string compiler = "Compiled with Clang\n"; |
|
24 |
+#elif defined( __INTEL_COMPILER ) |
|
25 |
+ std::string compiler = "Compiled with Intel ICC/ICPC\n"; |
|
26 |
+#elif defined( __GNUC__ ) |
|
27 |
+ std::string compiler = "Compiled with GCC v" + std::string(STR( __GNUC__ )) |
|
28 |
+ + "." + std::string(STR( __GNUC_MINOR__ )) + '\n'; |
|
29 |
+#elif defined( _MSC_VER ) |
|
30 |
+ std::string compiler = "Compiled with Microsoft Visual Studio\n"; |
|
31 |
+#endif |
|
32 |
+ |
|
33 |
+#if defined( __GAPS_AVX__ ) |
|
34 |
+ std::string simd = "SIMD: AVX instructions enabled\n"; |
|
35 |
+#elif defined( __GAPS_SSE__ ) |
|
36 |
+ std::string simd = "SIMD: SSE instructions enabled\n"; |
|
37 |
+#else |
|
38 |
+ std::string simd = "SIMD not enabled\n"; |
|
39 |
+#endif |
|
40 |
+ |
|
41 |
+#ifdef __GAPS_OPENMP__ |
|
42 |
+ std::string openmp = "Compiled with OpenMP\n"; |
|
43 |
+#else |
|
44 |
+ std::string openmp = "Compiler did not support OpenMP\n"; |
|
45 |
+#endif |
|
46 |
+ |
|
47 |
+ return compiler + simd + openmp; |
|
48 |
+} |
|
49 |
+ |
|
50 |
+#endif // __COGAPS_GLOBAL_CONFIG_H__ |
|
0 | 51 |
\ No newline at end of file |