final int dimy = DatabaseUtil.dimensionality(relationy);
assert (dim == 2);
KNNQuery<V, D> knnQuery = QueryUtil.getKNNQuery(relationx, getDistanceFunction(), k + 1);
// We need stable indexed DBIDs
ArrayModifiableDBIDs ids = DBIDUtil.newArray(relationx.getDBIDs());
// Sort, so we can do a binary search below.
ids.sort();
// init F,X,Z
Matrix X = new Matrix(ids.size(), 6);
Matrix F = new Matrix(ids.size(), ids.size());
Matrix Y = new Matrix(ids.size(), dimy);
for(int i = 0; i < ids.size(); i++) {
DBID id = ids.get(i);
// Fill the data matrix
{
V vec = relationx.get(id);
double la = vec.doubleValue(1);
double lo = vec.doubleValue(2);
X.set(i, 0, 1.0);
X.set(i, 1, la);
X.set(i, 2, lo);
X.set(i, 3, la * lo);
X.set(i, 4, la * la);
X.set(i, 5, lo * lo);
}
{
for(int d = 0; d < dimy; d++) {
double idy = relationy.get(id).doubleValue(d + 1);
Y.set(i, d, idy);
}
}
// Fill the neighborhood matrix F:
{
KNNResult<D> neighbors = knnQuery.getKNNForDBID(id, k + 1);
ModifiableDBIDs neighborhood = DBIDUtil.newArray(neighbors.size());
for(DistanceResultPair<D> dpair : neighbors) {
if(id.equals(dpair.getDBID())) {
continue;
}
neighborhood.add(dpair.getDBID());
}
// Weight object itself positively.
F.set(i, i, 1.0);
final int nweight = -1 / neighborhood.size();
// We need to find the index positions of the neighbors, unfortunately.
for(DBID nid : neighborhood) {
int pos = ids.binarySearch(nid);
assert (pos >= 0);
F.set(pos, i, nweight);
}
}
}
// Estimate the parameter beta
// Common term that we can save recomputing.
Matrix common = X.transposeTimesTranspose(F).times(F);
Matrix b = common.times(X).inverse().times(common.times(Y));
// Estimate sigma_0 and sigma:
// sigma_sum_square = sigma_0*sigma_0 + sigma*sigma
Matrix sigmaMat = F.times(X.times(b).minus(F.times(Y)));
final double sigma_sum_square = sigmaMat.normF() / (relationx.size() - 6 - 1);
final double norm = 1 / Math.sqrt(sigma_sum_square);
// calculate the absolute values of standard residuals
Matrix E = F.times(Y.minus(X.times(b))).timesEquals(norm);
DBID worstid = null;
double worstscore = Double.NEGATIVE_INFINITY;
for(int i = 0; i < ids.size(); i++) {
DBID id = ids.get(i);
double err = E.getRow(i).euclideanLength();
// double err = Math.abs(E.get(i, 0));
if(err > worstscore) {
worstscore = err;
worstid = id;