From ddcfe631b30a65f428c2ecd0d59fda9f8149ed14 Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 8 Aug 2024 17:02:31 -0500 Subject: [PATCH 1/4] package-info.java: relax reproducibility --- .../java/org/scijava/ops/api/package-info.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/scijava-ops-api/src/main/java/org/scijava/ops/api/package-info.java b/scijava-ops-api/src/main/java/org/scijava/ops/api/package-info.java index 1948c5d66..29cce5d5e 100644 --- a/scijava-ops-api/src/main/java/org/scijava/ops/api/package-info.java +++ b/scijava-ops-api/src/main/java/org/scijava/ops/api/package-info.java @@ -64,10 +64,12 @@ * An Op is an algorithm adhering to the following traits: *

*
    - *
  1. Ops are stateless and deterministic - with no internal state, calling an - * Op two times on the same inputs will produce the same output.
  2. *
  3. Ops are named - this name conveys an Op's purpose, and allows us to find * all Ops implementing a particular operation
  4. + *
  5. Ops have a number of input and output parameters, each defined by a + * type
  6. + *
  7. Ops adhere to a {@link java.lang.FunctionalInterface}, defining how it + * operates
  8. *
*

* Using the name and the combination of input and output parameters, we can @@ -151,10 +153,11 @@ * and SciJava Ops will take care to call the correct Op based on the concrete * inputs provided. *

  • Result-equivalence, and therefore reproducibility, in Ops, is guaranteed - * within an OpEnvironment and a set of input objects, but not just for Op - * calls. This allows us to ensure reproducible pipelines, but also allows us to - * introduce new Ops into the pipeline or to run pipelines on different inputs - * without changing the pipeline itself.
  • + * within an OpEnvironment and a set of input objects, when deterministic + * algorithms are used. Cognizance of algorithm determinism allows users to + * create reproducible pipelines, however determinism is not a + * requirement for Ops, as it would preclude many valuable algorithms from + * becoming an Op. * */ From 36fd044c2bdef628c434d5aa98ac60b45e078e6f Mon Sep 17 00:00:00 2001 From: Gabriel Selzer Date: Thu, 8 Aug 2024 17:40:48 -0500 Subject: [PATCH 2/4] Add Op determinism as a best practice --- docs/ops/doc/WritingYourOwnOpPackage.md | 43 +++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/docs/ops/doc/WritingYourOwnOpPackage.md b/docs/ops/doc/WritingYourOwnOpPackage.md index 9454a8054..3556cd51c 100644 --- a/docs/ops/doc/WritingYourOwnOpPackage.md +++ b/docs/ops/doc/WritingYourOwnOpPackage.md @@ -8,6 +8,49 @@ You'll find this page organized into two broad sections. The first section descr SciJava Ops is designed for modularity, extensibility, and granularity - you can exploit these aspects by adhering to the following guidelines when writing Ops: +### Fostering reproduciblity + +[Determinism](https://en.wikipedia.org/wiki/Deterministic_algorithm) is a valuable aspect of scientific computing, as it is necessary for reproducible science. As many powerful algorithms, due to internal state or parallel processing, behave non-deterministically SciJava Ops does not require Ops be deterministic, however if your Op can be deterministic, we highly recommend you make it so. Consider the following algorithm: + +```java +/** + * A simple noise adder + * @param input the input data + * @implNote op name="filter.addNoise" type=Inplace + */ +public static void addNoise(double[] data) { + Random r = new Random(); + for(int i = 0; i < data.length; i++) { + // Add a number in [-0.5, 0.5) + data[i] += (r.nextDouble() - 0.5); + } +} +``` + +We can make it deterministic by adding an `@Nullable` `seed` parameter, with a default value if the user does not pass one: + +```java +/** + * A simple noise adder + * @param input the input data + * @param seed the seed to the {@link java.util.Random} + * @implNote op name="filter.addNoise" type=Inplace + */ +public static void addNoise(double[] data, @Nullable Long seed) { + // use default seed if not provided + if (seed == null) { + seed = 0xdeadbeef; + } + Random r = new Random(seed); + for(int i = 0; i < data.length; i++) { + // Add a number in [-0.5, 0.5) + data[i] += (r.nextDouble() - 0.5); + } +} +``` + +These small steps ensure that user workflows return the same result every time, even years later! + ### Using Dependencies If you are writing an Op that performs many intermediate operations, there's a good chance someone else has written (and even optimized) some or all of them. From c58a8110e7f6961bf18e85ae11cfaaafed0c9d52 Mon Sep 17 00:00:00 2001 From: hinerm Date: Mon, 12 Aug 2024 16:37:04 -0500 Subject: [PATCH 3/4] package-info: remove trailing space --- .../src/main/java/org/scijava/ops/api/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scijava-ops-api/src/main/java/org/scijava/ops/api/package-info.java b/scijava-ops-api/src/main/java/org/scijava/ops/api/package-info.java index 29cce5d5e..fbdb6317e 100644 --- a/scijava-ops-api/src/main/java/org/scijava/ops/api/package-info.java +++ b/scijava-ops-api/src/main/java/org/scijava/ops/api/package-info.java @@ -157,7 +157,7 @@ * algorithms are used. Cognizance of algorithm determinism allows users to * create reproducible pipelines, however determinism is not a * requirement for Ops, as it would preclude many valuable algorithms from - * becoming an Op. + * becoming an Op. * */ From 4db2dab303bb56e5bff1614662073e5f486f67a6 Mon Sep 17 00:00:00 2001 From: hinerm Date: Mon, 12 Aug 2024 16:37:18 -0500 Subject: [PATCH 4/4] Determinism: reframe slightly for emphasis --- docs/ops/doc/WritingYourOwnOpPackage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ops/doc/WritingYourOwnOpPackage.md b/docs/ops/doc/WritingYourOwnOpPackage.md index 3556cd51c..757fc6432 100644 --- a/docs/ops/doc/WritingYourOwnOpPackage.md +++ b/docs/ops/doc/WritingYourOwnOpPackage.md @@ -10,7 +10,7 @@ SciJava Ops is designed for modularity, extensibility, and granularity - you can ### Fostering reproduciblity -[Determinism](https://en.wikipedia.org/wiki/Deterministic_algorithm) is a valuable aspect of scientific computing, as it is necessary for reproducible science. As many powerful algorithms, due to internal state or parallel processing, behave non-deterministically SciJava Ops does not require Ops be deterministic, however if your Op can be deterministic, we highly recommend you make it so. Consider the following algorithm: +[Determinism](https://en.wikipedia.org/wiki/Deterministic_algorithm) is a valuable aspect of scientific computing, as our goal is to facilitate reproducible science. As many powerful algorithms behave non-deterministically, due to factors such as internal state or parallel processing, SciJava Ops does not *require* Ops be deterministic. However, if your Op *can be* deterministic, we highly recommend you make it so. Consider the following algorithm: ```java /** @@ -49,7 +49,7 @@ public static void addNoise(double[] data, @Nullable Long seed) { } ``` -These small steps ensure that user workflows return the same result every time, even years later! +These small steps give workflows the best chance to return consistent results every time, even years later! ### Using Dependencies