Skip to content

Commit

Permalink
Fix the two piecewise-linear regression calculation
Browse files Browse the repository at this point in the history
WebKit#66

The current implementation of the regression calculation has these flaws:

When processing (x[0], y[0]), L1 must be any line through (x[0], y[0]) which meets
L2 at a point (x’, y’) where x[0] < x' < x[1]. L1 has no error.

When processing (x[n - 2], y[n - 2]), L2 must be any line through (x[n - 1], y[n - 1])
which meets L1 at a point (x’, y’) where x[n - 2] < x' < x[n - 1]. L2 has no error.

The lambda calculation is incorrect. It includes a term called H which is equal
to C - I. Looking at the algorithm of Kundu/Ubhaya, this should be just C.

lambda should to be used with calculating L1 and (1 - lambda) should to be used
with calculating L2. Currently (1 - lambda) is used in calculating L1 and L2.

The current calculation has this condition if (t1 != t2) continue; This condition
is almost always true even if t1 and t2 are essentiallyEqual.
  • Loading branch information
shallawa committed Aug 30, 2024
1 parent 1a99525 commit 69140b5
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 10 deletions.
6 changes: 3 additions & 3 deletions MotionMark/resources/runner/motionmark.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@
var calculation = regressionResult.regression;
result[Strings.json.complexity] = {};
result[Strings.json.complexity][Strings.json.regressions.segment1] = [
[regressionResult.minComplexity, calculation.s1 + calculation.t1 * regressionResult.minComplexity],
[calculation.complexity, calculation.s1 + calculation.t1 * calculation.complexity]
[regressionResult.minComplexity, desiredFrameLength],
[calculation.complexity, desiredFrameLength]
];
result[Strings.json.complexity][Strings.json.regressions.segment2] = [
[calculation.complexity, calculation.s2 + calculation.t2 * calculation.complexity],
[calculation.complexity, desiredFrameLength],
[regressionResult.maxComplexity, calculation.s2 + calculation.t2 * regressionResult.maxComplexity]
];
result[Strings.json.complexity][Strings.json.complexity] = calculation.complexity;
Expand Down
38 changes: 31 additions & 7 deletions MotionMark/resources/statistics.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,8 @@ Regression = Utilities.createClass(
e: Number.MAX_VALUE
};

this.complexity = 0;

if (options.preferredProfile == Strings.json.profiles.flat) {
this._calculateRegression(samples, {
s1: options.desiredFrameLength,
Expand All @@ -213,7 +215,6 @@ Regression = Utilities.createClass(
this.startIndex = Math.min(startIndex, endIndex);
this.endIndex = Math.max(startIndex, endIndex);

this.complexity = this._complexity();
this.s1 = this.segment1.s;
this.t1 = this.segment1.t;
this.s2 = this.segment2.s;
Expand Down Expand Up @@ -242,10 +243,30 @@ Regression = Utilities.createClass(
return this.segment1.e + this.segment2.e;
},

_setOptimal: function(segment1, segment2, options) {
_areEssentiallyEqual: function(n1, n2) {
const epsilon = 0.001;
return Math.abs(n1 - n2) < epsilon;
},

_setOptimal: function(segment1, segment2, xp, x, options) {
if (segment1.e + segment2.e > this.segment1.e + this.segment2.e)
return false;

let complexity = this._complexity();
if (!this._areEssentiallyEqual(this.segment1.t, this.segment2.t)) {
// If segment1 and segment2 are not parallel, then they have to meet
// at complexity such that xp < complexity < x.
if (!(complexity >= xp && complexity <= x))
return false;
} else {
// If segment1 and segment2 are parallel, then they have to form one
// single line.
if (!this._areEssentiallyEqual(this.segment1.s, this.segment2.s))
return false;
}

this.complexity = complexity;

this.segment1.s = options.s1 !== undefined ? options.s1 : segment1.s;
this.segment1.t = options.t1 !== undefined ? options.t1 : segment1.t;
this.segment1.e = segment1.e;
Expand Down Expand Up @@ -330,9 +351,10 @@ Regression = Utilities.createClass(

let segment1;
let segment2;
let xp = (j == 0) ? 0 : sortedSamples[j - 1][complexityIndex];

if (j == 0) {
// Let segment1 be any line through (x0, y0) which meets segment2 at
// Let segment1 be any line through (x[0], y[0]) which meets segment2 at
// a point (x’, y’) where x[0] < x' < x[1]. segment1 has no error.
let xMid = (x + sortedSamples[j + 1][complexityIndex]) / 2;
let yMid = s2 + t2 * xMid;
Expand Down Expand Up @@ -373,16 +395,18 @@ Regression = Utilities.createClass(
};
}

if (this._setOptimal(segment1, segment2, options))
if (this._setOptimal(segment1, segment2, xp, x, options))
continue

// These values remove the influence of this sample
let G = A + B * x - C * y;
let H = D + E * x - F * y;
let J = D + E * x - F * y;

let I = c1 - 2 * b1 * x + a1 * xx;
let K = c2 - 2 * b2 * x + a2 * xx;

let lambda = (G * F + G * K - H * C) / (I * H + G * K);
// Calculate lambda, which divides the weight of this sample between the two lines
let lambda = (G * F + G * K - J * C) / (I * J + G * K);
if (!(lambda > 0 && lambda < 1))
continue;

Expand All @@ -402,7 +426,7 @@ Regression = Utilities.createClass(
e: (k2 + a2 * s2 * s2 + c2 * t2 * t2 - 2 * d2 * s2 - 2 * h2 * t2 + 2 * b2 * s2 * t2) + lambda1 * Math.pow(y - (s2 + t2 * x), 2)
};

this._setOptimal(segment1, segment2, options);
this._setOptimal(segment1, segment2, xp, x, options);
}
}
});
Expand Down

0 comments on commit 69140b5

Please sign in to comment.