Skip to content

Commit

Permalink
Changed epsilon to relTol, absTol
Browse files Browse the repository at this point in the history
  • Loading branch information
dvd101x committed Apr 7, 2024
1 parent a073665 commit 05aab2b
Show file tree
Hide file tree
Showing 28 changed files with 68 additions and 63 deletions.
6 changes: 5 additions & 1 deletion src/core/config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
export const DEFAULT_CONFIG = {
// minimum relative difference between two compared values,
// used by all comparison functions
epsilon: 1e-12,
relTol: 1e-12,

// minimum absolute difference between two compared values,
// used by all comparison functions
absTol: 1e-15,

// type of default matrix output. Choose 'matrix' (default) or 'array'
matrix: 'Matrix',
Expand Down
8 changes: 4 additions & 4 deletions src/function/arithmetic/ceil.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ export const createCeilNumber = /* #__PURE__ */ factory(
name, ['typed', 'config', 'round'], ({ typed, config, round }) => {
return typed(name, {
number: function (x) {
if (nearlyEqual(x, round(x), config.epsilon, config.epsilon * 1e-3)) {
if (nearlyEqual(x, round(x), config.relTol, config.absTol)) {
return round(x)
} else {
return Math.ceil(x)
}
},

'number, number': function (x, n) {
if (nearlyEqual(x, round(x, n), config.epsilon, config.epsilon * 1e-3)) {
if (nearlyEqual(x, round(x, n), config.relTol, config.absTol)) {
return round(x, n)
} else {
let [number, exponent] = `${x}e`.split('e')
Expand Down Expand Up @@ -95,15 +95,15 @@ export const createCeil = /* #__PURE__ */ factory(name, dependencies, ({ typed,
},

BigNumber: function (x) {
if (bigNearlyEqual(x, round(x), config.epsilon, config.epsilon * 1e-3)) {
if (bigNearlyEqual(x, round(x), config.relTol, config.absTol)) {
return round(x)
} else {
return x.ceil()
}
},

'BigNumber, BigNumber': function (x, n) {
if (bigNearlyEqual(x, round(x, n), config.epsilon, config.epsilon * 1e-3)) {
if (bigNearlyEqual(x, round(x, n), config.relTol, config.absTol)) {
return round(x, n)
} else {
return x.toDecimalPlaces(n.toNumber(), Decimal.ROUND_CEIL)
Expand Down
8 changes: 4 additions & 4 deletions src/function/arithmetic/floor.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ export const createFloorNumber = /* #__PURE__ */ factory(
name, ['typed', 'config', 'round'], ({ typed, config, round }) => {
return typed(name, {
number: function (x) {
if (nearlyEqual(x, round(x), config.epsilon, config.epsilon * 1e-3)) {
if (nearlyEqual(x, round(x), config.relTol, config.absTol)) {
return round(x)
} else {
return Math.floor(x)
}
},

'number, number': function (x, n) {
if (nearlyEqual(x, round(x, n), config.epsilon, config.epsilon * 1e-3)) {
if (nearlyEqual(x, round(x, n), config.relTol, config.absTol)) {
return round(x, n)
} else {
let [number, exponent] = `${x}e`.split('e')
Expand Down Expand Up @@ -98,15 +98,15 @@ export const createFloor = /* #__PURE__ */ factory(name, dependencies, ({ typed,
},

BigNumber: function (x) {
if (bigNearlyEqual(x, round(x), config.epsilon, config.epsilon * 1e-3)) {
if (bigNearlyEqual(x, round(x), config.relTol, config.absTol)) {
return round(x)
} else {
return x.floor()
}
},

'BigNumber, BigNumber': function (x, n) {
if (bigNearlyEqual(x, round(x, n), config.epsilon, config.epsilon * 1e-3)) {
if (bigNearlyEqual(x, round(x, n), config.relTol, config.absTol)) {
return round(x, n)
} else {
return x.toDecimalPlaces(n.toNumber(), Decimal.ROUND_FLOOR)
Expand Down
16 changes: 8 additions & 8 deletions src/function/arithmetic/round.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,18 @@ export const createRound = /* #__PURE__ */ factory(name, dependencies, ({ typed,
return typed(name, {
number: function (x) {
// Handle round off errors by first rounding to epsilon precision
const xEpsilon = roundNumber(x, toExponent(config.epsilon))
const xSelected = nearlyEqual(x, xEpsilon, config.epsilon, config.epsilon * 1e-3) ? xEpsilon : x
const xEpsilon = roundNumber(x, toExponent(config.relTol))
const xSelected = nearlyEqual(x, xEpsilon, config.relTol, config.absTol) ? xEpsilon : x
return roundNumber(xSelected)
},

'number, number': function (x, n) {
// Same as number: unless user specifies more decimals than epsilon
const epsilonExponent = toExponent(config.epsilon)
const epsilonExponent = toExponent(config.relTol)
if (n >= epsilonExponent) { return roundNumber(x, n) }

const xEpsilon = roundNumber(x, epsilonExponent)
const xSelected = nearlyEqual(x, xEpsilon, config.epsilon, config.epsilon * 1e-3) ? xEpsilon : x
const xSelected = nearlyEqual(x, xEpsilon, config.relTol, config.absTol) ? xEpsilon : x
return roundNumber(xSelected, n)
},

Expand Down Expand Up @@ -116,20 +116,20 @@ export const createRound = /* #__PURE__ */ factory(name, dependencies, ({ typed,

BigNumber: function (x) {
// Handle round off errors by first rounding to epsilon precision
const xEpsilon = new BigNumber(x).toDecimalPlaces(toExponent(config.epsilon))
const xSelected = bigNearlyEqual(x, xEpsilon, config.epsilon, config.epsilon * 1e-3) ? xEpsilon : x
const xEpsilon = new BigNumber(x).toDecimalPlaces(toExponent(config.relTol))
const xSelected = bigNearlyEqual(x, xEpsilon, config.relTol, config.absTol) ? xEpsilon : x
return xSelected.toDecimalPlaces(0)
},

'BigNumber, BigNumber': function (x, n) {
if (!n.isInteger()) { throw new TypeError(NO_INT) }

// Same as BigNumber: unless user specifies more decimals than epsilon
const epsilonExponent = toExponent(config.epsilon)
const epsilonExponent = toExponent(config.relTol)
if (n >= epsilonExponent) { return x.toDecimalPlaces(n.toNumber()) }

const xEpsilon = x.toDecimalPlaces(epsilonExponent)
const xSelected = bigNearlyEqual(x, xEpsilon, config.epsilon, config.epsilon * 1e-3) ? xEpsilon : x
const xSelected = bigNearlyEqual(x, xEpsilon, config.relTol, config.absTol) ? xEpsilon : x
return xSelected.toDecimalPlaces(n.toNumber())
},

Expand Down
2 changes: 1 addition & 1 deletion src/function/geometry/intersect.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export const createIntersect = /* #__PURE__ */ factory(name, dependencies, ({ ty
const d2 = subtract(o2, p2b)
const det = subtract(multiplyScalar(d1[0], d2[1]), multiplyScalar(d2[0], d1[1]))
if (isZero(det)) return null
if (smaller(abs(det), config.epsilon)) {
if (smaller(abs(det), config.relTol)) {
return null
}
const d20o11 = multiplyScalar(d2[0], o1[1])
Expand Down
4 changes: 2 additions & 2 deletions src/function/matrix/eigs.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const createEigs = /* #__PURE__ */ factory(name, dependencies, ({ config,
*
* @param {Array | Matrix} x Matrix to be diagonalized
*
* @param {number | BigNumber | OptsObject} [opts] Object with keys `precision`, defaulting to config.epsilon, and `eigenvectors`, defaulting to true and specifying whether to compute eigenvectors. If just a number, specifies precision.
* @param {number | BigNumber | OptsObject} [opts] Object with keys `precision`, defaulting to config.relTol, and `eigenvectors`, defaulting to true and specifying whether to compute eigenvectors. If just a number, specifies precision.
* @return {{values: Array|Matrix, eigenvectors?: Array<EVobj>}} Object containing an array of eigenvalues and an array of {value: number|BigNumber, vector: Array|Matrix} objects. The eigenvectors property is undefined if eigenvectors were not requested.
*
*/
Expand Down Expand Up @@ -100,7 +100,7 @@ export const createEigs = /* #__PURE__ */ factory(name, dependencies, ({ config,

function doEigs (mat, opts = {}) {
const computeVectors = 'eigenvectors' in opts ? opts.eigenvectors : true
const prec = opts.precision ?? config.epsilon
const prec = opts.precision ?? config.relTol
const result = computeValuesAndVectors(mat, prec, computeVectors)
if (opts.matricize) {
result.values = matrix(result.values)
Expand Down
6 changes: 3 additions & 3 deletions src/function/matrix/eigs/realSymmetric.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export function createRealSymmetric ({ config, addScalar, subtract, abs, atan, c
* @param {number} prec
* @param {'number' | 'BigNumber'} type
*/
function main (arr, N, prec = config.epsilon, type, computeVectors) {
function main (arr, N, prec = config.relTol, type, computeVectors) {
if (type === 'number') {
return diag(arr, prec, computeVectors)
}
Expand Down Expand Up @@ -85,7 +85,7 @@ export function createRealSymmetric ({ config, addScalar, subtract, abs, atan, c
// get angle
function getTheta (aii, ajj, aij) {
const denom = (ajj - aii)
if (Math.abs(denom) <= config.epsilon) {
if (Math.abs(denom) <= config.relTol) {
return Math.PI / 4.0
} else {
return 0.5 * Math.atan(2.0 * aij / (ajj - aii))
Expand All @@ -95,7 +95,7 @@ export function createRealSymmetric ({ config, addScalar, subtract, abs, atan, c
// get angle
function getThetaBig (aii, ajj, aij) {
const denom = subtract(ajj, aii)
if (abs(denom) <= config.epsilon) {
if (abs(denom) <= config.relTol) {
return bignumber(-1).acos().div(4)
} else {
return multiplyScalar(0.5, atan(multiply(2.0, aij, inv(denom))))
Expand Down
4 changes: 2 additions & 2 deletions src/function/relational/compare.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const createCompare = /* #__PURE__ */ factory(name, dependencies, ({ type
},

'BigNumber, BigNumber': function (x, y) {
return bigNearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return bigNearlyEqual(x, y, config.relTol, config.absTol)
? new BigNumber(0)
: new BigNumber(x.cmp(y))
},
Expand All @@ -97,7 +97,7 @@ export const createCompare = /* #__PURE__ */ factory(name, dependencies, ({ type
export const createCompareNumber = /* #__PURE__ */ factory(name, ['typed', 'config'], ({ typed, config }) => {
return typed(name, {
'number, number': function (x, y) {
return nearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return nearlyEqual(x, y, config.relTol, config.absTol)
? 0
: (x > y ? 1 : -1)
}
Expand Down
8 changes: 4 additions & 4 deletions src/function/relational/equalScalar.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,27 @@ export const createEqualScalar = /* #__PURE__ */ factory(name, dependencies, ({
},

'number, number': function (x, y) {
return nearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return nearlyEqual(x, y, config.relTol, config.absTol)
},

'BigNumber, BigNumber': function (x, y) {
return x.eq(y) || bigNearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x.eq(y) || bigNearlyEqual(x, y, config.relTol, config.absTol)
},

'Fraction, Fraction': function (x, y) {
return x.equals(y)
},

'Complex, Complex': function (x, y) {
return complexEquals(x, y, config.epsilon)
return complexEquals(x, y, config.relTol, config.absTol)
}
}, compareUnits)
})

export const createEqualScalarNumber = factory(name, ['typed', 'config'], ({ typed, config }) => {
return typed(name, {
'number, number': function (x, y) {
return nearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return nearlyEqual(x, y, config.relTol, config.absTol)
}
})
})
4 changes: 2 additions & 2 deletions src/function/relational/larger.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const createLarger = /* #__PURE__ */ factory(name, dependencies, ({ typed
'boolean, boolean': (x, y) => x > y,

'BigNumber, BigNumber': function (x, y) {
return x.gt(y) && !bigNearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x.gt(y) && !bigNearlyEqual(x, y, config.relTol, config.absTol)
},

'Fraction, Fraction': (x, y) => (x.compare(y) === 1),
Expand All @@ -82,7 +82,7 @@ export const createLarger = /* #__PURE__ */ factory(name, dependencies, ({ typed
export const createLargerNumber = /* #__PURE__ */ factory(name, ['typed', 'config'], ({ typed, config }) => {
return typed(name, {
'number, number': function (x, y) {
return x > y && !nearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x > y && !nearlyEqual(x, y, config.relTol, config.absTol)
}
})
})
4 changes: 2 additions & 2 deletions src/function/relational/largerEq.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const createLargerEq = /* #__PURE__ */ factory(name, dependencies, ({ typ
'boolean, boolean': (x, y) => x >= y,

'BigNumber, BigNumber': function (x, y) {
return x.gte(y) || bigNearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x.gte(y) || bigNearlyEqual(x, y, config.relTol, config.absTol)
},

'Fraction, Fraction': (x, y) => (x.compare(y) !== -1),
Expand All @@ -78,7 +78,7 @@ export const createLargerEq = /* #__PURE__ */ factory(name, dependencies, ({ typ
export const createLargerEqNumber = /* #__PURE__ */ factory(name, ['typed', 'config'], ({ typed, config }) => {
return typed(name, {
'number, number': function (x, y) {
return x >= y || nearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x >= y || nearlyEqual(x, y, config.relTol, config.absTol)
}
})
})
4 changes: 2 additions & 2 deletions src/function/relational/smaller.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export const createSmaller = /* #__PURE__ */ factory(name, dependencies, ({ type
'boolean, boolean': (x, y) => x < y,

'BigNumber, BigNumber': function (x, y) {
return x.lt(y) && !bigNearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x.lt(y) && !bigNearlyEqual(x, y, config.relTol, config.absTol)
},

'Fraction, Fraction': (x, y) => (x.compare(y) === -1),
Expand All @@ -82,7 +82,7 @@ export const createSmaller = /* #__PURE__ */ factory(name, dependencies, ({ type
export const createSmallerNumber = /* #__PURE__ */ factory(name, ['typed', 'config'], ({ typed, config }) => {
return typed(name, {
'number, number': function (x, y) {
return x < y && !nearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x < y && !nearlyEqual(x, y, config.relTol, config.absTol)
}
})
})
4 changes: 2 additions & 2 deletions src/function/relational/smallerEq.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const createSmallerEq = /* #__PURE__ */ factory(name, dependencies, ({ ty
'boolean, boolean': (x, y) => (x <= y),

'BigNumber, BigNumber': function (x, y) {
return x.lte(y) || bigNearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x.lte(y) || bigNearlyEqual(x, y, config.relTol, config.absTol)
},

'Fraction, Fraction': (x, y) => (x.compare(y) !== 1),
Expand All @@ -78,7 +78,7 @@ export const createSmallerEq = /* #__PURE__ */ factory(name, dependencies, ({ ty
export const createSmallerEqNumber = /* #__PURE__ */ factory(name, ['typed', 'config'], ({ typed, config }) => {
return typed(name, {
'number, number': function (x, y) {
return x <= y || nearlyEqual(x, y, config.epsilon, config.epsilon * 1e-3)
return x <= y || nearlyEqual(x, y, config.relTol, config.absTol)
}
})
})
2 changes: 1 addition & 1 deletion src/function/special/zeta.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const createZeta = /* #__PURE__ */ factory(name, dependencies, ({ typed,
value => new BigNumber(value),
() => {
// epsilon is for example 1e-12. Extract the positive exponent 12 from that
return Math.abs(Math.log10(config.epsilon))
return Math.abs(Math.log10(config.relTol))
}
),
Complex: zetaComplex
Expand Down
4 changes: 2 additions & 2 deletions src/utils/bignumber/nearlyEqual.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
* @throws {Error} If `absTol` is less than 0.
*
* @example
* nearlyEqual(1.000000001, 1.0, 1e-8); // true
* nearlyEqual(1.000000002, 1.0, 1e-9); // false
* nearlyEqual(1.000000001, 1.0, 1e-9); // true
* nearlyEqual(1.000000002, 1.0, 0); // false
* nearlyEqual(1.0, 1.009, undefined, 0.02); // true
* nearlyEqual(0.000000001, 0.0, undefined, 1e-8); // true
*/
Expand Down
7 changes: 4 additions & 3 deletions src/utils/complex.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ import { nearlyEqual } from './number.js'
* Does not use or change the global Complex.EPSILON setting
* @param {Complex} x
* @param {Complex} y
* @param {number} epsilon
* @param {number} relTol
* @param {number} absTol
* @returns {boolean}
*/
export function complexEquals (x, y, epsilon) {
return nearlyEqual(x.re, y.re, epsilon, epsilon * 1e-3) && nearlyEqual(x.im, y.im, epsilon, epsilon * 1e-3)
export function complexEquals (x, y, relTol, absTol) {
return nearlyEqual(x.re, y.re, relTol, absTol) && nearlyEqual(x.im, y.im, relTol, absTol)
}
4 changes: 2 additions & 2 deletions src/utils/number.js
Original file line number Diff line number Diff line change
Expand Up @@ -627,11 +627,11 @@ export const DBL_EPSILON = Number.EPSILON || 2.2204460492503130808472633361816E-
*
* @example
* nearlyEqual(1.000000001, 1.0, 1e-8); // true
* nearlyEqual(1.000000002, 1.0, 1e-9); // false
* nearlyEqual(1.000000002, 1.0, 0); // false
* nearlyEqual(1.0, 1.009, undefined, 0.01); // true
* nearlyEqual(0.000000001, 0.0, undefined, 1e-8); // true
*/
export function nearlyEqual (a, b, relTol = 1e-12, absTol = 1e-9) {
export function nearlyEqual (a, b, relTol = 1e-8, absTol = 0) {
if (relTol <= 0) {
throw new Error('Relative tolerance must be greater than 0')
}
Expand Down
6 changes: 3 additions & 3 deletions test/unit-tests/function/arithmetic/round.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -173,13 +173,13 @@ describe('round', function () {
assert.deepStrictEqual(round(math.matrix([1.7, 2.3])).valueOf(), [2, 2])
})

describe('changing config.epsilon during runtime', function () {
it('uses default config.epsilon of 1e-12', function () {
describe('changing config.relTol during runtime', function () {
it('uses default config.relTol of 1e-12', function () {
assert.strictEqual(math2.round((0.000000000001459), 12), 1e-12)
assert.deepStrictEqual(math2.round(bignumber(1.49e-12), bignumber(12)), bignumber(1e-12))
})

it('uses updated config.epsilon value', function () {
it('uses updated config.relTol value', function () {
math2.config({ epsilon: 1e-13 })
assert.strictEqual(math2.round((0.000000000001459), 12), 1e-12)
assert.deepStrictEqual(math2.round(bignumber(1.49e-12), bignumber(12)), bignumber(1e-12))
Expand Down
4 changes: 2 additions & 2 deletions test/unit-tests/function/matrix/eigs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ describe('eigs', function () {
approx.equal(ev[1].value, 2)
approx.equal(ev[0].vector[0], 0)
approx.equal(ev[0].vector[1], 0)
assert.ok(abs(ev[0].vector[2]) > math.config.epsilon)
assert.ok(abs(ev[0].vector[2]) > math.config.relTol)
approx.equal(ev[1].vector[0], -ev[1].vector[2])
approx.equal(ev[1].vector[1], 0)
const web2 = eigs([[1, 1, 0], [0, 1, 2], [0, 0, 3]]) // https://www2.math.upenn.edu/~moose/240S2013/slides7-31.pdf
Expand All @@ -224,7 +224,7 @@ describe('eigs', function () {
assert.strictEqual(ev2[1].value, 3)
assert.strictEqual(ev2[0].vector[1], 0)
assert.strictEqual(ev2[0].vector[2], 0)
assert.ok(abs(ev2[0].vector[0]) > math.config.epsilon)
assert.ok(abs(ev2[0].vector[0]) > math.config.relTol)
assert.strictEqual(ev2[1].vector[1], ev2[1].vector[2])
approx.equal(ev2[1].vector[1], 2 * ev2[1].vector[0])
})
Expand Down

0 comments on commit 05aab2b

Please sign in to comment.