Skip to content
Commits on Source (8)
......@@ -29,12 +29,12 @@
/* The Matrix organization is as follows: */
/* */
/* nx = numberOfUnitsInLayer[layer] */
/* ny = numberOfUnitsInLayer[layer-1]+1 */
/* xmin = 1 xmax = nx */
/* ymin = 1 ymax = ny */
/* dx = dy = 1 */
/* x1 = y1 = 1 */
/* nx == numberOfUnitsInLayer[layer] */
/* ny == numberOfUnitsInLayer[layer-1]+1 */
/* xmin == 1, xmax == nx */
/* ymin == 1, ymax == ny */
/* dx == dy == 1 */
/* x1 == y1 == 1 */
/* */
autoMatrix FFNet_weightsToMatrix (FFNet me, integer layer, bool deltaWeights);
......
/* FFNet_PatternList_ActivationList.cpp
*
* Copyright (C) 1994-2018 David Weenink, 2015,2017 Paul Boersma
* Copyright (C) 1994-2019 David Weenink, 2015,2017 Paul Boersma
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -27,7 +27,7 @@
#include "Graphics.h"
#include "FFNet_PatternList_ActivationList.h"
static double func (Daata object, VEC p) {
static double func (Daata object, VEC const& p) {
FFNet me = (FFNet) object;
Minimizer thee = my minimizer.get();
longdouble fp = 0.0;
......@@ -45,11 +45,11 @@ static double func (Daata object, VEC p) {
for (integer k = 1; k <= my numberOfWeights; k ++)
my dw [k] += my dwi [k];
}
thy funcCalls ++;
thy numberOfFunctionCalls ++;
return (double) fp;
}
static void dfunc_optimized (Daata object, VEC /* p */, VEC dp) {
static void dfunc_optimized (Daata object, VEC const& /* p */, VEC const& dp) {
FFNet me = (FFNet) object;
integer j = 1;
......
......@@ -63,12 +63,12 @@ autoPowerCepstrum PowerCepstrum_create (double qmax, integer nq);
/* Preconditions:
nq >= 2;
Postconditions:
my xmin = 0; my ymin = 1;
my xmax = qmax; my ymax = 1;
my nx = nq; my ny = 1;
my dx = qmax / (nq -1); my dy = 1;
my x1 = 0; my y1 = 1;
my z [1..ny] [1..nx] = 0.0;
my xmin == 0; my ymin == 1;
my xmax == qmax; my ymax == 1;
my nx == nq; my ny == 1;
my dx == qmax / (nq -1); my dy == 1;
my x1 == 0.0; my y1 == 1.0;
my z [1..ny] [1..nx] == 0.0;
*/
void PowerCepstrum_draw (PowerCepstrum me, Graphics g, double qmin, double qmax, double dBminimum, double dBmaximum, int garnish);
......
......@@ -7,6 +7,36 @@ What's new?
<p>
Latest changes in Praat.</p>
<p>
<b>6.0.56</b> (20 June 2019)</p>
<ul>
<li>
&nbsp;Windows: file dropping on the Praat icon works for higher-Unicode file names.
<li>
&nbsp;SpellingChecker: Unicode support.
</ul>
<p>
<b>6.0.55</b> (13 June 2019)</p>
<ul>
<li>
&nbsp;Unicode normalization in file names.
</ul>
<p>
<b>6.0.54</b> (6 June 2019)</p>
<ul>
<li>
&nbsp;Removed a bug introduced in 6.0.51 that could cause incorrect axes in Demo window.
</ul>
<p>
<b>6.0.53</b> (26 May 2019)</p>
<ul>
<li>
&nbsp;Much faster playing of short parts of long sounds that need resampling.
<li>
&nbsp;Better handling of broken CSV files.
<li>
&nbsp;64-bit floating-point WAV files.
</ul>
<p>
<b>6.0.52</b> (2 May 2019)</p>
<ul>
<li>
......@@ -680,7 +710,7 @@ What used to be new?</h3>
</ul>
<hr>
<address>
<p>&copy; ppgb, May 2, 2019</p>
<p>&copy; ppgb, June 20, 2019</p>
</address>
</body>
</html>
praat (6.0.56-1) unstable; urgency=medium
* New upstream version 6.0.56
* Upload to unstable
* d/control: Bump Standards-Version to 4.4.0 (no changes needed)
* Add lacking file for unit test test/kar/unicode.praat.
See https://github.com/praat/praat/pull/1018 for details.
-- Rafael Laboissiere <rafael@debian.org> Wed, 10 Jul 2019 07:41:08 -0300
praat (6.0.52-2) experimental; urgency=medium
* d/t/run-tests: Exclude unit test test_Discriminant (fails on i386)
......
......@@ -17,3 +17,4 @@ praat.1
praat_nogui.1
sendpraat.1
praat-open-files.1
test/kar/éééürtüéŋəü.wav
......@@ -12,7 +12,7 @@ Build-Depends: debhelper-compat (= 12),
xauth,
xvfb,
xmlto
Standards-Version: 4.3.0
Standards-Version: 4.4.0
Vcs-Browser: https://salsa.debian.org/med-team/praat
Vcs-Git: https://salsa.debian.org/med-team/praat.git
Homepage: http://www.praat.org
......
......@@ -149,7 +149,7 @@ Copyright: 1996-2004, 2006-2007 Brian Gough
License: GPL-3+
Files: debian/*
Copyright: 2005-2009, 2012-2018 Rafael Laboissière <rafael@debian.org>
Copyright: 2005-2009, 2012-2019 Rafael Laboissière <rafael@debian.org>
2009-2011 Andreas Tille <tille@debian.org>
License: GPL-3+
......
......@@ -69,6 +69,8 @@ sendpraat: sys/sendpraat.c
$(shell $(PKG_CONFIG) --cflags --libs gtk+-2.0)
override_dh_auto_build: sendpraat praat.1 praat_nogui.1 sendpraat.1 praat-open-files.1
# Copy lacking file for unit test unicode.praat
cp debian/éééürtüéŋəü.wav test/kar
dh_auto_build
sed 's/^LIBS = /& -Wl,--as-needed /' \
makefiles/makefile.defs.linux.nogui > makefile.defs
......
debian/éééürtüéŋəü.wav
......@@ -1536,14 +1536,14 @@ double NUMcubicSplineInterpolation (constVEC x, constVEC y, constVEC y2, double
integer klo = 1, khi = x.size;
while (khi - klo > 1) {
integer k = (khi + klo) >> 1;
if (x [k] > xin) {
if (x [k] > xin)
khi = k;
} else {
else
klo = k;
}
}
double h = x [khi] - x [klo];
Melder_require (h != 0.0, U"NUMcubicSplineInterpolation: bad input value.");
Melder_require (h != 0.0,
U"NUMcubicSplineInterpolation: bad input value.");
double a = (x [khi] - xin) / h;
double b = (xin - x [klo]) / h;
......
......@@ -1196,7 +1196,7 @@ inline double NUMmean_weighted (constVEC x, constVEC w) {
return inproduct / wsum;
}
inline void VECchainRows_preallocated (VEC v, MAT m) {
inline void VECchainRows_preallocated (VECVU const& v, constMATVU const& m) {
Melder_assert (m.nrow * m.ncol == v.size);
integer k = 1;
for (integer irow = 1; irow <= m.nrow; irow ++)
......@@ -1204,13 +1204,13 @@ inline void VECchainRows_preallocated (VEC v, MAT m) {
v [k ++] = m [irow] [icol];
}
inline autoVEC VECchainRows (MAT m) {
inline autoVEC VECchainRows (constMATVU const& m) {
autoVEC result = newVECraw (m.nrow * m.ncol);
VECchainRows_preallocated (result.get(), m);
return result;
}
inline void VECchainColumns_preallocated (VEC v, MAT m) {
inline void VECchainColumns_preallocated (VEC const& v, constMATVU const& m) {
Melder_assert (m.nrow * m.ncol == v.size);
integer k = 1;
for (integer icol = 1; icol <= m.ncol; icol ++)
......@@ -1218,7 +1218,7 @@ inline void VECchainColumns_preallocated (VEC v, MAT m) {
v [k ++] = m [irow] [icol];
}
inline autoVEC VECchainColumns (MAT m) {
inline autoVEC VECchainColumns (constMATVU const& m) {
autoVEC result = newVECraw (m.nrow * m.ncol);
VECchainColumns_preallocated (result.get(), m);
return result;
......
......@@ -164,7 +164,7 @@ procedure testDissimilarityInterface
.mdsCommand$ [5] = "To Configuration (absolute mds): "
.extraParameters$ [5] = ""
.mdsCommand$ [6] = "To Configuration (kruskal): "
.extraParameters$ [6] = """Primary approach"", ""Formula1"", "
.extraParameters$ [6] = """Primary approach"", ""Kruskal's stress-1"", "
# Create a random configuration
.command$ = .mdsCommand$ [1] + .numberOfDimensions$ [1] + .extraParameters$ [1] + .minimizationParameters$
......@@ -196,8 +196,10 @@ procedure testDissimilarityInterface
.stressMeasure$ [4] = "Raw"
.tiesHandling$ [1] = "Primary approach"
.tiesHandling$ [2] = "Secondary approach"
.stressCalculation$ [1] = "Formula1"
.stressCalculation$ [2] = "Formula2"
;.stressCalculation$ [1] = "Formula1"
;.stressCalculation$ [2] = "Formula2"
.stressCalculation$ [1] = "Kruskal's stress-1"
.stressCalculation$ [2] = "Kruskal's stress-1"
# test kruskal's stress-1 and stress-2
......
......@@ -21,7 +21,9 @@ procedure test_simple
.dist = Get Euclidean distance
Improve factorization (m.u.): 10, 1e-09, 1e-09, "yes"
.dist2 = Get Euclidean distance
assert .dist2 <= .dist
# if we are very near the minimum assert .dist2 <= .dist is not always true
# abs (.dist2 - .dist) <= 1e-09 makes sure that we are within tolerace to the minimum.
assert .dist2 <= .dist || abs (.dist2 - .dist) <= 1e-09
appendInfoLine: tab$, tab$, .nrow, "x", .ncol, ", aprox = ", .nfeatures, " 2-norm=", .dist2
removeObject: .mat, .nmf
endfor
......
......@@ -30,7 +30,7 @@ autoTableOfReal CCA_Correlation_factorLoadings (CCA me, Correlation thee) {
try {
integer ny = my y -> dimension, nx = my x -> dimension;
Melder_require (ny + nx == thy numberOfColumns,
U"The number of columns in the Correlation should equal the sum of the dimensions in the CCA object");
U"The number of columns in the Correlation should equal the sum of the dimensions in the CCA object.");
autoTableOfReal him = TableOfReal_create (2 * my numberOfCoefficients, thy numberOfColumns);
his columnLabels.all() <<= thy columnLabels.all();
......@@ -53,7 +53,7 @@ autoTableOfReal CCA_Correlation_factorLoadings (CCA me, Correlation thee) {
static void _CCA_Correlation_check (CCA me, Correlation thee, integer canonicalVariate_from, integer canonicalVariate_to) {
Melder_require (my y -> dimension + my x -> dimension == thy numberOfColumns,
U"The number of columns in the Correlation object should equal the sum of the dimensions in the CCA object");
U"The number of columns in the Correlation object should equal the sum of the dimensions in the CCA object.");
Melder_require (canonicalVariate_to >= canonicalVariate_from,
U"The second value in the \"Canonical variate range\" should be equal or larger than the first.");
Melder_require (canonicalVariate_from > 0 && canonicalVariate_to <= my numberOfCoefficients,
......
......@@ -42,7 +42,8 @@ Thing_implement (ComplexSpectrogram, Matrix, 2);
autoComplexSpectrogram ComplexSpectrogram_create (double tmin, double tmax, integer nt, double dt,
double t1, double fmin, double fmax, integer nf, double df, double f1) {
double t1, double fmin, double fmax, integer nf, double df, double f1)
{
try {
autoComplexSpectrogram me = Thing_new (ComplexSpectrogram);
Matrix_init (me.get(), tmin, tmax, nt, dt, t1, fmin, fmax, nf, df, f1);
......@@ -55,34 +56,35 @@ autoComplexSpectrogram ComplexSpectrogram_create (double tmin, double tmax, inte
autoComplexSpectrogram Sound_to_ComplexSpectrogram (Sound me, double windowLength, double timeStep) {
try {
double samplingFrequency = 1.0 / my dx, myDuration = my xmax - my xmin, t1;
const double samplingFrequency = 1.0 / my dx, myDuration = my xmax - my xmin;
Melder_require (windowLength <= myDuration,
U"Your sound is too short:\nit should be at least as long as one window length.");
U"Your sound is too short: it should be at least as long as one window length.");
integer nsamp_window = Melder_ifloor (windowLength / my dx);
integer halfnsamp_window = nsamp_window / 2 - 1;
const integer halfnsamp_window = nsamp_window / 2 - 1;
nsamp_window = halfnsamp_window * 2;
Melder_require (nsamp_window > 1,
U"There should be at least two samples in the window.");
integer numberOfFrames;
double t1;
Sampled_shortTermAnalysis (me, windowLength, timeStep, & numberOfFrames, & t1);
// Compute sampling of the spectrum
integer numberOfFrequencies = halfnsamp_window + 1;
double df = samplingFrequency / (numberOfFrequencies - 1);
const integer numberOfFrequencies = halfnsamp_window + 1;
const double df = samplingFrequency / (numberOfFrequencies - 1);
autoComplexSpectrogram thee = ComplexSpectrogram_create (my xmin, my xmax, numberOfFrames, timeStep, t1, 0.0, 0.5 * samplingFrequency, numberOfFrequencies, df, 0.0);
//
autoSound analysisWindow = Sound_create (1, 0.0, nsamp_window * my dx, nsamp_window, my dx, 0.5 * my dx);
for (integer iframe = 1; iframe <= numberOfFrames; iframe ++) {
double t = Sampled_indexToX (thee.get(), iframe);
integer leftSample = Sampled_xToLowIndex (me, t), rightSample = leftSample + 1;
integer startSample = rightSample - halfnsamp_window;
integer endSample = leftSample + halfnsamp_window;
const double t = Sampled_indexToX (thee.get(), iframe);
const integer leftSample = Sampled_xToLowIndex (me, t), rightSample = leftSample + 1;
const integer startSample = rightSample - halfnsamp_window;
const integer endSample = leftSample + halfnsamp_window;
Melder_assert (startSample >= 1);
Melder_assert (endSample <= my nx);
......@@ -95,7 +97,7 @@ autoComplexSpectrogram Sound_to_ComplexSpectrogram (Sound me, double windowLengt
thy z [1] [iframe] = spec -> z [1] [1] * spec -> z [1] [1];
thy phase [1] [iframe] = 0.0;
for (integer ifreq = 2; ifreq <= numberOfFrequencies - 1; ifreq ++) {
double x = spec -> z [1] [ifreq], y = spec -> z [2] [ifreq];
const double x = spec -> z [1] [ifreq], y = spec -> z [2] [ifreq];
thy z [ifreq] [iframe] = x * x + y * y; // power
thy phase [ifreq] [iframe] = atan2 (y, x); // phase [-pi,+pi]
}
......@@ -114,62 +116,59 @@ autoSound ComplexSpectrogram_to_Sound (ComplexSpectrogram me, double stretchFact
/* original number of samples is odd: imaginary part of last spectral value is zero ->
* phase is either zero or +/-pi
*/
double pi = atan2 (0.0, - 0.5);
double samplingFrequency = 2.0 * my ymax;
double lastFrequency = my y1 + (my ny - 1) * my dy, lastPhase = my phase [my ny] [1];
int originalNumberOfSamplesProbablyOdd = (lastPhase != 0.0 && lastPhase != pi && lastPhase != -pi) ||
my ymax - lastFrequency > 0.25 * my dx;
const double pi = atan2 (0.0, - 0.5);
const double samplingFrequency = 2.0 * my ymax;
const double lastFrequency = my y1 + (my ny - 1) * my dy, lastPhase = my phase [my ny] [1];
const bool originalNumberOfSamplesProbablyOdd = ( lastPhase != 0.0 && lastPhase != pi && lastPhase != -pi ||
my ymax - lastFrequency > 0.25 * my dx );
Melder_require (my y1 == 0.0,
U"A Fourier-transformable ComplexSpectrogram should have a first frequency of 0 Hz, not ", my y1, U" Hz.");
integer nsamp_window = 2 * my ny - ( originalNumberOfSamplesProbablyOdd ? 1 : 2 );
integer halfnsamp_window = nsamp_window / 2;
double synthesisWindowDuration = nsamp_window / samplingFrequency;
const integer nsamp_window = 2 * my ny - ( originalNumberOfSamplesProbablyOdd ? 1 : 2 );
const integer halfnsamp_window = nsamp_window / 2;
const double synthesisWindowDuration = nsamp_window / samplingFrequency;
autoSpectrum spectrum = Spectrum_create (my ymax, my ny);
autoSound synthesisWindow = Sound_createSimple (1, synthesisWindowDuration, samplingFrequency);
double newDuration = (my xmax - my xmin) * stretchFactor;
const double newDuration = (my xmax - my xmin) * stretchFactor;
autoSound thee = Sound_createSimple (1, newDuration, samplingFrequency); //TODO
double thyStartTime;
for (integer iframe = 1; iframe <= my nx; iframe ++) {
// "original" sound :
double tmid = Sampled_indexToX (me, iframe);
integer leftSample = Sampled_xToLowIndex (thee.get(), tmid);
integer rightSample = leftSample + 1;
integer startSample = rightSample - halfnsamp_window;
double startTime = Sampled_indexToX (thee.get(), startSample);
if (iframe == 1) {
const double tmid = Sampled_indexToX (me, iframe);
const integer leftSample = Sampled_xToLowIndex (thee.get(), tmid);
const integer rightSample = leftSample + 1;
const integer startSample = rightSample - halfnsamp_window;
const double startTime = Sampled_indexToX (thee.get(), startSample);
if (iframe == 1)
thyStartTime = Sampled_indexToX (thee.get(), startSample);
}
//integer endSample = leftSample + halfnsamp_window;
// New Sound with stretch
integer thyStartSample = Sampled_xToLowIndex (thee.get(),thyStartTime);
double thyEndTime = thyStartTime + my dx * stretchFactor;
integer thyEndSample = Sampled_xToLowIndex (thee.get(), thyEndTime);
integer stretchedStepSizeSamples = thyEndSample - thyStartSample + 1;
const integer thyStartSample = Sampled_xToLowIndex (thee.get(), thyStartTime);
const double thyEndTime = thyStartTime + my dx * stretchFactor;
const integer thyEndSample = Sampled_xToLowIndex (thee.get(), thyEndTime);
const integer stretchedStepSizeSamples = thyEndSample - thyStartSample + 1;
//double extraTime = (thyStartSample - startSample + 1) * thy dx;
double extraTime = (thyStartTime - startTime);
const double extraTime = thyStartTime - startTime;
spectrum -> z [1] [1] = sqrt (my z [1] [iframe]);
for (integer ifreq = 2; ifreq <= my ny; ifreq ++) {
double f = my y1 + (ifreq - 1) * my dy;
double a = sqrt (my z [ifreq] [iframe]);
double phi = my phase [ifreq] [iframe], intPart;
double extraPhase = 2.0 * pi * modf (extraTime * f, & intPart); // fractional part
phi += extraPhase;
const double f = my y1 + (ifreq - 1) * my dy;
const double a = sqrt (my z [ifreq] [iframe]);
double intPart;
const double extraPhase = 2.0 * pi * modf (extraTime * f, & intPart); // fractional part
const double phi = my phase [ifreq] [iframe] + extraPhase;
spectrum -> z [1] [ifreq] = a * cos (phi);
spectrum -> z [2] [ifreq] = a * sin (phi);
}
autoSound synthesis = Spectrum_to_Sound (spectrum.get());
// Where should the sound be placed?
integer thyEndSampleP = Melder_ifloor (fmin (thyStartSample + synthesis -> nx - 1, thyStartSample + stretchedStepSizeSamples - 1)); // guard against extreme stretches
if (iframe == my nx) {
thyEndSampleP = Melder_ifloor (fmin (thy nx, thyStartSample + synthesis -> nx - 1)); // ppgb: waarom naar beneden afgerond?
}
for (integer j = thyStartSample; j <= thyEndSampleP; j++) {
integer thyEndSampleP = ( iframe == my nx ?
std::min (thy nx, thyStartSample + synthesis -> nx - 1) :
std::min (thyStartSample + synthesis -> nx - 1, thyStartSample + stretchedStepSizeSamples - 1)
); // guard against extreme stretches
for (integer j = thyStartSample; j <= thyEndSampleP; j ++)
thy z [1] [j] = synthesis -> z [1] [j - thyStartSample + 1];
}
thyStartTime += my dx * stretchFactor;
}
return thee;
......@@ -202,19 +201,18 @@ static autoSound ComplexSpectrogram_to_Sound2 (ComplexSpectrogram me, double str
for (integer iframe = 1; iframe <= my nx; iframe++) {
spectrum -> z [1] [1] = sqrt (my z [1] [iframe]);
for (integer ifreq = 2; ifreq <= my ny; ifreq ++) {
double f = my y1 + (ifreq - 1) * my dy;
double a = sqrt (my z[ifreq][iframe]);
double phi = my phase[ifreq][iframe];
double extraPhase = 2.0 * pi * (stretchFactor - 1.0) * my dx * f;
phi += extraPhase;
const double f = my y1 + (ifreq - 1) * my dy;
const double a = sqrt (my z [ifreq] [iframe]);
const double extraPhase = 2.0 * pi * (stretchFactor - 1.0) * my dx * f;
const double phi = my phase [ifreq] [iframe] + extraPhase;
spectrum -> z [1] [ifreq] = a * cos (phi);
spectrum -> z [2] [ifreq] = a * sin (phi);
}
autoSound synthesis = Spectrum_to_Sound (spectrum.get());
for (integer j = istart; j <= iend; j++) {
for (integer j = istart; j <= iend; j ++)
thy z [1] [j] = synthesis -> z [1] [j - istart + 1];
}
istart = iend + 1; iend = istart + stepSizeSamples - 1;
istart = iend + 1;
iend = istart + stepSizeSamples - 1;
}
return thee;
} catch (MelderError) {
......@@ -234,18 +232,22 @@ autoSpectrogram ComplexSpectrogram_to_Spectrogram (ComplexSpectrogram me) {
}
void ComplexSpectrogram_Spectrogram_replaceAmplitudes (ComplexSpectrogram me, Spectrogram thee) {
Melder_require (my nx == thy nx && my ny == thy ny, U"The number of cells must be equal.");
Melder_require (my nx == thy nx && my ny == thy ny,
U"The numbers of cells in the ComplexSpectrogram and Spectrogram should be equal.");
my z.all() <<= thy z.all();
}
autoSpectrum ComplexSpectrogram_to_Spectrum (ComplexSpectrogram me, double time) {
try {
integer iframe = Sampled_xToLowIndex (me, time); // ppgb: geen Sampled_xToIndex gebruiken voor integers (afrondingen altijd expliciet maken)
iframe = ( iframe < 1 ? 1 : ( iframe > my nx ? my nx : iframe ) );
integer iframe = Sampled_xToLowIndex (me, time);
if (iframe < 1)
iframe = 1;
if (iframe > my nx)
iframe = my nx;
autoSpectrum thee = Spectrum_create (my ymax, my ny);
for (integer ifreq = 1; ifreq <= my ny; ifreq ++) {
double a = sqrt (my z [ifreq] [iframe]);
double phi = my phase [ifreq] [iframe];
const double a = sqrt (my z [ifreq] [iframe]);
const double phi = my phase [ifreq] [iframe];
thy z [1] [ifreq] = a * cos (phi);
thy z [2] [ifreq] = a * sin (phi);
}
......@@ -254,4 +256,5 @@ autoSpectrum ComplexSpectrogram_to_Spectrum (ComplexSpectrogram me, double time)
Melder_throw (me, U": no Spectrum created.");
}
}
/* End of file ComplexSpectrogram.cpp */
......@@ -115,7 +115,8 @@ integer Discriminant_getNumberOfObservations (Discriminant me, integer group) {
void Discriminant_setAprioriProbability (Discriminant me, integer group, double p) {
Melder_require (group > 0 && group <= my numberOfGroups,
U"The group number (", group, U") should be in the interval [1, ", my numberOfGroups, U"]; the supplied value (", group, U") falls outside it.");
Melder_require (p >= 0.0 && p <= 1.0, U"The probability should be in the interval [0, 1]");
Melder_require (p >= 0.0 && p <= 1.0,
U"The probability should be in the interval [0, 1]");
my aprioriProbabilities [group] = p;
}
......@@ -204,7 +205,7 @@ double Discriminant_getWilksLambda (Discriminant me, integer from) {
unstandardized u [j]: sqrt(N-g) * r [j]
standardized s [j]: u [j] sqrt (w [i] [i] / (N-g))
*/
autoTableOfReal Discriminant_extractCoefficients (Discriminant me, int choice) {
autoTableOfReal Discriminant_extractCoefficients (Discriminant me, integer choice) {
try {
bool raw = choice == 0, standardized = choice == 2;
integer nx = my eigen -> dimension, ny = my eigen -> numberOfEigenvalues;
......@@ -215,24 +216,22 @@ autoTableOfReal Discriminant_extractCoefficients (Discriminant me, int choice) {
// The elements in my groups always have my eigen -> dimension columns
autoSSCP within;
if (standardized) {
if (standardized)
within = Discriminant_extractPooledWithinGroupsSSCP (me);
}
TableOfReal_setColumnLabel (thee.get(), nx + 1, U"constant");
TableOfReal_setSequentialRowLabels (thee.get(), 1, ny, U"function_", 1, 1);
double scale = sqrt (total -> numberOfObservations - my numberOfGroups);
double *centroid = my total -> centroid.at;
//double *centroid = my total -> centroid.at;
for (integer i = 1; i <= ny; i ++) {
longdouble u0 = 0.0;
for (integer j = 1; j <= nx; j ++) {
if (standardized) {
if (standardized)
scale = sqrt (within -> data [j] [j]);
}
double ui = scale * my eigen -> eigenvectors [i] [j];
thy data [i] [j] = ui;
u0 += ui * centroid [j];
u0 += ui * my total -> centroid [j];
}
thy data [i] [nx + 1] = ( raw ? 0.0 : - (double) u0 );
}
......@@ -488,25 +487,30 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable (Discrim
integer numberOfGroups = Discriminant_getNumberOfGroups (me);
integer dimension = Eigen_getDimensionOfComponents (my eigen.get());
Melder_require (dimension == thy numberOfColumns, U"The number of columns should agree with the dimension of the discriminant.");
Melder_require (dimension == thy numberOfColumns,
U"The number of columns should agree with the dimension of the discriminant.");
autoVEC log_p (numberOfGroups, kTensorInitializationType::RAW);
autoVEC log_apriori (numberOfGroups, kTensorInitializationType::RAW);
autoVEC ln_determinant (numberOfGroups, kTensorInitializationType::RAW);
autoVEC buf (dimension, kTensorInitializationType::RAW);
autoVEC log_p = newVECraw (numberOfGroups);
autoVEC log_apriori = newVECraw (numberOfGroups);
autoVEC ln_determinant = newVECraw (numberOfGroups);
autoVEC buf = newVECraw (dimension);
autoNUMvector<SSCP> sscpvec (1, numberOfGroups);
autoSSCP pool = SSCPList_to_SSCP_pool (my groups.get());
autoClassificationTable him = ClassificationTable_create (thy numberOfRows, numberOfGroups);
his rowLabels.all() <<= thy rowLabels.all();
// Scale the sscp to become a covariance matrix
/*
Scale the sscp to become a covariance matrix.
*/
pool -> data.get() *= 1.0 / (pool -> numberOfObservations - numberOfGroups);
double lnd;
autoSSCPList agroups; SSCPList groups; // ppgb FIXME dit kan niet goed izjn
autoSSCPList agroups;
SSCPList groups; // ppgb FIXME dit kan niet goed izjn
if (poolCovarianceMatrices) {
/*
Covariance matrix S can be decomposed as S = L.L'. Calculate L^-1.
L^-1 will be used later in the Mahalanobis distance calculation:
......@@ -520,8 +524,11 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable (Discrim
}
groups = my groups.get();
} else {
// Calculate the inverses of all group covariance matrices.
// In case of a singular matrix, substitute inverse of pooled.
/*
Calculate the inverses of all group covariance matrices.
In case of a singular matrix, substitute inverse of pooled.
*/
agroups = Data_copy (my groups.get());
groups = agroups.get();
......@@ -535,8 +542,11 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable (Discrim
try {
MATlowerCholeskyInverse_inplace (t -> data.get(), & ln_determinant [j]);
} catch (MelderError) {
// Try the alternative: the pooled covariance matrix.
// Clear the error.
/*
Clear the error.
Try the alternative: the pooled covariance matrix.
*/
Melder_clearError ();
if (npool == 0)
......@@ -550,7 +560,9 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable (Discrim
Melder_warning (npool, U" groups use pooled covariance matrix.");
}
// Labels for columns in ClassificationTable
/*
Labels for columns in ClassificationTable
*/
for (integer j = 1; j <= numberOfGroups; j ++) {
conststring32 name = Thing_getName (my groups->at [j]);
......@@ -559,37 +571,37 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable (Discrim
TableOfReal_setColumnLabel (him.get(), j, name);
}
// Normalize the sum of the apriori probabilities to 1.
// Next take ln (p) because otherwise probabilities might be too small to represent.
/*
Normalize the sum of the apriori probabilities to 1.
Next take ln (p) because otherwise probabilities might be too small to represent.
*/
VECnormalize_inplace (my aprioriProbabilities.get(), 1.0, 1.0);
double logg = log (numberOfGroups);
for (integer j = 1; j <= numberOfGroups; j ++) {
for (integer j = 1; j <= numberOfGroups; j ++)
log_apriori [j] = ( useAprioriProbabilities ? log (my aprioriProbabilities [j]) : - logg );
}
// Generalized squared distance function:
// D^2(x) = (x - mu)' S^-1 (x - mu) + ln (determinant(S)) - 2 ln (apriori)
/*
Generalized squared distance function:
D^2(x) = (x - mu)' S^-1 (x - mu) + ln (determinant(S)) - 2 ln (apriori)
*/
for (integer i = 1; i <= thy numberOfRows; i ++) {
double norm = 0.0, pt_max = -1e308;
for (integer j = 1; j <= numberOfGroups; j ++) {
SSCP t = groups->at [j];
double md = NUMmahalanobisDistance (sscpvec [j] -> data.get(), thy data.row (i), t -> centroid.get());
//double md = mahalanobisDistanceSq (sscpvec [j] -> data.at, dimension, thy data [i], t -> centroid, buf.at);
double pt = log_apriori [j] - 0.5 * (ln_determinant [j] + md);
if (pt > pt_max) {
if (pt > pt_max)
pt_max = pt;
}
log_p [j] = pt;
}
for (integer j = 1; j <= numberOfGroups; j ++) {
for (integer j = 1; j <= numberOfGroups; j ++)
norm += log_p [j] = exp (log_p [j] - pt_max);
}
for (integer j = 1; j <= numberOfGroups; j ++) {
for (integer j = 1; j <= numberOfGroups; j ++)
his data [i] [j] = log_p [j] / norm;
}
}
return him;
} catch (MelderError) {
Melder_throw (U"ClassificationTable from Discriminant & TableOfReal not created.");
......@@ -602,13 +614,14 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable_dw (Disc
integer p = Eigen_getDimensionOfComponents (my eigen.get());
integer m = thy numberOfRows;
Melder_require (p == thy numberOfColumns, U"The number of columns does not agree with the dimension of the discriminant.");
Melder_require (p == thy numberOfColumns,
U"The number of columns does not agree with the dimension of the discriminant.");
autoNUMvector<double> log_p (1, g);
autoNUMvector<double> log_apriori (1, g);
autoNUMvector<double> ln_determinant (1, g);
autoNUMvector<double> buf (1, p);
autoNUMvector<double> displacement (1, p);
autoVEC log_p = newVECraw (g);
autoVEC log_apriori = newVECraw (g);
autoVEC ln_determinant = newVECraw (g);
autoVEC buf = newVECraw (p);
autoVEC displacement = newVECraw (p);
autoVEC x = newVECzero (p);
autoNUMvector<SSCP> sscpvec (1, g);
autoSSCP pool = SSCPList_to_SSCP_pool (my groups.get());
......@@ -616,16 +629,23 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable_dw (Disc
his rowLabels.all() <<= thy rowLabels.all();
autoTableOfReal adisplacements = Data_copy (thee);
// Scale the sscp to become a covariance matrix.
/*
Scale the sscp to become a covariance matrix.
*/
pool -> data.get() *= 1.0 / (pool -> numberOfObservations - g);
double lnd;
autoSSCPList agroups;
SSCPList groups;
if (poolCovarianceMatrices) {
// Covariance matrix S can be decomposed as S = L.L'. Calculate L^-1.
// L^-1 will be used later in the Mahalanobis distance calculation:
// v'.S^-1.v == v'.L^-1'.L^-1.v == (L^-1.v)'.(L^-1.v).
/*
Covariance matrix S can be Cholesky decomposed as S = L.L'.
Calculate L^-1.
L^-1 will be used later in the Mahalanobis distance calculation:
v'.S^-1.v = v'.L^-1'.L^-1.v = (L^-1.v)'.(L^-1.v).
*/
MATlowerCholeskyInverse_inplace (pool -> data.get(), & lnd);
for (integer j = 1; j <= g; j ++) {
......@@ -634,8 +654,11 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable_dw (Disc
}
groups = my groups.get();
} else {
//Calculate the inverses of all group covariance matrices.
// In case of a singular matrix, substitute inverse of pooled.
/*
Calculate the inverses of all group covariance matrices.
In case of a singular matrix, substitute inverse of pooled.
*/
agroups = Data_copy (my groups.get());
groups = agroups.get();
......@@ -649,24 +672,27 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable_dw (Disc
try {
MATlowerCholeskyInverse_inplace (t -> data.get(), & ln_determinant [j]);
} catch (MelderError) {
// Try the alternative: the pooled covariance matrix.
// Clear the error.
/*
Clear the error.
Try the alternative: the pooled covariance matrix.
*/
Melder_clearError ();
if (npool == 0) {
if (npool == 0)
MATlowerCholeskyInverse_inplace (pool -> data.get(), & lnd);
}
npool ++;
sscpvec [j] = pool.get();
ln_determinant [j] = lnd;
}
}
if (npool > 0) {
if (npool > 0)
Melder_warning (npool, U" groups use pooled covariance matrix.");
}
}
// Labels for columns in ClassificationTable
/*
Labels for columns in ClassificationTable
*/
for (integer j = 1; j <= g; j ++) {
conststring32 name = Thing_getName (my groups->at [j]);
......@@ -675,8 +701,10 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable_dw (Disc
TableOfReal_setColumnLabel (him.get(), j, name);
}
// Normalize the sum of the apriori probabilities to 1.
// Next take ln (p) because otherwise probabilities might be too small to represent.
/*
Normalize the sum of the apriori probabilities to 1.
Next take ln (p) because otherwise probabilities might be too small to represent.
*/
double logg = log (g);
VECnormalize_inplace (my aprioriProbabilities.get(), 1.0, 1.0);
......@@ -684,20 +712,20 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable_dw (Disc
log_apriori [j] = ( useAprioriProbabilities ? log (my aprioriProbabilities [j]) : - logg );
}
// Generalized squared distance function:
// D^2(x) = (x - mu)' S^-1 (x - mu) + ln (determinant(S)) - 2 ln (apriori)
/*
Generalized squared distance function:
D^2(x) = (x - mu)' S^-1 (x - mu) + ln (determinant(S)) - 2 ln (apriori)
*/
for (integer i = 1; i <= m; i ++) {
SSCP winner;
double norm = 0, pt_max = -1e308;
integer iwinner = 1;
for (integer k = 1; k <= p; k ++) {
for (integer k = 1; k <= p; k ++)
x [k] = thy data [i] [k] + displacement [k];
}
for (integer j = 1; j <= g; j ++) {
SSCP t = groups->at [j];
double md = NUMmahalanobisDistance (sscpvec [j] -> data.get(), x.get(), t -> centroid.get());
// double md = mahalanobisDistanceSq (sscpvec [j] -> data.at, p, x.peek(), t -> centroid, buf.peek());
double pt = log_apriori [j] - 0.5 * (ln_determinant [j] + md);
if (pt > pt_max) {
pt_max = pt;
......@@ -705,15 +733,15 @@ autoClassificationTable Discriminant_TableOfReal_to_ClassificationTable_dw (Disc
}
log_p [j] = pt;
}
for (integer j = 1; j <= g; j ++) {
for (integer j = 1; j <= g; j ++)
norm += log_p [j] = exp (log_p [j] - pt_max);
}
for (integer j = 1; j <= g; j ++) {
for (integer j = 1; j <= g; j ++)
his data [i] [j] = log_p [j] / norm;
}
// Save old displacement, calculate new displacement
/*
Save old displacement, calculate new displacement
*/
winner = groups->at [iwinner];
for (integer k = 1; k <= p; k ++) {
......
......@@ -2,7 +2,7 @@
#define _Discriminant_h_
/* Discriminant.h
*
* Copyright (C) 1993-2017 David Weenink
* Copyright (C) 1993-2019 David Weenink
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -59,7 +59,7 @@ void Discriminant_drawConcentrationEllipses (Discriminant me, Graphics g,
integer d1, integer d2, double xmin, double xmax, double ymin, double ymax,
double fontSize, bool garnish);
autoTableOfReal Discriminant_extractCoefficients (Discriminant me, int choice);
autoTableOfReal Discriminant_extractCoefficients (Discriminant me, integer choice);
autoTableOfReal Discriminant_extractGroupCentroids (Discriminant me);
......
......@@ -26,6 +26,8 @@
Thing_implement (Distance, Proximity, 0);
Thing_implement (DistanceList, ProximityList, 0);
autoDistance Distance_create (integer numberOfPoints) {
try {
autoDistance me = Thing_new (Distance);
......@@ -75,7 +77,6 @@ autoDistance Configuration_to_Distance (Configuration me) {
}
}
void Distance_drawDendogram (Distance me, Graphics g, int method) {
(void) me;
(void) g;
......