Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

full eslint #625

Merged
merged 7 commits into from
Feb 27, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @ts-check

module.exports = {
root: true,
extends: "standard-with-typescript",
parserOptions: {
project: "./tsconfig.eslint.json",
tsconfigRootDir: __dirname,
},
ignorePatterns: ["**/dist/", "*.d.ts"],
env: {
mocha: true,
},
};
4 changes: 3 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ RUN apt-get upgrade -y \
&& apt install -y nodejs

COPY package*.json .
COPY isomorphic-wrtc/package.json isomorphic-wrtc/
COPY discojs/discojs-core/package.json discojs/discojs-core/
COPY discojs/discojs-node/package.json discojs/discojs-node/
COPY server/package*.json server/
COPY server/package.json server/
RUN npm ci

COPY isomorphic-wrtc/ isomorphic-wrtc/
COPY discojs/tsconfig.base.json discojs/
COPY discojs/discojs-core/ discojs/discojs-core/
COPY discojs/discojs-node/ discojs/discojs-node/
Expand Down
2 changes: 0 additions & 2 deletions cli/.eslintignore

This file was deleted.

16 changes: 3 additions & 13 deletions cli/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,5 @@
{
"extends": "standard-with-typescript",
"parserOptions": {
"project": "./tsconfig.eslint.json"
},
"env": {
"node": true,
"es6": true,
"mocha": true
},
"rules": {
// TODO need strict
"@typescript-eslint/strict-boolean-expressions": "off"
}
"env": {
"node": true
}
}
9 changes: 1 addition & 8 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"watch": "nodemon --ext ts --ignore dist --watch ../discojs/discojs-node/dist --watch ../server/dist --watch . --exec npm run",
"start": "npm run build && node dist/benchmark.js",
"build": "tsc",
"lint": "npx eslint --max-warnings 0 .",
"lint": "npx eslint --ext ts --max-warnings 0 .",
"test": ": nothing"
},
"author": "",
Expand All @@ -18,13 +18,6 @@
"tslib": "2"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "4",
"@typescript-eslint/parser": "4",
"eslint": "7",
"eslint-config-standard-with-typescript": "21",
"eslint-plugin-import": "2",
"eslint-plugin-node": "11",
"eslint-plugin-promise": "5",
"nodemon": "3",
"ts-command-line-args": "2"
}
Expand Down
70 changes: 21 additions & 49 deletions cli/src/args.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import { parse } from 'ts-command-line-args'
import { Map } from 'immutable'

import { defaultTasks, Task } from '@epfml/discojs-node'
import { defaultTasks, type Task } from '@epfml/discojs-node'

interface BenchmarkUnsafeArguments {
task: string
interface BenchmarkArguments {
task: Task
numberOfUsers: number
epochs: number
roundDuration: number
batchSize: number
save: boolean
}

type BenchmarkUnsafeArguments = {
[K in keyof BenchmarkArguments as Exclude<K, 'task'>]: BenchmarkArguments[K]
} & {
task: string
help?: boolean
}

const argExample = 'e.g. npm run benchmark -- -u 2 -e 3, runs 2 users for 3 epochs'

const unsafeArgs = parse<BenchmarkUnsafeArguments>(
{
// @ts-expect-error
task: { type: String, alias: 't', description: 'Task', optional: true },
// @ts-expect-error
numberOfUsers: { type: Number, alias: 'u', description: 'Number of users', optional: true },
// @ts-expect-error
epochs: { type: Number, alias: 'e', description: 'Number of epochs', optional: true },
// @ts-expect-error
roundDuration: { type: Number, alias: 'r', description: 'Round duration', optional: true },
// @ts-expect-error
batchSize: { type: Number, alias: 'b', description: 'Round duration', optional: true },
// @ts-expect-error
save: { type: Boolean, alias: 's', description: 'Save logs of benchmark', default: false },
task: { type: String, alias: 't', description: 'Task', defaultValue: 'simple_face' },
numberOfUsers: { type: Number, alias: 'u', description: 'Number of users', defaultValue: 1 },
epochs: { type: Number, alias: 'e', description: 'Number of epochs', defaultValue: 10 },
roundDuration: { type: Number, alias: 'r', description: 'Round duration', defaultValue: 10 },
batchSize: { type: Number, alias: 'b', description: 'Round duration', defaultValue: 10 },
save: { type: Boolean, alias: 's', description: 'Save logs of benchmark', defaultValue: false },
help: { type: Boolean, optional: true, alias: 'h', description: 'Prints this usage guide' }
},
{
Expand All @@ -37,52 +37,24 @@ const unsafeArgs = parse<BenchmarkUnsafeArguments>(
}
)

const taskID = unsafeArgs.task === undefined ? 'simple_face' : unsafeArgs.task
let task = defaultTasks.simpleFace.getTask()

let supportedTasks: Map<string, Task> = Map()
supportedTasks = supportedTasks.set(defaultTasks.simpleFace.getTask().taskID, defaultTasks.simpleFace.getTask())
supportedTasks = supportedTasks.set(defaultTasks.titanic.getTask().taskID, defaultTasks.titanic.getTask())
supportedTasks = supportedTasks.set(defaultTasks.cifar10.getTask().taskID, defaultTasks.cifar10.getTask())

const task_ = supportedTasks.get(taskID)

if (task_ !== undefined) {
task = task_
} else {
const task = supportedTasks.get(unsafeArgs.task)
if (task === undefined) {
throw Error(`${unsafeArgs.task} not implemented.`)
}

// TODO: Default args for non boolean did not seem to work
const numberOfUsers = unsafeArgs.numberOfUsers === undefined ? 1 : unsafeArgs.numberOfUsers
const roundDuration = unsafeArgs.roundDuration === undefined ? 10 : unsafeArgs.roundDuration
const epochs = unsafeArgs.epochs === undefined ? 10 : unsafeArgs.epochs
const batchSize = unsafeArgs.batchSize === undefined ? 10 : unsafeArgs.batchSize

// Override training information
if (task.trainingInformation !== undefined) {
task.trainingInformation.batchSize = batchSize
task.trainingInformation.roundDuration = roundDuration
task.trainingInformation.epochs = epochs
task.trainingInformation.batchSize = unsafeArgs.batchSize
task.trainingInformation.roundDuration = unsafeArgs.roundDuration
task.trainingInformation.epochs = unsafeArgs.epochs
// For DP
// TASK.trainingInformation.clippingRadius = 10000000
// TASK.trainingInformation.noiseScale = 0
}

interface BenchmarkArguments {
task: Task
numberOfUsers: number
epochs: number
roundDuration: number
batchSize: number
save: boolean
}

export const args: BenchmarkArguments = {
task,
numberOfUsers,
epochs,
roundDuration,
batchSize,
save: unsafeArgs.save
}
export const args: BenchmarkArguments = { ...unsafeArgs, task }
2 changes: 1 addition & 1 deletion cli/src/benchmark.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Range } from 'immutable'

import { Disco, TrainingSchemes, TrainerLog, data, Task } from '@epfml/discojs-node'
import { Disco, TrainingSchemes, type TrainerLog, type data, type Task } from '@epfml/discojs-node'

import { startServer, saveLog } from './utils'
import { getTaskData } from './data'
Expand Down
6 changes: 3 additions & 3 deletions cli/src/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import fs from 'node:fs'
import fs_promises from 'fs/promises'
import path from 'node:path'

import { tf, node, data, Task } from '@epfml/discojs-node'
import { tf, node, data, type Task } from '@epfml/discojs-node'

function filesFromFolder (dir: string, folder: string, fractionToKeep: number): string[] {
const f = fs.readdirSync(dir + folder)
Expand Down Expand Up @@ -34,15 +34,15 @@ async function simplefaceData (task: Task): Promise<data.DataSplit> {
const labels = filesPerFolder.flatMap((files, index) => Array(files.length).fill(index))
const files = filesPerFolder.flat()

return await new node.data.NodeImageLoader(task).loadAll(files, { labels: labels })
return await new node.data.NodeImageLoader(task).loadAll(files, { labels })
}

async function cifar10Data (cifar10: Task): Promise<data.DataSplit> {
const dir = '../example_training_data/CIFAR10/'
const files = (await fs_promises.readdir(dir)).map((file) => path.join(dir, file))
const labels = Range(0, 24).map((label) => (label % 10).toString()).toArray()

return await new node.data.NodeImageLoader(cifar10).loadAll(files, { labels: labels })
return await new node.data.NodeImageLoader(cifar10).loadAll(files, { labels })
}

class NodeTabularLoader extends data.TabularLoader<string> {
Expand Down
4 changes: 2 additions & 2 deletions cli/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import http from 'node:http'
import type http from 'node:http'
import fs from 'node:fs'

import { TrainerLog } from '@epfml/discojs-node'
import { type TrainerLog } from '@epfml/discojs-node'
import { Disco } from '@epfml/disco-server'

export async function startServer (): Promise<[http.Server, URL]> {
Expand Down
4 changes: 0 additions & 4 deletions cli/tsconfig.eslint.json

This file was deleted.

2 changes: 0 additions & 2 deletions discojs/discojs-core/.eslintignore

This file was deleted.

15 changes: 0 additions & 15 deletions discojs/discojs-core/.eslintrc.json

This file was deleted.

3 changes: 2 additions & 1 deletion discojs/discojs-core/.mocharc.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"spec": ["src"],
"recursive": true,
"extension": ["ts"],
"spec": ["src/**/*.spec.ts"],
"require": "ts-node/register"
}
12 changes: 3 additions & 9 deletions discojs/discojs-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"scripts": {
"watch": "nodemon --ext ts --ignore dist --exec npm run",
"build": "tsc",
"lint": "npx eslint --max-warnings 0 .",
"lint": "npx eslint --ext ts --max-warnings 0 .",
"test": "mocha",
"docs": "typedoc ./src/index.ts --theme oxide"
},
Expand All @@ -19,33 +19,27 @@
},
"homepage": "https://github.com/epfml/disco#readme",
"dependencies": {
"@koush/wrtc": "0.5",
"@tensorflow/tfjs": "4",
"@types/msgpack-lite": "0.1",
"axios": "0.27",
"gpt3-tokenizer": "1",
"immutable": "4",
"isomorphic-wrtc": "1",
"isomorphic-ws": "4",
"msgpack-lite": "0.1",
"simple-peer": "9",
"tslib": "2",
"url": "0.11",
"ws": "8"
},
"devDependencies": {
"@types/chai": "4",
"@types/mocha": "9",
"@types/simple-peer": "9",
"@typescript-eslint/eslint-plugin": "4",
"@typescript-eslint/parser": "4",
"chai": "4",
"eslint": "7",
"eslint-config-standard-with-typescript": "21",
"mocha": "9",
"nodemon": "3",
"ts-node": "10",
"typedoc": "0.22",
"typedoc-theme-oxide": "0.1",
"typescript": "4"
"typedoc-theme-oxide": "0.1"
}
}
28 changes: 22 additions & 6 deletions discojs/discojs-core/src/aggregator/base.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,31 @@
import { client, Task, tf, AsyncInformant } from '..'
import { type client, type Task, type tf, type AsyncInformant } from '..'

import { EventEmitter } from 'events'

import { Map, Set } from 'immutable'
import { List, Map, Set } from 'immutable'

export enum AggregationStep {
ADD,
UPDATE,
AGGREGATE
}

class AggregationEventEmitter<T> {
private listeners = List<[once: boolean, act: (_: T) => void]>()

on (_: 'aggregation', act: (_: T) => void): void {
this.listeners = this.listeners.push([false, act])
}

once (_: 'aggregation', act: (_: T) => void): void {
this.listeners = this.listeners.push([true, act])
}

emit (_: 'aggregation', aggregated: T): void {
const listeners = this.listeners
this.listeners = this.listeners.filterNot(([once, _]) => once)
listeners.forEach(([_, act]) => { act(aggregated) })
}
}

/**
* Main, abstract, aggregator class whose role is to buffer contributions and to produce
* a result based off their aggregation, whenever some defined condition is met.
Expand All @@ -31,7 +47,7 @@ export abstract class Base<T> {
* Triggers the resolve of the result promise and the preparation for the
* next aggregation round.
*/
private readonly eventEmitter: EventEmitter
private readonly eventEmitter: AggregationEventEmitter<T>

protected informant?: AsyncInformant<T>
/**
Expand Down Expand Up @@ -71,7 +87,7 @@ export abstract class Base<T> {
*/
public readonly communicationRounds = 1
) {
this.eventEmitter = new EventEmitter()
this.eventEmitter = new AggregationEventEmitter()
this.contributions = Map()
this._nodes = Set()

Expand Down
2 changes: 1 addition & 1 deletion discojs/discojs-core/src/aggregator/get.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { aggregator, Task } from '..'
import { aggregator, type Task } from '..'

/**
* Enumeration of the available types of aggregator.
Expand Down
4 changes: 2 additions & 2 deletions discojs/discojs-core/src/aggregator/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WeightsContainer } from '../weights'
import { Base } from './base'
import { type WeightsContainer } from '../weights'
import { type Base } from './base'

export { Base as AggregatorBase, AggregationStep } from './base'
export { MeanAggregator } from './mean'
Expand Down
7 changes: 3 additions & 4 deletions discojs/discojs-core/src/aggregator/mean.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { assert, expect } from 'chai'
import { Map } from 'immutable'
import { type Map } from 'immutable'

import { aggregator, defaultTasks, client, Task, tf } from '..'
import { aggregator, defaultTasks, type client, type Task, type tf } from '..'
import { AggregationStep } from './base'

const task = defaultTasks.titanic.getTask()
Expand Down Expand Up @@ -109,7 +108,7 @@ describe('mean aggregator tests', () => {
mockAggregatedWeights = 5
result = aggregator.receiveResult()
const t1 = Date.now()
newWeights.map(async (w) => aggregator.add(w.toString(), w, t1))
newWeights.map((w) => aggregator.add(w.toString(), w, t1))
expect(await result).eql(mockAggregatedWeights)
expect(aggregator.round).equal(2)
})
Expand Down
Loading
Loading