From 904686be95abaeba76c1145efa6a8cd9d3219467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Olivier=20L=C3=A9obal?= Date: Fri, 7 Apr 2023 18:36:46 +0200 Subject: [PATCH] chore: release 0.26.0 (#293) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Olivier LĂ©obal Signed-off-by: ThibaultFy <50656860+ThibaultFy@users.noreply.github.com> Co-authored-by: ThibaultFy <50656860+ThibaultFy@users.noreply.github.com> --- .gitignore | 1 + docs/source/additional/release.rst | 46 ++++++++--- docs/source/additional/releases.yaml | 38 +++++++++ docs/source/conf.py | 4 +- .../source/documentation/get_performances.rst | 8 +- .../images/substra-0.26-task-duration.png | Bin 0 -> 15676 bytes examples/diabetes_example/run_diabetes.py | 76 ++++++------------ 7 files changed, 106 insertions(+), 67 deletions(-) create mode 100644 docs/source/static/images/substra-0.26-task-duration.png diff --git a/.gitignore b/.gitignore index 32c1cad7..dde3ea97 100644 --- a/.gitignore +++ b/.gitignore @@ -151,6 +151,7 @@ docs/source/substrafl_doc/examples # Data folder when run locally **/data_iris **/data_mnist +**/data # Algo files folder when run locally **/algo_files diff --git a/docs/source/additional/release.rst b/docs/source/additional/release.rst index 683a35ff..4caf52bd 100644 --- a/docs/source/additional/release.rst +++ b/docs/source/additional/release.rst @@ -28,8 +28,34 @@ This is an overview of the main changes, please have a look at the changelog of - `backend changelog `__ - `orchestrator changelog `__ -Substra 0.25.0 - 2023-02-17 -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Substra 0.26.0 --- 2023-04-03 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- Improve backend performance when handling large amounts of compute plans and tasks. This will result in faster front-end pages. +- Rename Algo to Function. +- SSO Login +- Experimental: Add a task duration breakdown for every task in the front-end. Note that this is an experimental feature and only works on the current backend you are logged into. + +.. figure:: ../static/images/substra-0.26-task-duration.png + :align: center + :alt: Screenshot of task duration + +Front-end: + +- Fix issue where a performance of ``0`` was displayed as ``-``. + +SubstraFL: + +- **BREAKING CHANGE**: ``algo`` are now passed as a parameter to the ``strategy`` and not to ``execute_experiement`` anymore. +- **BREAKING CHANGE**: a ``strategy`` needs to implement a new method ``build_graph`` to build the graph of tasks to be executed in ``execute_experiment``. +- **BREAKING CHANGE**: ``predict`` method of ``strategy`` has been renamed to ``perform_predict``. +- **BREAKING CHANGE**: clarify ``EvaluationStrategy`` arguments: change ``rounds`` to ``eval_frequency`` and ``eval_rounds``. +- Fix an issue where ``aggregation_lr`` could not be changed in the Scaffold strategy. +- Add Initialization task to each strategy in SubstraFL + + +Substra 0.25.0 --- 2023-02-17 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - **BREAKING CHANGE**: ``DataSampleSpec`` does not have a ``test_only`` field anymore. - SubstraFL: It is now possible to test on an organization where no training have been performed. @@ -37,15 +63,15 @@ Substra 0.25.0 - 2023-02-17 - Fix an issue where Skaffold spawned too many backends. - Add contributing guide & code of conduct to all repos. -Substra 0.24.0 - 2023-01-13 -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Substra 0.24.0 --- 2023-01-13 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Fix issue where launching a large compute sometimes fails with: ``ERROR: could not serialize access due to read/write dependencies among transactions (SQLSTATE 40001)`` - Documentation: add **contributing guide** and **code of conduct** - Update **Substra Tools** base docker image to ``substra-tools:0.20.0-nvidiacuda11.6.0-base-ubuntu20.04-python3.*`` -Substra 0.23.1 - 2022-11-24 -^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Substra 0.23.1 --- 2022-11-24 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Main changes @@ -195,8 +221,8 @@ Substra Backend: - Prevent use of ``__`` in asset metadata keys -Substra 0.22.0 - 2022-10-20 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Substra 0.22.0 --- 2022-10-20 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Main changes @@ -350,8 +376,8 @@ could be replace with: tools.metrics.execute(AUC()) -Substra 0.21.0 - 2022-09-12 -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Substra 0.21.0 --- 2022-09-12 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ This is our first open source release since 2021! When the product was closed source it used to be named Connect. It is now renamed Substra. diff --git a/docs/source/additional/releases.yaml b/docs/source/additional/releases.yaml index f676356f..8134cb24 100644 --- a/docs/source/additional/releases.yaml +++ b/docs/source/additional/releases.yaml @@ -9,6 +9,44 @@ components: # for table headers - substra-tests releases: + - version: 0.26.0 + components: + substrafl: + version: 0.35.0 + link: https://github.com/Substra/substrafl/releases/tag/0.35.0 + substra: + version: 0.43.0 + link: https://github.com/Substra/substra/releases/tag/0.43.0 + substra-tools: + version: 0.20.0 + link: https://github.com/Substra/substra-tools/releases/tag/0.20.0 + substra-backend: + version: 0.36.0 + link: https://github.com/Substra/substra-backend/releases/tag/0.36.0 + helm: + version: 22.4.2 + link: https://artifacthub.io/packages/helm/substra/substra-backend/22.4.2 + orchestrator: + version: 0.33.0 + link: https://github.com/Substra/orchestrator/releases/tag/0.33.0 + helm: + version: 7.4.12 + link: https://artifacthub.io/packages/helm/substra/orchestrator/7.4.12 + substra-frontend: + version: 0.40.0 + link: https://github.com/Substra/substra-frontend/releases/tag/0.40.0 + helm: + version: 1.0.16 + link: https://artifacthub.io/packages/helm/substra/substra-frontend/1.0.16 + hlf-k8s: + version: 0.2.3 + link: https://github.com/Substra/hlf-k8s/releases/tag/0.2.3 + helm: + version: 10.2.3 + link: https://artifacthub.io/packages/helm/substra/hlf-k8s/10.2.3 + substra-tests: + version: 0.39.0 + link: https://github.com/Substra/substra-tests/releases/tag/0.39.0 - version: 0.25.0 components: substrafl: diff --git a/docs/source/conf.py b/docs/source/conf.py index c7442e94..aa709cd5 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -29,9 +29,9 @@ TMP_FOLDER = Path(__file__).parents[2] / "tmp" if os.environ.get("READTHEDOCS_VERSION_TYPE") == "tag": - SUBSTRA_VERSION = "0.42.0" + SUBSTRA_VERSION = "0.43.0" TOOLS_VERSION = "0.20.0" - SUBSTRAFL_VERSION = "0.34.0" + SUBSTRAFL_VERSION = "0.35.0" else: SUBSTRA_VERSION = "main" diff --git a/docs/source/documentation/get_performances.rst b/docs/source/documentation/get_performances.rst index 7b2e28a8..9c8f43dd 100644 --- a/docs/source/documentation/get_performances.rst +++ b/docs/source/documentation/get_performances.rst @@ -59,11 +59,11 @@ This script will automatically end if the :code:`performance.json` file has not df = pd.DataFrame(dict_perf) for _, row in df.iterrows(): - if row["testtuple_key"] in logged_rows: + if row["task_key"] in logged_rows: continue - logged_rows.append(row["testtuple_key"]) + logged_rows.append(row["task_key"]) - step = int(row["round_idx"]) if row["round_idx"] is not None else int(row["testtuple_rank"]) + step = int(row["round_idx"]) if row["round_idx"] is not None else int(row["task_rank"]) - log_metric(f"{row['metric_name']}_{row['worker']}", row["performance"], step) + log_metric(f"{row['function_name']}_{row['worker']}", row["performance"], step) diff --git a/docs/source/static/images/substra-0.26-task-duration.png b/docs/source/static/images/substra-0.26-task-duration.png new file mode 100644 index 0000000000000000000000000000000000000000..43ebf6c1c1ed75b5c987ad30caed52e8ef25d502 GIT binary patch literal 15676 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