differential-privacy
Version:
Implements Global Differential Privacy for almost any numeric function
102 lines • 4.9 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.privatizeFactory = void 0;
var tslib_1 = require("tslib");
var util_1 = require("util");
var sensitivity_1 = require("./sensitivity");
var types_1 = require("./types");
var decimal_js_1 = tslib_1.__importDefault(require("decimal.js"));
function privatizeFactory(probDistFunc) {
return function (sensitiveFunction, userOptions) {
var _this = this;
var validatedOptions = validateOptions(userOptions);
var options = fillDefaultOptions(validatedOptions);
var maxEpsilon = new decimal_js_1.default(options.maxEpsilon);
var epsilon = maxEpsilon.dividedBy(options.maxCallCount);
var epsilonAsNumber = epsilon.toNumber();
var epsilonSoFar = new decimal_js_1.default(0);
return function (dataset) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var sensitivity, rawResult, sensitiveResult, _a, _b, noise, noisyResult, result;
return tslib_1.__generator(this, function (_c) {
switch (_c.label) {
case 0:
epsilonSoFar = epsilonSoFar.plus(epsilon);
if (epsilonSoFar.greaterThan(maxEpsilon)) {
throw new types_1.PrivacyBudgetExceededError("Exceeded max privacy budget");
}
return [4, (0, sensitivity_1.calculateSensitivity)(sensitiveFunction, options.newShadowIterator, dataset, options.maxConcurrentCalls)];
case 1:
sensitivity = _c.sent();
rawResult = sensitiveFunction(dataset);
_a = decimal_js_1.default.bind;
if (!(rawResult instanceof Promise)) return [3, 3];
return [4, rawResult];
case 2:
_b = (_c.sent());
return [3, 4];
case 3:
_b = rawResult;
_c.label = 4;
case 4:
sensitiveResult = new (_a.apply(decimal_js_1.default, [void 0, _b]))();
noise = probDistFunc(sensitivity, epsilon);
noisyResult = sensitiveResult.plus(noise);
result = {
epsilonBudgetUsed: epsilonAsNumber,
percentBudgetUsed: 1.0 / options.maxCallCount,
result: noisyResult.toNumber(),
};
return [2, (options.debugDangerously === true ? {
epsilonBudgetUsed: result.epsilonBudgetUsed,
percentBudgetUsed: result.percentBudgetUsed,
noiseAdded: noise.toNumber(),
privateResult: sensitiveResult.toNumber(),
result: result.result,
} : result)];
}
});
}); };
};
}
exports.privatizeFactory = privatizeFactory;
function fillDefaultOptions(options) {
var filled = {
maxEpsilon: options.maxEpsilon,
newShadowIterator: options.newShadowIterator,
maxCallCount: (0, util_1.isNumber)(options.maxCallCount) ? options.maxCallCount : 1,
maxConcurrentCalls: (0, util_1.isNumber)(options.maxConcurrentCalls) ?
options.maxConcurrentCalls : 4,
debugDangerously: options.debugDangerously === true,
};
if (options.debugDangerously === true) {
filled.debugDangerously = true;
}
return filled;
}
function validateOptions(options) {
if (!(0, util_1.isNumber)(options.maxEpsilon) || options.maxEpsilon <= 0) {
throw new Error("maxEpsilon must be a number > 0");
}
if (!(0, util_1.isFunction)(options.newShadowIterator)) {
throw new Error("Must provide a newShadowIterator function, please see docs");
}
if (options.maxCallCount !== undefined) {
if (!(0, util_1.isNumber)(options.maxCallCount) || options.maxCallCount < 1) {
throw new Error("Please get a valid integer >= 1 for maxCallCount");
}
}
if (options.maxConcurrentCalls !== undefined) {
if (!(0, util_1.isNumber)(options.maxConcurrentCalls) || options.maxConcurrentCalls < 1) {
throw new Error("Please give a valid integer >= 1 for maxConcurrentCalls");
}
}
var parsed = {
maxEpsilon: options.maxEpsilon,
newShadowIterator: options.newShadowIterator,
maxCallCount: options.maxCallCount,
maxConcurrentCalls: options.maxConcurrentCalls,
debugDangerously: (options.debugDangerously === true ? true : undefined),
};
return parsed;
}
//# sourceMappingURL=privatize.js.map