-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmodel.cpp
235 lines (199 loc) · 6.46 KB
/
model.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#include "model.hpp"
/**
* Return row of element within a malloc'd 1D array as 2D
*/
inline int* AdaptiveModel::Ptr1Dto2D(int *ptr, int x, int xMax)
{
return &ptr[x * xMax];
}
AdaptiveModel::AdaptiveModel(int Alpha)
{
AlphabetSize = (int*)malloc(1 * sizeof(int));
if(Alpha <= 0)
Error("Alphabet size must be at least 1!");
*AlphabetSize = Alpha;
Mix = (int*)malloc((*AlphabetSize * (*AlphabetSize + 1)) * sizeof(int));
CumFreqs = (int*)malloc((*AlphabetSize + 1) * sizeof(int));
if(Mix == NULL)
Error("Failed to allocate mixing table!");
if(CumFreqs == NULL)
Error("Failed to allocate cumulative table!");
}
AdaptiveModel::~AdaptiveModel()
{
free(AlphabetSize);
free(Mix);
free(CumFreqs);
}
unsigned int AdaptiveModel::SymToLow(unsigned short sym)
{
return CumFreqs[sym];
}
unsigned int AdaptiveModel::SymToFreq(unsigned short sym)
{
return CumFreqs[sym + 1] - CumFreqs[sym];
}
unsigned short AdaptiveModel::RangeToSym(unsigned int range)
{
// Linear search
for(int i = 0; i < *AlphabetSize; i++)
{
if(CumFreqs[i] <= range && range < CumFreqs[i + 1])
return i;
}
Error("Range to symbol failure!");
return 0;
}
/**
* CDF transformation-style update (always remains a total of ProbScale)
* As long as both CDF's have non-zero probabilities and are both a signed type then it'll mix just fine
*/
void AdaptiveModel::Update(int symbol)
{
/*
// Only for fixed sized alphabets
__m128i CDF1 = _mm_loadu_si128((__m128i*)&CumFreqs[0]);
__m128i CDF2 = _mm_loadu_si128((__m128i*)&CumFreqs[4]);
__m128i MIX1 = _mm_loadu_si128((__m128i*)&Mix[symbol][0]);
__m128i MIX2 = _mm_loadu_si128((__m128i*)&Mix[symbol][4]);
CDF1 = _mm_add_epi32(_mm_srai_epi32(_mm_sub_epi32(MIX1, CDF1), Rate), CDF1);
CDF2 = _mm_add_epi32(_mm_srai_epi32(_mm_sub_epi32(MIX2, CDF2), Rate), CDF2);
_mm_storeu_si128((__m128i *)&CumFreqs[0], CDF1);
_mm_storeu_si128((__m128i *)&CumFreqs[4], CDF2);
*/
int *MixRow = Ptr1Dto2D(Mix, symbol, *AlphabetSize + 1); // *t+(row*width)+column
for(int i = 1; i < *AlphabetSize; i++)
CumFreqs[i] += (MixRow[i] - CumFreqs[i]) >> Rate;
}
/**
* Reinitialize the state of the model back to equal probabilities
* Build mixing table
*/
void AdaptiveModel::Reset()
{
int *freqs = (int*)malloc(*AlphabetSize * sizeof(int));
int scale = ProbScale / *AlphabetSize;
for(int i = 0; i < *AlphabetSize; i++)
freqs[i] = scale;
int ActualScale = scale * *AlphabetSize;
freqs[0] += ProbScale - ActualScale;
memset(CumFreqs, 0, (*AlphabetSize + 1) * sizeof(unsigned int));
for(int i = 0; i < *AlphabetSize; i++)
CumFreqs[i + 1] = CumFreqs[i] + freqs[i];
assert(CumFreqs[*AlphabetSize] == ProbScale);
free(freqs);
for(int sym = 0; sym < *AlphabetSize; sym++)
{
int rm = 0;
int *MixRow = Ptr1Dto2D(Mix, sym, *AlphabetSize + 1);
for(int state = 0; state <= *AlphabetSize; state++)
{
MixRow[state] = rm;
if(state == sym)
rm += ProbScale - *AlphabetSize + 1;
else
rm += 1;
}
assert(MixRow[*AlphabetSize] == ProbScale);
}
}
QuasiModel::QuasiModel(int Alpha)
{
AlphabetSize = (int*)malloc(1 * sizeof(int));
if(Alpha <= 0)
Error("Alphabet size must be at least 1!");
*AlphabetSize = Alpha;
Freqs = (int*)malloc((*AlphabetSize) * sizeof(int));
CumFreqs = (int*)malloc((*AlphabetSize + 1) * sizeof(int));
if(Freqs == NULL)
Error("Failed to allocate frequency table!");
if(CumFreqs == NULL)
Error("Failed to allocate cumulative table!");
}
QuasiModel::~QuasiModel()
{
free(AlphabetSize);
free(Freqs);
free(CumFreqs);
}
unsigned int QuasiModel::SymToLow(unsigned short sym)
{
return CumFreqs[sym];
}
unsigned int QuasiModel::SymToFreq(unsigned short sym)
{
return CumFreqs[sym + 1] - CumFreqs[sym];
}
/**
* Fast symbol to range, with binary searches we'd require up to ceil(log2(alpha))*n operations per block to get all symbols.
* But with simple array mappings we need at most n+k operations per block, this is typically more efficient due to less operation dependencies.
*/
unsigned short QuasiModel::RangeToSym(unsigned int range)
{
return RangeToSymbol[range];
}
/**
* Increment symbol freq, conditionally update (stretch and rescale) the model if we've seen enough symbols
*/
void QuasiModel::Update(int symbol)
{
Freqs[symbol] += ProbBits;
SEEN++;
if(SEEN > EXP)
{
// New cumulative model
int Total = 0;
int Log = 0;
for(int i = 0; i < *AlphabetSize; i++)
Total += Freqs[i];
// Scale down
while(((Total >> Log) + *AlphabetSize) > ProbScale)
Log++;
// All symbols will now sum to less than ProbScale and be assigned a value of at least one
Total = 0;
for(int i = 0; i < *AlphabetSize; i++)
Total += Freqs[i] = (Freqs[i] >> Log) + 1;
// Stretch up
for(int i = 0; i < *AlphabetSize; i++)
Freqs[i] = ProbScale * Freqs[i] / Total;
Total = 0;
for(int i = 0; i < *AlphabetSize; i++)
Total += Freqs[i];
Freqs[0] += ProbScale - Total; // Fill the entire range if there's a remainder (this is usually the best symbol)
memset(CumFreqs, 0, (*AlphabetSize + 1) * sizeof(int));
for(int i = 0; i < *AlphabetSize; i++)
CumFreqs[i + 1] = CumFreqs[i] + Freqs[i];
assert(CumFreqs[*AlphabetSize] == ProbScale);
memset(Freqs, 0, *AlphabetSize * sizeof(int));
for(int sym = 0; sym < *AlphabetSize; sym++)
for(unsigned int i = CumFreqs[sym]; i < CumFreqs[sym + 1]; i++)
RangeToSymbol[i] = sym;
SEEN = 0;
EXP = (EXP < UPDATE_RATE) ? EXP << 1 : UPDATE_RATE;
}
}
/**
* Reinitialize the state of the model back to equal probabilities
*/
void QuasiModel::Reset()
{
memset(Freqs, 0, *AlphabetSize * sizeof(int));
memset(CumFreqs, 0, (*AlphabetSize + 1) * sizeof(int));
SEEN = 0;
EXP = 8;
int scale = ProbScale / *AlphabetSize;
for(int i = 0; i < *AlphabetSize; i++)
Freqs[i] = scale;
int ActualScale = 0;
for(int i = 0; i < *AlphabetSize; i++)
ActualScale += Freqs[i];
Freqs[0] += ProbScale - ActualScale; // Accommodate for any integer division errors
memset(CumFreqs, 0, (*AlphabetSize + 1) * sizeof(int));
for(int i = 0; i < *AlphabetSize; i++)
CumFreqs[i + 1] = CumFreqs[i] + Freqs[i];
assert(CumFreqs[*AlphabetSize] == ProbScale);
memset(Freqs, 0, *AlphabetSize * sizeof(int));
for(int sym = 0; sym < *AlphabetSize; sym++)
for(unsigned int i = CumFreqs[sym]; i < CumFreqs[sym + 1]; i++)
RangeToSymbol[i] = sym;
}