UNPKG

federer

Version:

Experiments in asynchronous federated learning and decentralized learning

126 lines 5.12 kB
"use strict"; var _ClientPool_training, _ClientPool_available; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClientPool = void 0; const tslib_1 = require("tslib"); const assert = require("assert"); const Reservoir = require("reservoir"); /** * Helper class to keep track of the state of clients. Clients can be marked as * either "available" (meaning that they are not currently training, and are * eligible to start training), or "training" (meaning that they have been given * a training task, and we are awaiting results). * * Clients are tracked by their socket ID, which is a `string`. * * The methods in this class use assertions to enforce the following invariants: * * - All clients start in the available state * - A client is either training or available, but never both * - A client can only transition from available to training, or from training * to available; it cannot transition to the state that it is currently in * * These invariants are helpful to debug server implementations, and to ensure * the reported numbers are accurate. */ class ClientPool { constructor() { _ClientPool_training.set(this, new Set()); _ClientPool_available.set(this, new Set()); } /** * Adds a client to the pool, in the "available" state. * * @throws if the client is already in the pool. */ addAvailableClient(client) { assert(!this.has(client)); tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f").add(client); } /** Returns whether the client is in the "available" state */ isAvailable(client) { assert(this.has(client)); return tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f").has(client); } /** Returns whether the client is in the "training" state. */ isTraining(client) { assert(this.has(client)); return tslib_1.__classPrivateFieldGet(this, _ClientPool_training, "f").has(client); } /** * Changes the state of a client. * * @throws if the client is already in the target state */ transition(client, newState) { if (newState === "training") { assert(this.isAvailable(client)); tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f").delete(client); tslib_1.__classPrivateFieldGet(this, _ClientPool_training, "f").add(client); } else if (newState === "available") { assert(this.isTraining(client)); tslib_1.__classPrivateFieldGet(this, _ClientPool_training, "f").delete(client); tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f").add(client); } else { throw new Error(`Unknown state '${newState}'`); } } /** * Removes a client from the pool. * * @returns `true` if it was removed, `false` if it wasn't in the pool. */ delete(client) { return tslib_1.__classPrivateFieldGet(this, _ClientPool_training, "f").delete(client) || tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f").delete(client); } /** Returns whether a client is in the pool. */ has(client) { return tslib_1.__classPrivateFieldGet(this, _ClientPool_training, "f").has(client) || tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f").has(client); } /** Number of clients in the pool in total. */ get size() { return this.numberTraining + this.numberAvailable; } /** Number of clients in the "training" state. */ get numberTraining() { return tslib_1.__classPrivateFieldGet(this, _ClientPool_training, "f").size; } /** Number of clients in the "available" state. */ get numberAvailable() { return tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f").size; } get clients() { return new Set(...tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f"), tslib_1.__classPrivateFieldGet(this, _ClientPool_training, "f")); } get available() { return tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f"); } get training() { return tslib_1.__classPrivateFieldGet(this, _ClientPool_training, "f"); } get percentTraining() { const percent = (this.numberTraining / this.numberAvailable) * 100; assert(0 <= percent); assert(percent <= 100); return percent; } /** * Samples `n` clients without replacement from the list of available clients, * using reservoir sampling. * * @returns array of `n` sampled client IDs * @throws if there are not enough available clients */ sampleAvailable(n) { assert(0 <= n); assert(n <= this.numberAvailable); const reservoir = Reservoir(n); reservoir.pushSome(...tslib_1.__classPrivateFieldGet(this, _ClientPool_available, "f")); return reservoir; } } exports.ClientPool = ClientPool; _ClientPool_training = new WeakMap(), _ClientPool_available = new WeakMap(); //# sourceMappingURL=ClientPool.js.map