summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Marc Valin <jmvalin@jmvalin.ca>2011-11-17 19:21:07 +0800
committerJean-Marc Valin <jmvalin@jmvalin.ca>2012-07-03 14:39:42 -0400
commite65ff3492582fa0283779a5a460a5e16e2b7a36b (patch)
treed6b88949cfa5617ec93ca08614ea1c2934c932b5
parent14c58406327963355fef18742b82373919e1fcad (diff)
downloadopus-e65ff3492582fa0283779a5a460a5e16e2b7a36b.tar.gz
Speech/music discrimination (not used for anything yet)
Also, reducing the VBR rate on panned mono
-rw-r--r--celt/celt.c15
-rw-r--r--celt/celt.h1
-rw-r--r--src/analysis.c25
-rw-r--r--src/mlp.c103
-rw-r--r--src/mlp.h41
-rw-r--r--src/mlp_data.c77
-rw-r--r--src/mlp_train.c495
-rw-r--r--src/mlp_train.h86
-rw-r--r--src/tansig_table.h105
9 files changed, 946 insertions, 2 deletions
diff --git a/celt/celt.c b/celt/celt.c
index 6d50a279..8c5e56e4 100644
--- a/celt/celt.c
+++ b/celt/celt.c
@@ -791,7 +791,8 @@ static void init_caps(const CELTMode *m,int *cap,int LM,int C)
}
static int alloc_trim_analysis(const CELTMode *m, const celt_norm *X,
- const opus_val16 *bandLogE, int end, int LM, int C, int N0, AnalysisInfo *analysis)
+ const opus_val16 *bandLogE, int end, int LM, int C, int N0,
+ AnalysisInfo *analysis, opus_val16 *stereo_saving)
{
int i;
opus_val32 diff=0;
@@ -819,6 +820,12 @@ static int alloc_trim_analysis(const CELTMode *m, const celt_norm *X,
trim_index-=2;
else if (sum > QCONST16(.8f,10))
trim_index-=1;
+#ifndef FIXED_POINT
+ *stereo_saving = -.5*log2(1.01-sum*sum);
+ /*printf("%f\n", *stereo_saving);*/
+#else
+ *stereo_saving = 0;
+#endif
}
/* Estimate spectral tilt */
@@ -944,6 +951,7 @@ int celt_encode_with_ec(CELTEncoder * restrict st, const opus_val16 * pcm, int f
int anti_collapse_on=0;
int silence=0;
opus_val16 tf_estimate=0;
+ opus_val16 stereo_saving = 0;
ALLOC_STACK;
if (nbCompressedBytes<2 || pcm==NULL)
@@ -1409,7 +1417,7 @@ int celt_encode_with_ec(CELTEncoder * restrict st, const opus_val16 * pcm, int f
if (tell+(6<<BITRES) <= total_bits - total_boost)
{
alloc_trim = alloc_trim_analysis(st->mode, X, bandLogE,
- st->end, LM, C, N, &st->analysis);
+ st->end, LM, C, N, &st->analysis, &stereo_saving);
ec_enc_icdf(enc, alloc_trim, trim_icdf, 7);
tell = ec_tell_frac(enc);
}
@@ -1470,6 +1478,8 @@ int celt_encode_with_ec(CELTEncoder * restrict st, const opus_val16 * pcm, int f
#ifndef FIXED_POINT
if (st->analysis.valid && st->analysis.activity<.4)
target -= (coded_bins<<BITRES)*2*(.4-st->analysis.activity);
+
+ target -= MIN32(target/3, stereo_saving*(st->mode->eBands[intensity]<<LM<<BITRES));
#endif
#ifdef FIXED_POINT
@@ -1548,6 +1558,7 @@ int celt_encode_with_ec(CELTEncoder * restrict st, const opus_val16 * pcm, int f
/*printf ("+%d\n", adjust);*/
}
nbCompressedBytes = IMIN(nbCompressedBytes,nbAvailableBytes+nbFilledBytes);
+ /*printf("%d\n", nbCompressedBytes*50*8);*/
/* This moves the raw bits to take into account the new compressed size */
ec_enc_shrink(enc, nbCompressedBytes);
}
diff --git a/celt/celt.h b/celt/celt.h
index 54bca447..89484015 100644
--- a/celt/celt.h
+++ b/celt/celt.h
@@ -57,6 +57,7 @@ typedef struct {
opus_val16 activity;
int boost_band[2];
opus_val16 boost_amount[2];
+ opus_val16 music_prob;
}AnalysisInfo;
#define __celt_check_mode_ptr_ptr(ptr) ((ptr) + ((ptr) - (const CELTMode**)(ptr)))
diff --git a/src/analysis.c b/src/analysis.c
index 6c1db2f3..b39defe5 100644
--- a/src/analysis.c
+++ b/src/analysis.c
@@ -35,6 +35,10 @@
#include "arch.h"
#include "quant_bands.h"
#include <stdio.h>
+#ifndef FIXED_POINT
+#include "mlp.c"
+#include "mlp_data.c"
+#endif
#ifndef M_PI
#define M_PI 3.141592653
@@ -103,6 +107,7 @@ void tonality_analysis(TonalityAnalysisState *tonal, AnalysisInfo *info, CELTEnc
float slope=0;
float frame_stationarity;
float relativeE;
+ float frame_prob;
celt_encoder_ctl(celt_enc, CELT_GET_MODE(&mode));
kfft = mode->mdct.kfft[0];
@@ -294,6 +299,26 @@ void tonality_analysis(TonalityAnalysisState *tonal, AnalysisInfo *info, CELTEnc
features[25] = info->activity;
features[26] = frame_stationarity;
+#ifndef FIXED_POINT
+ mlp_process(&net, features, &frame_prob);
+ frame_prob = .5*(frame_prob+1);
+ /*frame_prob = .45*frame_prob + .55*frame_prob*frame_prob*frame_prob;*/
+ /*printf("%f\n", frame_prob);*/
+ {
+ float alpha, beta;
+ float p0, p1;
+ alpha = .01;
+ beta = .2;
+ p0 = (1-info->music_prob)*(1-alpha) + info->music_prob *alpha;
+ p1 = info->music_prob *(1-alpha) + (1-info->music_prob)*alpha;
+ p0 *= pow(1-frame_prob, beta);
+ p1 *= pow(frame_prob, beta);
+ info->music_prob = p1/(p0+p1);
+ /*printf("%f\n", info->music_prob);*/
+ }
+#else
+ info->music_prob = 0;
+#endif
/*for (i=0;i<27;i++)
printf("%f ", features[i]);
printf("\n");*/
diff --git a/src/mlp.c b/src/mlp.c
new file mode 100644
index 00000000..200f1cb3
--- /dev/null
+++ b/src/mlp.c
@@ -0,0 +1,103 @@
+/* Copyright (c) 2008-2011 Octasic Inc.
+ Written by Jean-Marc Valin */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include <math.h>
+#include "mlp.h"
+#include "arch.h"
+#include "tansig_table.h"
+#define MAX_NEURONS 100
+
+#ifdef FIXED_POINT
+extern const opus_val16 tansig_table[501];
+static inline opus_val16 tansig_approx(opus_val32 _x) /* Q19 */
+{
+ int i;
+ opus_val16 xx; /* Q11 */
+ /*double x, y;*/
+ opus_val16 dy, yy; /* Q14 */
+ /*x = 1.9073e-06*_x;*/
+ if (_x>=QCONST32(10,19))
+ return QCONST32(1.,14);
+ if (_x<=-QCONST32(10,19))
+ return -QCONST32(1.,14);
+ xx = EXTRACT16(SHR32(_x, 8));
+ /*i = lrint(25*x);*/
+ i = SHR32(ADD32(1024,MULT16_16(25, xx)),11);
+ /*x -= .04*i;*/
+ xx -= EXTRACT16(SHR32(MULT16_16(20972,i),8));
+ /*x = xx*(1./2048);*/
+ /*y = tansig_table[250+i];*/
+ yy = tansig_table[250+i];
+ /*y = yy*(1./16384);*/
+ dy = 16384-MULT16_16_Q14(yy,yy);
+ yy = yy + MULT16_16_Q14(MULT16_16_Q11(xx,dy),(16384 - MULT16_16_Q11(yy,xx)));
+ return yy;
+}
+#else
+/*extern const float tansig_table[501];*/
+static inline double tansig_approx(double x)
+{
+ int i;
+ double y, dy;
+ if (x>=10)
+ return 1;
+ if (x<=-10)
+ return -1;
+ i = lrint(25*x);
+ x -= .04*i;
+ y = tansig_table[250+i];
+ dy = 1-y*y;
+ y = y + x*dy*(1 - y*x);
+ return y;
+}
+#endif
+
+void mlp_process(const MLP *m, const opus_val16 *in, opus_val16 *out)
+{
+ int j;
+ opus_val16 hidden[MAX_NEURONS];
+ const opus_val16 *W = m->weights;
+ /* Copy to tmp_in */
+ for (j=0;j<m->topo[1];j++)
+ {
+ int k;
+ opus_val32 sum = SHL32(EXTEND32(*W++),8);
+ for (k=0;k<m->topo[0];k++)
+ sum = MAC16_16(sum, in[k],*W++);
+ hidden[j] = tansig_approx(sum);
+ }
+ for (j=0;j<m->topo[2];j++)
+ {
+ int k;
+ opus_val32 sum = SHL32(EXTEND32(*W++),14);
+ for (k=0;k<m->topo[1];k++)
+ sum = MAC16_16(sum, hidden[k], *W++);
+ out[j] = tansig_approx(EXTRACT16(PSHR32(sum,17)));
+ }
+}
+
diff --git a/src/mlp.h b/src/mlp.h
new file mode 100644
index 00000000..68ff68d8
--- /dev/null
+++ b/src/mlp.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2008-2011 Octasic Inc.
+ Written by Jean-Marc Valin */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _MLP_H_
+#define _MLP_H_
+
+#include "arch.h"
+
+typedef struct {
+ int layers;
+ const int *topo;
+ const opus_val16 *weights;
+} MLP;
+
+void mlp_process(const MLP *m, const opus_val16 *in, opus_val16 *out);
+
+#endif /* _MLP_H_ */
diff --git a/src/mlp_data.c b/src/mlp_data.c
new file mode 100644
index 00000000..fdd32db3
--- /dev/null
+++ b/src/mlp_data.c
@@ -0,0 +1,77 @@
+#include "mlp.h"
+
+/* RMS error was 0.196871, seed was 1321340808 */
+
+static const float weights[291] = {
+
+/* hidden layer */
+1.93994, 0.00636575, -0.0838112, 0.188811, -0.157845,
+-0.122662, 0.296779, -0.066386, -0.0764464, -0.00372055,
+-0.0397377, -0.000976218, -0.03931, 0.0111525, -0.0377797,
+-0.003592, 0.00213057, 0.115952, -0.0864595, 0.170621,
+-0.139312, -0.125683, 0.226746, -0.148058, -3.11536,
+-5.7119, -0.325896, -4.37802, 2.1242, -0.119952,
+0.0232531, -0.0998321, -0.0909719, -0.164338, 0.0370311,
+0.0196689, 0.0495503, -0.267277, -0.15925, -0.129835,
+-0.171845, -0.0672326, -0.0319364, -0.0960325, 0.132835,
+0.0978292, -0.0204049, -0.128357, -0.0582566, -0.21682,
+0.00496659, -0.0224912, -2.3249, 2.10627, -5.06275,
+0.300689, -1.05938, 0.111387, 0.100606, 0.122446,
+-0.0175274, 0.0107236, -0.030947, -0.0712338, -0.0456196,
+0.0158188, 0.0139863, 0.0122389, 0.0426144, 0.00963211,
+0.00741379, 0.014572, -0.0365356, 0.0780221, 0.0835844,
+0.101463, -0.0194471, 0.016752, -0.0360326, -0.0671933,
+5.35889, -6.06707, 1.35677, -1.90924, 0.0347801,
+0.0122876, 0.00258179, -0.0217294, 0.0827611, 0.0859281,
+-0.00417207, -0.109872, -0.238913, -0.288535, -0.0319008,
+0.156671, -0.00911369, -0.0351284, 0.0355504, 0.101236,
+-0.140194, -0.128439, 0.0275677, -0.0507381, 0.106048,
+0.0672367, 0.00438842, -0.0925318, 5.68238, -3.47798,
+0.246634, 0.0970976, -1.33011, 0.0498353, 0.179046,
+0.0162675, -0.102764, -0.227255, 0.234701, -0.00777973,
+0.0767733, -0.00420136, 0.0344874, -0.0332389, 0.062122,
+-0.0360523, 0.0461029, 0.0861842, 0.0136479, 0.0133092,
+0.165541, -0.0573712, -0.0694408, -0.196571, 0.222621,
+0.0197353, 3.42359, 5.23165, -1.10221, 3.66079,
+0.40144, -0.493484, 0.217106, -0.0143906, 0.295599,
+-0.614104, 0.596788, 0.956514, 0.107316, -0.172138,
+-0.111201, 0.0162694, -0.136564, 0.0567972, -0.107051,
+-0.0578785, 0.0597572, -0.592051, 0.11802, -0.0846178,
+0.144399, -0.386859, 0.429763, 0.763419, 8.40166,
+4.25269, -3.25962, 2.04492, -1.54948, 0.0286627,
+0.0855541, -0.128902, 0.0428149, 0.147296, -0.178688,
+0.582621, -0.0423034, -0.168806, -0.0930681, -0.0505222,
+-0.059881, 0.0344017, -0.0538223, -0.0095173, 0.044275,
+0.178126, 0.0321441, -0.192936, -0.0359919, 0.0449504,
+-0.255187, 0.330503, 14.3362, -12.7585, 2.10511,
+1.00446, -1.5146, 0.00315578, -0.0189675, 0.0506854,
+-0.0306224, -0.0343434, -0.0222091, 0.00040356, -0.179946,
+-0.213007, -0.046152, 0.0122855, 0.0335543, -0.0172102,
+0.0236597, 0.088535, -0.0980871, -0.129909, -0.019153,
+0.0544563, -0.0272701, -0.00304803, -0.00145721, 0.0190295,
+-6.75401, 2.83619, 2.38708, -0.904901, 0.670252,
+-0.0809205, -0.077534, -0.0347895, -0.0143415, -0.00527138,
+0.0400907, 0.041551, 0.00823289, -0.00772847, -0.0172196,
+-0.0125943, -0.0285652, -0.00141913, -0.010938, -0.0154068,
+0.0149916, -0.0577316, -0.0750255, -0.019028, -0.0175507,
+-0.00248046, 0.0350994, 0.0396102, -0.334886, 1.32123,
+-0.363775, 0.0925417, 7.5025, 0.76236, 0.489961,
+0.514362, 0.350457, 0.321636, -0.000131804, 0.0942301,
+0.506788, -0.325235, 0.162356, -0.147705, 0.155451,
+-0.111074, 0.120173, 0.0586432, 0.407685, 0.374031,
+0.510908, 0.25445, 0.285288, 0.184939, 0.0386202,
+0.089713, 12.6662, 2.54239, -14.9728, 7.46559,
+
+/* output layer */
+-4.63633, -1.25936, -1.33365, 4.91614, 1.1609,
+1.30642, -0.780207, 1.09432, -1.46686, 8.41454,
+1.55149, };
+
+static const int topo[3] = {27, 10, 1};
+
+const MLP net = {
+ 3,
+ topo,
+ weights
+};
+
diff --git a/src/mlp_train.c b/src/mlp_train.c
new file mode 100644
index 00000000..a9d548b1
--- /dev/null
+++ b/src/mlp_train.c
@@ -0,0 +1,495 @@
+/* Copyright (c) 2008-2011 Octasic Inc.
+ Written by Jean-Marc Valin */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "mlp_train.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <time.h>
+#include <signal.h>
+
+int stopped = 0;
+
+void handler(int sig)
+{
+ stopped = 1;
+ signal(sig, handler);
+}
+
+MLPTrain * mlp_init(int *topo, int nbLayers, float *inputs, float *outputs, int nbSamples)
+{
+ int i, j, k;
+ MLPTrain *net;
+ int inDim, outDim;
+ net = malloc(sizeof(*net));
+ net->topo = malloc(nbLayers*sizeof(net->topo[0]));
+ for (i=0;i<nbLayers;i++)
+ net->topo[i] = topo[i];
+ inDim = topo[0];
+ outDim = topo[nbLayers-1];
+ net->in_rate = malloc((inDim+1)*sizeof(net->in_rate[0]));
+ net->weights = malloc((nbLayers-1)*sizeof(net->weights));
+ net->best_weights = malloc((nbLayers-1)*sizeof(net->weights));
+ for (i=0;i<nbLayers-1;i++)
+ {
+ net->weights[i] = malloc((topo[i]+1)*topo[i+1]*sizeof(net->weights[0][0]));
+ net->best_weights[i] = malloc((topo[i]+1)*topo[i+1]*sizeof(net->weights[0][0]));
+ }
+ double inMean[inDim];
+ for (j=0;j<inDim;j++)
+ {
+ double std=0;
+ inMean[j] = 0;
+ for (i=0;i<nbSamples;i++)
+ {
+ inMean[j] += inputs[i*inDim+j];
+ std += inputs[i*inDim+j]*inputs[i*inDim+j];
+ }
+ inMean[j] /= nbSamples;
+ std /= nbSamples;
+ net->in_rate[1+j] = .5/(.0001+std);
+ std = std-inMean[j]*inMean[j];
+ if (std<.001)
+ std = .001;
+ std = 1/sqrt(inDim*std);
+ for (k=0;k<topo[1];k++)
+ net->weights[0][k*(topo[0]+1)+j+1] = randn(std);
+ }
+ net->in_rate[0] = 1;
+ for (j=0;j<topo[1];j++)
+ {
+ double sum = 0;
+ for (k=0;k<inDim;k++)
+ sum += inMean[k]*net->weights[0][j*(topo[0]+1)+k+1];
+ net->weights[0][j*(topo[0]+1)] = -sum;
+ }
+ for (j=0;j<outDim;j++)
+ {
+ double mean = 0;
+ double std;
+ for (i=0;i<nbSamples;i++)
+ mean += outputs[i*outDim+j];
+ mean /= nbSamples;
+ std = 1/sqrt(topo[nbLayers-2]);
+ net->weights[nbLayers-2][j*(topo[nbLayers-2]+1)] = mean;
+ for (k=0;k<topo[nbLayers-2];k++)
+ net->weights[nbLayers-2][j*(topo[nbLayers-2]+1)+k+1] = randn(std);
+ }
+ return net;
+}
+
+#define MAX_NEURONS 100
+
+double compute_gradient(MLPTrain *net, float *inputs, float *outputs, int nbSamples, double *W0_grad, double *W1_grad, double *error_rate)
+{
+ int i,j;
+ int s;
+ int inDim, outDim, hiddenDim;
+ int *topo;
+ double *W0, *W1;
+ double rms=0;
+ int W0_size, W1_size;
+ double hidden[MAX_NEURONS];
+ double netOut[MAX_NEURONS];
+ double error[MAX_NEURONS];
+
+ *error_rate = 0;
+ topo = net->topo;
+ inDim = net->topo[0];
+ hiddenDim = net->topo[1];
+ outDim = net->topo[2];
+ W0_size = (topo[0]+1)*topo[1];
+ W1_size = (topo[1]+1)*topo[2];
+ W0 = net->weights[0];
+ W1 = net->weights[1];
+ memset(W0_grad, 0, W0_size*sizeof(double));
+ memset(W1_grad, 0, W1_size*sizeof(double));
+ for (i=0;i<outDim;i++)
+ netOut[i] = outputs[i];
+ for (s=0;s<nbSamples;s++)
+ {
+ float *in, *out;
+ in = inputs+s*inDim;
+ out = outputs + s*outDim;
+ for (i=0;i<hiddenDim;i++)
+ {
+ double sum = W0[i*(inDim+1)];
+ for (j=0;j<inDim;j++)
+ sum += W0[i*(inDim+1)+j+1]*in[j];
+ hidden[i] = tansig_approx(sum);
+ }
+ for (i=0;i<outDim;i++)
+ {
+ double sum = W1[i*(hiddenDim+1)];
+ for (j=0;j<hiddenDim;j++)
+ sum += W1[i*(hiddenDim+1)+j+1]*hidden[j];
+ netOut[i] = tansig_approx(sum);
+ error[i] = out[i] - netOut[i];
+ rms += error[i]*error[i];
+ *error_rate += fabs(error[i])>1;
+ }
+ /* Back-propagate error */
+ for (i=0;i<outDim;i++)
+ {
+ float grad = 1-netOut[i]*netOut[i];
+ W1_grad[i*(hiddenDim+1)] += error[i]*grad;
+ for (j=0;j<hiddenDim;j++)
+ W1_grad[i*(hiddenDim+1)+j+1] += grad*error[i]*hidden[j];
+ }
+ for (i=0;i<hiddenDim;i++)
+ {
+ double grad;
+ grad = 0;
+ for (j=0;j<outDim;j++)
+ grad += error[j]*W1[j*(hiddenDim+1)+i+1];
+ grad *= 1-hidden[i]*hidden[i];
+ W0_grad[i*(inDim+1)] += grad;
+ for (j=0;j<inDim;j++)
+ W0_grad[i*(inDim+1)+j+1] += grad*in[j];
+ }
+ }
+ return rms;
+}
+
+#define NB_THREADS 8
+
+sem_t sem_begin[NB_THREADS];
+sem_t sem_end[NB_THREADS];
+
+struct GradientArg {
+ int id;
+ int done;
+ MLPTrain *net;
+ float *inputs;
+ float *outputs;
+ int nbSamples;
+ double *W0_grad;
+ double *W1_grad;
+ double rms;
+ double error_rate;
+};
+
+void *gradient_thread_process(void *_arg)
+{
+ int W0_size, W1_size;
+ struct GradientArg *arg = _arg;
+ int *topo = arg->net->topo;
+ W0_size = (topo[0]+1)*topo[1];
+ W1_size = (topo[1]+1)*topo[2];
+ double W0_grad[W0_size];
+ double W1_grad[W1_size];
+ arg->W0_grad = W0_grad;
+ arg->W1_grad = W1_grad;
+ while (1)
+ {
+ sem_wait(&sem_begin[arg->id]);
+ if (arg->done)
+ break;
+ arg->rms = compute_gradient(arg->net, arg->inputs, arg->outputs, arg->nbSamples, arg->W0_grad, arg->W1_grad, &arg->error_rate);
+ sem_post(&sem_end[arg->id]);
+ }
+ fprintf(stderr, "done\n");
+ return NULL;
+}
+
+float mlp_train_backprop(MLPTrain *net, float *inputs, float *outputs, int nbSamples, int nbEpoch, float rate)
+{
+ int i, j;
+ int e;
+ float best_rms = 1e10;
+ int inDim, outDim, hiddenDim;
+ int *topo;
+ double *W0, *W1, *best_W0, *best_W1;
+ double *W0_old, *W1_old;
+ double *W0_old2, *W1_old2;
+ double *W0_grad, *W1_grad;
+ double *W0_oldgrad, *W1_oldgrad;
+ double *W0_rate, *W1_rate;
+ double *best_W0_rate, *best_W1_rate;
+ int W0_size, W1_size;
+ topo = net->topo;
+ W0_size = (topo[0]+1)*topo[1];
+ W1_size = (topo[1]+1)*topo[2];
+ struct GradientArg args[NB_THREADS];
+ pthread_t thread[NB_THREADS];
+ int samplePerPart = nbSamples/NB_THREADS;
+ int count_worse=0;
+ int count_retries=0;
+
+ topo = net->topo;
+ inDim = net->topo[0];
+ hiddenDim = net->topo[1];
+ outDim = net->topo[2];
+ W0 = net->weights[0];
+ W1 = net->weights[1];
+ best_W0 = net->best_weights[0];
+ best_W1 = net->best_weights[1];
+ W0_old = malloc(W0_size*sizeof(double));
+ W1_old = malloc(W1_size*sizeof(double));
+ W0_old2 = malloc(W0_size*sizeof(double));
+ W1_old2 = malloc(W1_size*sizeof(double));
+ W0_grad = malloc(W0_size*sizeof(double));
+ W1_grad = malloc(W1_size*sizeof(double));
+ W0_oldgrad = malloc(W0_size*sizeof(double));
+ W1_oldgrad = malloc(W1_size*sizeof(double));
+ W0_rate = malloc(W0_size*sizeof(double));
+ W1_rate = malloc(W1_size*sizeof(double));
+ best_W0_rate = malloc(W0_size*sizeof(double));
+ best_W1_rate = malloc(W1_size*sizeof(double));
+ memcpy(W0_old, W0, W0_size*sizeof(double));
+ memcpy(W0_old2, W0, W0_size*sizeof(double));
+ memset(W0_grad, 0, W0_size*sizeof(double));
+ memset(W0_oldgrad, 0, W0_size*sizeof(double));
+ memcpy(W1_old, W1, W1_size*sizeof(double));
+ memcpy(W1_old2, W1, W1_size*sizeof(double));
+ memset(W1_grad, 0, W1_size*sizeof(double));
+ memset(W1_oldgrad, 0, W1_size*sizeof(double));
+
+ rate /= nbSamples;
+ for (i=0;i<hiddenDim;i++)
+ for (j=0;j<inDim+1;j++)
+ W0_rate[i*(inDim+1)+j] = rate*net->in_rate[j];
+ for (i=0;i<W1_size;i++)
+ W1_rate[i] = rate;
+
+ for (i=0;i<NB_THREADS;i++)
+ {
+ args[i].net = net;
+ args[i].inputs = inputs+i*samplePerPart*inDim;
+ args[i].outputs = outputs+i*samplePerPart*outDim;
+ args[i].nbSamples = samplePerPart;
+ args[i].id = i;
+ args[i].done = 0;
+ sem_init(&sem_begin[i], 0, 0);
+ sem_init(&sem_end[i], 0, 0);
+ pthread_create(&thread[i], NULL, gradient_thread_process, &args[i]);
+ }
+ for (e=0;e<nbEpoch;e++)
+ {
+ double rms=0;
+ double error_rate = 0;
+ for (i=0;i<NB_THREADS;i++)
+ {
+ sem_post(&sem_begin[i]);
+ }
+ memset(W0_grad, 0, W0_size*sizeof(double));
+ memset(W1_grad, 0, W1_size*sizeof(double));
+ for (i=0;i<NB_THREADS;i++)
+ {
+ sem_wait(&sem_end[i]);
+ rms += args[i].rms;
+ error_rate += args[i].error_rate;
+ for (j=0;j<W0_size;j++)
+ W0_grad[j] += args[i].W0_grad[j];
+ for (j=0;j<W1_size;j++)
+ W1_grad[j] += args[i].W1_grad[j];
+ }
+
+ float mean_rate = 0, min_rate = 1e10;
+ rms = (rms/(outDim*nbSamples));
+ error_rate = (error_rate/(outDim*nbSamples));
+ fprintf (stderr, "%f (%f %f) ", error_rate, rms, best_rms);
+ if (rms < best_rms)
+ {
+ best_rms = rms;
+ for (i=0;i<W0_size;i++)
+ {
+ best_W0[i] = W0[i];
+ best_W0_rate[i] = W0_rate[i];
+ }
+ for (i=0;i<W1_size;i++)
+ {
+ best_W1[i] = W1[i];
+ best_W1_rate[i] = W1_rate[i];
+ }
+ count_worse=0;
+ count_retries=0;
+ } else {
+ count_worse++;
+ if (count_worse>30)
+ {
+ count_retries++;
+ count_worse=0;
+ for (i=0;i<W0_size;i++)
+ {
+ W0[i] = best_W0[i];
+ best_W0_rate[i] *= .7;
+ if (best_W0_rate[i]<1e-15) best_W0_rate[i]=1e-15;
+ W0_rate[i] = best_W0_rate[i];
+ W0_grad[i] = 0;
+ }
+ for (i=0;i<W1_size;i++)
+ {
+ W1[i] = best_W1[i];
+ best_W1_rate[i] *= .8;
+ if (best_W1_rate[i]<1e-15) best_W1_rate[i]=1e-15;
+ W1_rate[i] = best_W1_rate[i];
+ W1_grad[i] = 0;
+ }
+ }
+ }
+ if (count_retries>10)
+ break;
+ for (i=0;i<W0_size;i++)
+ {
+ if (W0_oldgrad[i]*W0_grad[i] > 0)
+ W0_rate[i] *= 1.01;
+ else if (W0_oldgrad[i]*W0_grad[i] < 0)
+ W0_rate[i] *= .9;
+ mean_rate += W0_rate[i];
+ if (W0_rate[i] < min_rate)
+ min_rate = W0_rate[i];
+ if (W0_rate[i] < 1e-15)
+ W0_rate[i] = 1e-15;
+ /*if (W0_rate[i] > .01)
+ W0_rate[i] = .01;*/
+ W0_oldgrad[i] = W0_grad[i];
+ W0_old2[i] = W0_old[i];
+ W0_old[i] = W0[i];
+ W0[i] += W0_grad[i]*W0_rate[i];
+ }
+ for (i=0;i<W1_size;i++)
+ {
+ if (W1_oldgrad[i]*W1_grad[i] > 0)
+ W1_rate[i] *= 1.01;
+ else if (W1_oldgrad[i]*W1_grad[i] < 0)
+ W1_rate[i] *= .9;
+ mean_rate += W1_rate[i];
+ if (W1_rate[i] < min_rate)
+ min_rate = W1_rate[i];
+ if (W1_rate[i] < 1e-15)
+ W1_rate[i] = 1e-15;
+ W1_oldgrad[i] = W1_grad[i];
+ W1_old2[i] = W1_old[i];
+ W1_old[i] = W1[i];
+ W1[i] += W1_grad[i]*W1_rate[i];
+ }
+ mean_rate /= (topo[0]+1)*topo[1] + (topo[1]+1)*topo[2];
+ fprintf (stderr, "%g %d", mean_rate, e);
+ if (count_retries)
+ fprintf(stderr, " %d", count_retries);
+ fprintf(stderr, "\n");
+ if (stopped)
+ break;
+ }
+ for (i=0;i<NB_THREADS;i++)
+ {
+ args[i].done = 1;
+ sem_post(&sem_begin[i]);
+ pthread_join(thread[i], NULL);
+ fprintf (stderr, "joined %d\n", i);
+ }
+ free(W0_old);
+ free(W1_old);
+ free(W0_grad);
+ free(W1_grad);
+ free(W0_rate);
+ free(W1_rate);
+ return best_rms;
+}
+
+int main(int argc, char **argv)
+{
+ int i, j;
+ int nbInputs;
+ int nbOutputs;
+ int nbHidden;
+ int nbSamples;
+ int nbEpoch;
+ int nbRealInputs;
+ unsigned int seed;
+ int ret;
+ float rms;
+ float *inputs;
+ float *outputs;
+ if (argc!=6)
+ {
+ fprintf (stderr, "usage: mlp_train <inputs> <hidden> <outputs> <nb samples> <nb epoch>\n");
+ return 1;
+ }
+ nbInputs = atoi(argv[1]);
+ nbHidden = atoi(argv[2]);
+ nbOutputs = atoi(argv[3]);
+ nbSamples = atoi(argv[4]);
+ nbEpoch = atoi(argv[5]);
+ nbRealInputs = nbInputs;
+ inputs = malloc(nbInputs*nbSamples*sizeof(*inputs));
+ outputs = malloc(nbOutputs*nbSamples*sizeof(*outputs));
+
+ seed = time(NULL);
+ fprintf (stderr, "Seed is %u\n", seed);
+ srand(seed);
+ build_tansig_table();
+ signal(SIGTERM, handler);
+ signal(SIGINT, handler);
+ signal(SIGHUP, handler);
+ for (i=0;i<nbSamples;i++)
+ {
+ for (j=0;j<nbRealInputs;j++)
+ ret = scanf(" %f", &inputs[i*nbInputs+j]);
+ for (j=0;j<nbOutputs;j++)
+ ret = scanf(" %f", &outputs[i*nbOutputs+j]);
+ if (feof(stdin))
+ {
+ nbSamples = i;
+ break;
+ }
+ }
+ int topo[3] = {nbInputs, nbHidden, nbOutputs};
+ MLPTrain *net;
+
+ fprintf (stderr, "Got %d samples\n", nbSamples);
+ net = mlp_init(topo, 3, inputs, outputs, nbSamples);
+ rms = mlp_train_backprop(net, inputs, outputs, nbSamples, nbEpoch, 1);
+ printf ("#include \"mlp.h\"\n\n");
+ printf ("/* RMS error was %f, seed was %u */\n\n", rms, seed);
+ printf ("static const float weights[%d] = {\n", (topo[0]+1)*topo[1] + (topo[1]+1)*topo[2]);
+ printf ("\n/* hidden layer */\n");
+ for (i=0;i<(topo[0]+1)*topo[1];i++)
+ {
+ printf ("%g, ", net->weights[0][i]);
+ if (i%5==4)
+ printf("\n");
+ }
+ printf ("\n/* output layer */\n");
+ for (i=0;i<(topo[1]+1)*topo[2];i++)
+ {
+ printf ("%g, ", net->weights[1][i]);
+ if (i%5==4)
+ printf("\n");
+ }
+ printf ("};\n\n");
+ printf ("static const int topo[3] = {%d, %d, %d};\n\n", topo[0], topo[1], topo[2]);
+ printf ("const MLP net = {\n");
+ printf ("\t3,\n");
+ printf ("\ttopo,\n");
+ printf ("\tweights\n};\n");
+ return 0;
+}
diff --git a/src/mlp_train.h b/src/mlp_train.h
new file mode 100644
index 00000000..1857f644
--- /dev/null
+++ b/src/mlp_train.h
@@ -0,0 +1,86 @@
+/* Copyright (c) 2008-2011 Octasic Inc.
+ Written by Jean-Marc Valin */
+/*
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _MLP_TRAIN_H_
+#define _MLP_TRAIN_H_
+
+#include <math.h>
+#include <stdlib.h>
+
+double tansig_table[501];
+static inline double tansig_double(double x)
+{
+ return 2./(1.+exp(-2.*x)) - 1.;
+}
+static inline void build_tansig_table()
+{
+ int i;
+ for (i=0;i<501;i++)
+ tansig_table[i] = tansig_double(.04*(i-250));
+}
+
+static inline double tansig_approx(double x)
+{
+ int i;
+ double y, dy;
+ if (x>=10)
+ return 1;
+ if (x<=-10)
+ return -1;
+ i = lrint(25*x);
+ x -= .04*i;
+ y = tansig_table[250+i];
+ dy = 1-y*y;
+ y = y + x*dy*(1 - y*x);
+ return y;
+}
+
+inline float randn(float sd)
+{
+ float U1, U2, S, x;
+ do {
+ U1 = ((float)rand())/RAND_MAX;
+ U2 = ((float)rand())/RAND_MAX;
+ U1 = 2*U1-1;
+ U2 = 2*U2-1;
+ S = U1*U1 + U2*U2;
+ } while (S >= 1 || S == 0.0f);
+ x = sd*sqrt(-2 * log(S) / S) * U1;
+ return x;
+}
+
+
+typedef struct {
+ int layers;
+ int *topo;
+ double **weights;
+ double **best_weights;
+ double *in_rate;
+} MLPTrain;
+
+
+#endif /* _MLP_TRAIN_H_ */
diff --git a/src/tansig_table.h b/src/tansig_table.h
new file mode 100644
index 00000000..ccf43daa
--- /dev/null
+++ b/src/tansig_table.h
@@ -0,0 +1,105 @@
+/* This file is auto-generated by gen_tables */
+
+static const opus_val16 tansig_table[501] = {
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -1.000000, -1.000000, -1.000000, -1.000000,
+-1.000000, -0.999999, -0.999999, -0.999999, -0.999999,
+-0.999999, -0.999999, -0.999999, -0.999999, -0.999999,
+-0.999999, -0.999999, -0.999999, -0.999999, -0.999998,
+-0.999998, -0.999998, -0.999998, -0.999998, -0.999998,
+-0.999997, -0.999997, -0.999997, -0.999997, -0.999997,
+-0.999996, -0.999996, -0.999996, -0.999995, -0.999995,
+-0.999994, -0.999994, -0.999994, -0.999993, -0.999992,
+-0.999992, -0.999991, -0.999990, -0.999990, -0.999989,
+-0.999988, -0.999987, -0.999986, -0.999984, -0.999983,
+-0.999982, -0.999980, -0.999978, -0.999977, -0.999975,
+-0.999973, -0.999970, -0.999968, -0.999965, -0.999962,
+-0.999959, -0.999956, -0.999952, -0.999948, -0.999944,
+-0.999939, -0.999934, -0.999929, -0.999923, -0.999916,
+-0.999909, -0.999902, -0.999893, -0.999885, -0.999875,
+-0.999865, -0.999853, -0.999841, -0.999828, -0.999813,
+-0.999798, -0.999781, -0.999763, -0.999743, -0.999722,
+-0.999699, -0.999673, -0.999646, -0.999617, -0.999585,
+-0.999550, -0.999513, -0.999472, -0.999428, -0.999381,
+-0.999329, -0.999273, -0.999213, -0.999147, -0.999076,
+-0.999000, -0.998916, -0.998826, -0.998728, -0.998623,
+-0.998508, -0.998384, -0.998249, -0.998104, -0.997946,
+-0.997775, -0.997590, -0.997389, -0.997172, -0.996937,
+-0.996682, -0.996407, -0.996108, -0.995784, -0.995434,
+-0.995055, -0.994644, -0.994199, -0.993718, -0.993196,
+-0.992631, -0.992020, -0.991359, -0.990642, -0.989867,
+-0.989027, -0.988119, -0.987136, -0.986072, -0.984921,
+-0.983675, -0.982327, -0.980869, -0.979293, -0.977587,
+-0.975743, -0.973749, -0.971594, -0.969265, -0.966747,
+-0.964028, -0.961090, -0.957917, -0.954492, -0.950795,
+-0.946806, -0.942503, -0.937863, -0.932862, -0.927473,
+-0.921669, -0.915420, -0.908698, -0.901468, -0.893698,
+-0.885352, -0.876393, -0.866784, -0.856485, -0.845456,
+-0.833655, -0.821040, -0.807569, -0.793199, -0.777888,
+-0.761594, -0.744277, -0.725897, -0.706419, -0.685809,
+-0.664037, -0.641077, -0.616909, -0.591519, -0.564900,
+-0.537050, -0.507977, -0.477700, -0.446244, -0.413644,
+-0.379949, -0.345214, -0.309507, -0.272905, -0.235496,
+-0.197375, -0.158649, -0.119427, -0.079830, -0.039979,
+0.000000, 0.039979, 0.079830, 0.119427, 0.158649,
+0.197375, 0.235496, 0.272905, 0.309507, 0.345214,
+0.379949, 0.413644, 0.446244, 0.477700, 0.507977,
+0.537050, 0.564900, 0.591519, 0.616909, 0.641077,
+0.664037, 0.685809, 0.706419, 0.725897, 0.744277,
+0.761594, 0.777888, 0.793199, 0.807569, 0.821040,
+0.833655, 0.845456, 0.856485, 0.866784, 0.876393,
+0.885352, 0.893698, 0.901468, 0.908698, 0.915420,
+0.921669, 0.927473, 0.932862, 0.937863, 0.942503,
+0.946806, 0.950795, 0.954492, 0.957917, 0.961090,
+0.964028, 0.966747, 0.969265, 0.971594, 0.973749,
+0.975743, 0.977587, 0.979293, 0.980869, 0.982327,
+0.983675, 0.984921, 0.986072, 0.987136, 0.988119,
+0.989027, 0.989867, 0.990642, 0.991359, 0.992020,
+0.992631, 0.993196, 0.993718, 0.994199, 0.994644,
+0.995055, 0.995434, 0.995784, 0.996108, 0.996407,
+0.996682, 0.996937, 0.997172, 0.997389, 0.997590,
+0.997775, 0.997946, 0.998104, 0.998249, 0.998384,
+0.998508, 0.998623, 0.998728, 0.998826, 0.998916,
+0.999000, 0.999076, 0.999147, 0.999213, 0.999273,
+0.999329, 0.999381, 0.999428, 0.999472, 0.999513,
+0.999550, 0.999585, 0.999617, 0.999646, 0.999673,
+0.999699, 0.999722, 0.999743, 0.999763, 0.999781,
+0.999798, 0.999813, 0.999828, 0.999841, 0.999853,
+0.999865, 0.999875, 0.999885, 0.999893, 0.999902,
+0.999909, 0.999916, 0.999923, 0.999929, 0.999934,
+0.999939, 0.999944, 0.999948, 0.999952, 0.999956,
+0.999959, 0.999962, 0.999965, 0.999968, 0.999970,
+0.999973, 0.999975, 0.999977, 0.999978, 0.999980,
+0.999982, 0.999983, 0.999984, 0.999986, 0.999987,
+0.999988, 0.999989, 0.999990, 0.999990, 0.999991,
+0.999992, 0.999992, 0.999993, 0.999994, 0.999994,
+0.999994, 0.999995, 0.999995, 0.999996, 0.999996,
+0.999996, 0.999997, 0.999997, 0.999997, 0.999997,
+0.999997, 0.999998, 0.999998, 0.999998, 0.999998,
+0.999998, 0.999998, 0.999999, 0.999999, 0.999999,
+0.999999, 0.999999, 0.999999, 0.999999, 0.999999,
+0.999999, 0.999999, 0.999999, 0.999999, 0.999999,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000, 1.000000, 1.000000, 1.000000, 1.000000,
+1.000000,
+};