UNPKG

differential-privacy

Version:

Implements Global Differential Privacy for almost any numeric function

102 lines 4.9 kB
"use strict"; 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