From ad46be8a160ff5e5e8d967261a2c50aabffbb0e7 Mon Sep 17 00:00:00 2001 From: Edoardo Mancini Date: Fri, 11 Oct 2024 13:34:13 +0000 Subject: [PATCH 1/9] Update dates CICD --- .github/workflows/update_post_dates.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/update_post_dates.yml b/.github/workflows/update_post_dates.yml index c1398a89..52addc09 100644 --- a/.github/workflows/update_post_dates.yml +++ b/.github/workflows/update_post_dates.yml @@ -24,6 +24,7 @@ jobs: - name: Check for changes id: check_changes + shell: bash run: | git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' From 77dc96f282d51b038e3a84519b63cc2a3323c3fb Mon Sep 17 00:00:00 2001 From: Alexandros Kouretsis Date: Wed, 16 Oct 2024 15:45:40 +0300 Subject: [PATCH 2/9] Closes #195: HPC vs reproducibility post (#232) * HPC vs reproducibility post (#195) * Improve intro text and text wrap * Adding links to resources * Update wordlist * Apply styler * Add description in yaml header * Add logrx to execute the workflow * Adding floating point paragraph * Add a link to round-robin explanation * Add daemon definition * Adding explicit description of the simulation * Fix reference to log ile * Update the dispatcher argument to "none" * Update WORDLIST.txt * Apply styler fixes * Adding affiliation * Fix typo * Replace RNG reference link with doi URL --------- Co-authored-by: Edoardo Mancini <53403957+manciniedoardo@users.noreply.github.com> --- inst/WORDLIST.txt | 27 ++ .../zzz_DO_NOT_EDIT_the__tensio.../appendix.R | 73 ++++ .../cache_execution.rds | Bin 0 -> 262 bytes .../mirai_workflow.R | 26 ++ .../mirai_workflow.log | 171 ++++++++++ ...__reproducibility_vs.__parallelization.qmd | 320 ++++++++++++++++++ 6 files changed, 617 insertions(+) create mode 100644 posts/zzz_DO_NOT_EDIT_the__tensio.../appendix.R create mode 100644 posts/zzz_DO_NOT_EDIT_the__tensio.../cache_execution.rds create mode 100644 posts/zzz_DO_NOT_EDIT_the__tensio.../mirai_workflow.R create mode 100644 posts/zzz_DO_NOT_EDIT_the__tensio.../mirai_workflow.log create mode 100644 posts/zzz_DO_NOT_EDIT_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd diff --git a/inst/WORDLIST.txt b/inst/WORDLIST.txt index 3dd3efd4..f668b780 100644 --- a/inst/WORDLIST.txt +++ b/inst/WORDLIST.txt @@ -1120,6 +1120,33 @@ astrazeneca laura MeetLaura needleman +Alexandros +clusterSetRNGStream +CMRG +doFuture +elbersb +filelock +HPC +Kouretsis +L'Ecuyer +mirai +nanonext +opre +parallelization +Parallelization +parallelizes +parallelMap +pubsonline +reprovision +shikokuchuo +tensio +tidylog +tidyr +wlandau +zzz +axecute +readRDS +saveRDS WEL Parmar RMarkdown diff --git a/posts/zzz_DO_NOT_EDIT_the__tensio.../appendix.R b/posts/zzz_DO_NOT_EDIT_the__tensio.../appendix.R new file mode 100644 index 00000000..c69926b0 --- /dev/null +++ b/posts/zzz_DO_NOT_EDIT_the__tensio.../appendix.R @@ -0,0 +1,73 @@ +suppressMessages(library(dplyr)) +# markdown helpers -------------------------------------------------------- + +markdown_appendix <- function(name, content) { + paste(paste("##", name, "{.appendix}"), " ", content, sep = "\n") +} +markdown_link <- function(text, path) { + paste0("[", text, "](", path, ")") +} + + + +# worker functions -------------------------------------------------------- + +insert_source <- function(repo_spec, name, + collection = "posts", + branch = "main", + host = "https://github.com", + text = "Source", + file_name) { + path <- paste( + host, + repo_spec, + "tree", + branch, + collection, + name, + file_name, + sep = "/" + ) + return(markdown_link(text, path)) +} + +insert_timestamp <- function(tzone = Sys.timezone()) { + time <- lubridate::now(tzone = tzone) + stamp <- as.character(time, tz = tzone, usetz = TRUE) + return(stamp) +} + +insert_lockfile <- function(repo_spec, name, + collection = "posts", + branch = "main", + host = "https://github.com", + text = "Session info") { + path <- path <- "https://pharmaverse.github.io/blog/session_info.html" + + return(markdown_link(text, path)) +} + + + +# top level function ------------------------------------------------------ + +insert_appendix <- function(repo_spec, name, collection = "posts", file_name) { + appendices <- paste( + markdown_appendix( + name = "Last updated", + content = insert_timestamp() + ), + " ", + markdown_appendix( + name = "Details", + content = paste( + insert_source(repo_spec, name, collection, file_name = file_name), + # get renv information, + insert_lockfile(repo_spec, name, collection), + sep = ", " + ) + ), + sep = "\n" + ) + knitr::asis_output(appendices) +} diff --git a/posts/zzz_DO_NOT_EDIT_the__tensio.../cache_execution.rds b/posts/zzz_DO_NOT_EDIT_the__tensio.../cache_execution.rds new file mode 100644 index 0000000000000000000000000000000000000000..880f563ccee75652ee5f9e2cfe065d7e71eb1b26 GIT binary patch literal 262 zcmV+h0r~zPiwFP!000001FexkOT#b}fM1hV+&~a^+ASBEA{LM0O%EPDP4OnL&D$(w zDW<8t`pccKTLPsAL1^G5-wQ9_3(4CY03@VRfXo;nds#o-+yRIMCxYX=--Hxqygu$) zkDl(8rA~dJtFSPnR=84^mG!hGccr?@f2i7$YHjt|5| + 1 foo foo foo foo foo + +-------------------------------------------------------------------------------- +- Log Output File - +-------------------------------------------------------------------------------- +Log name: mirai_workflow.log +Log path: /home/ale/blog/posts/zzz_DO_NOT_EDIT_the__tensio... diff --git a/posts/zzz_DO_NOT_EDIT_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd b/posts/zzz_DO_NOT_EDIT_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd new file mode 100644 index 00000000..be355758 --- /dev/null +++ b/posts/zzz_DO_NOT_EDIT_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd @@ -0,0 +1,320 @@ +--- +title: "The Tension of High-Performance Computing: Reproducibility vs. Parallelization" +author: + - name: Alexandros Kouretsis + - name: APPSILON +description: "Discover how to manage parallel processing and ensure + reproducibility in drug development using the {mirai} package and other HPC + tools." +# Note that the date below will be auto-updated when the post is merged. +date: "2024-12-01" +# Please do not use any non-default categories. +# You can find the default categories in the repository README.md +categories: [Submissions, Technical] +# Feel free to change the image +image: "pharmaverse.png" + +--- + + + +```{r setup, include=FALSE} +long_slug <- "zzz_DO_NOT_EDIT_the__tensio..." +# renv::use(lockfile = "renv.lock") +``` + + + +## Harnessing HPC for Drug Development + +In pharmaceutical research, high-performance computing (HPC) plays a pivotal +role in driving advancements in drug discovery and development. From analyzing +vast genomic datasets to simulating drug interactions across diverse +populations, HPC enables researchers to tackle complex computational tasks at +high speeds. As pharmaceutical research becomes increasingly data-driven, the +need for powerful computational tools has grown, allowing for more accurate +predictions, faster testing, and more efficient processes. However, with the +growing complexity and scale of these computations, ensuring reproducibility +of results becomes a significant challenge. + +In this blog post, we will explore common reproducibility challenges in drug +development and simulations, using the +[`{mirai}`](https://shikokuchuo.net/mirai/) package as a backend solution to +manage parallelization. + +## The Problem: Reproducibility in Parallel Processing + +Imagine a research team working on a cutting-edge drug development project. To +process and analyze vast amounts of data efficiently, they leverage parallel +processing, distributing tasks across multiple processors. This approach +significantly accelerates their work, enabling them to handle large datasets and +complex computations in a fraction of the time. + +However, the team soon encounters an issue. Each time they rerun the same +processing tasks with identical input parameters, the results differ slightly. +This raises a major concern: *the results are not reproducible.* In industries +like pharmaceuticals, where accuracy and consistency are critical, +reproducibility is not just important—it's a regulatory requirement. + +For example, in large-scale Monte Carlo simulations, small differences can arise +not only from changes in execution order across processors but also from +inconsistencies between workers or difficulties in maintaining synchronized +random number generation (RNG) streams. Furthermore, the more complex the +environment—with multiple components such as distributed workers, different +hardware, or varying system configurations—the harder it becomes to reprovision +the exact same environment and repeat the computations exactly. As these +variations accumulate, ensuring consistent and reproducible results becomes +a significant challenge in data-driven research. + +### Tracking Operations in Parallel Computing + +Let’s explore a simple scenario where parallelization creates confusion in +tracking operations due to the asynchronous nature of task execution and +logging. For this, we will also use the +[`{tidylog}`](https://github.com/elbersb/tidylog) package, which tracks and logs +`{dplyr}` operations, providing insight into how the computations are executed +across multiple workers. + +We'll create our workflow in a script and run it using the `{logrx}` package +from Pharmaverse. The workflow will be written as an expression using +`base::substitute()`, which will help generate the complete script. In our +example, we'll start four daemons. A daemon is a background process that runs in +the background continuously and handles specific computing tasks. + +```{r, message=FALSE, eval=TRUE} +mirai_workflow <- substitute({ + library("mirai") + library("dplyr") + + log_file <- tempfile() + + # start parallel workers + mirai::daemons(4) + + # load libraries on each worker and set up logging to a file + mirai::everywhere( + { + library("dplyr") + library("tidylog") + + # Define function to log messages to the log file + log_to_file <- \(txt) cat(txt, file = log_file, sep = "\n", append = TRUE) + options("tidylog.display" = list(message, log_to_file)) + }, + log_file = log_file + ) + + # perform computations in parallel + m <- mirai_map(letters[1:5], \(x) { + mutate(tibble(.rows = 1), "{x}" := sample(1:100, 1)) + }) + + # collect results + result <- m[] |> bind_cols() + + mirai::daemons(0) + + print( + list( + logs = readLines(log_file), + result = result + ) + ) +}) +``` + +In the above code chunk, we set up a parallel processing environment using the +`{mirai}` package. The function `mirai_map()` is used to apply a mutating +function in parallel to a tibble for each element of `letters`, logging the +operations to a file using the `{tidylog}` package. However, while we can log +each operation as it happens, due to the parallel nature of `{mirai}`, the +logging does not occur in a controlled or sequential order. *Each daemon +executes its task independently, and the order of logging in the file will +depend on the completion times of these parallel processes rather than the +intended flow of operations.* + +> Parallel computations can obscure the traceability of operations + +This lack of control can lead to a situation where the log entries do not +reflect the actual sequence in which the `{dplyr}` commands were expected to be +processed. Although the operations themselves are carried out correctly, the +asynchronous logging may create challenges in *tracing* and *debugging* the +process, as entries in the log file could appear out of order, giving an +incomplete or misleading representation of the task flow. + +Let's first save the code to an R script called `mirai_workflow.R`. This step +helps ensure that the execution can be properly tracked and documented: + +```{r} +mirai_workflow |> + deparse() |> + writeLines("mirai_workflow.R") +``` + +Next, we execute the script using `logrx::axecute()`, which not only runs the +workflow but also logs key metadata and outputs for enhanced traceability and +reproducibility: + +```{r, eval=FALSE} +logrx::axecute("mirai_workflow.R", to_report = "result") +``` + +```{r cache_exec, eval=FALSE, echo=FALSE} +# run this to refresh cache and get a non ordered log file +res_to_cache <- source("mirai_workflow.R") +saveRDS(res_to_cache$value, "cache_execution.rds") +``` + +```{r, echo=FALSE} +readRDS("cache_execution.rds") +``` + +Upon examining the log file generated, you'll notice that the entries are not in +the same order as the commands were dispatched. This illustrates the inherent +difficulty in maintaining a consistent logging sequence for parallel tasks, +especially since the timing of each process completion and log recording is +unpredictable. + +Additionally, it is worth noting that `logrx` does not capture the logging +performed by `{tidylog}` during the execution of tasks on `{mirai}` daemons. +This is because the daemons run as independent R processes, and the logging +messages are not propagated back to the parent process in a straightforward +manner. As described in `{mirai}`'s documentation, daemons are responsible for +handling tasks asynchronously, and messages logged within these processes do not +automatically integrate into the parent session. Therefore, we access +`{tidylog}` messages indirectly, by reading the dedicated log file +(`log_file`) that each worker writes to during execution. + +### Task Dispatching and RNG Management + +By default, `{mirai}` uses an advanced dispatcher to manage task distribution +efficiently, scheduling tasks in a First-In-First-Out manner and leveraging +[`{nanonext}`](https://shikokuchuo.net/nanonext/) primitives for zero-latency, +resource-free task management. However, its asynchronous execution can hinder +reproducibility, especially with random number generation (RNG) or tasks needing +strict order. + +To enhance reproducibility, `{mirai}` allows disabling the dispatcher which +usually decides the order in which tasks are run. Instead, it connects directly +to the workers one by one in a simple order +(see [round-robin](https://en.wikipedia.org/wiki/Round-robin_scheduling)). While +less efficient, this approach provides greater control over task execution and +is better suited for ensuring reproducibility by initializing +[L'Ecuyer-CMRG RNG streams](doi:10.1287/opre.47.1.159). + +In the following example, we simulate drug efficacy across different patient +cohorts using parallel processing with the `{mirai}` package. We define three +cohorts, each with a different mean drug effect and standard deviation, and +initialize four daemons to handle the computations. + +```{r} +library(mirai) +library(dplyr, warn.conflicts = FALSE) + +# Parameters for the simulation +cohorts <- tribble( + ~patient_count, ~mean_effect, ~sd_effect, + 1000, 0.7, 0.1, + 1000, 0.65, 0.15, + 1000, 0.75, 0.05 +) + +# Start daemons with consistent RNG streams +x <- mirai::daemons( + n = 4, + dispatcher = "none", # For mirai versions below 1.3.0, use dispatcher = FALSE + seed = 123 +) + +# Parallel simulation for each row of the cohorts table +m <- mirai::mirai_map(cohorts, \(patient_count, mean_effect, sd_effect) { + dplyr::tibble( + patient_id = 1:patient_count, + efficacy = rnorm(patient_count, mean = mean_effect, sd = sd_effect) + ) +}) + +results <- m[] |> bind_rows() + +x <- mirai::daemons(0, dispatcher = "none") + +results %>% + group_by(patient_id) %>% + summarise( + mean_efficacy = mean(efficacy), + sd_efficacy = sd(efficacy) + ) +``` + +We used `tribble()` to define the simulation parameters and +initialize 4 daemons with `dispatcher = "none"` and a fixed seed to ensure +consistent random number generation across tasks. The `mirai_map()` function +parallelizes the drug efficacy simulation, and the results are combined using +`bind_rows()` for further analysis. + +Disabling the dispatcher gives more control over task execution, ensuring +reproducibility. If you repeat the computation you +will notice that it generates consistent results. However, this approach comes +at a cost. Disabling the dispatcher may lead to inefficient resource utilization +when tasks are unevenly distributed, as some daemons may remain idle. While +reproducibility is prioritized, we sacrifice some performance, especially +when handling tasks with varying workloads. + +Reproducibility becomes trickier when using parallelization frameworks like +`{parallelMap}`, `{doFuture}`, and `{future}`, as each handles random number +generation (RNG) differently. While `set.seed()` works for sequential tasks, +parallel tasks need careful management of RNG streams, often using specific +methods like "L’Ecuyer-CMRG" or functions like `clusterSetRNGStream()` to keep +results consistent. Each framework has its own approach, so it's important to +understand how each one manages RNG to ensure reproducibility. + +Even without random numbers, simple tasks—like adding floating-point numbers—can +give different results in parallel processing. This happens because +floating-point numbers aren’t exactly represented, and the order of operations +can affect the outcome. In parallel environments, where tasks finish in +different orders, these small differences can add up, making it harder to +reproduce results in large computations. + +## Closing Thoughts + +While we've explored the basics of reproducibility in parallel computing with +simple examples, the challenges extend beyond random number generation. Issues +such as *process synchronization*, using tools like lock files (see for example +[`{filelock}`](https://r-lib.github.io/filelock/)), become critical +in multi-process environments. *Floating-point arithmetic* adds complexity, +particularly when distributed across heterogeneous systems with varying +architectures and precision. *Managing dependencies* also becomes more +intricate as tasks grow in complexity, and ensuring *error recovery* in a +controlled manner is vital to avoid crashes or inconsistent results in +large-scale operations. + +Powerful tools like [`{targets}`](https://docs.ropensci.org/targets/) and +[`{crew}`](https://wlandau.github.io/crew/) can help tackle these advanced +challenges. `{targets}` is a workflow orchestration tool that manages +dependencies, automates reproducible pipelines, and ensures consistent results +across runs. Meanwhile, `{crew}` extends this by efficiently managing +distributed computing tasks, allowing for seamless scaling, load balancing, +and error handling across local processes or cloud environments. Together, these +tools simplify the execution of complex high-performance computing (HPC) +workflows, providing flexibility and robustness for scaling computations while +trying for maintaining control and reproducibility. + +This blog post has hopefully increased your intuition about the challenges that +may arise when incorporating HPC into your work. By understanding these +complexities, you’ll be better positioned to make informed decisions about the +trade-offs—such as balancing performance and reproducibility—that are most +relevant to your specific case. As your computations scale, finding the right +balance between efficiency, accuracy, and reproducibility will be crucial for +the success of your projects. + + + +```{r, echo=FALSE} +source("appendix.R") +insert_appendix( + repo_spec = "pharmaverse/blog", + name = long_slug, + # file_name should be the name of your file + file_name = list.files() %>% stringr::str_subset(".qmd") %>% first() +) +``` From 2521d097083330395049c384ef899f64f47b85f3 Mon Sep 17 00:00:00 2001 From: Edoardo Mancini Date: Wed, 16 Oct 2024 13:04:17 +0000 Subject: [PATCH 3/9] Update to CICD dates --- .github/workflows/update_post_dates.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update_post_dates.yml b/.github/workflows/update_post_dates.yml index 52addc09..e2649f30 100644 --- a/.github/workflows/update_post_dates.yml +++ b/.github/workflows/update_post_dates.yml @@ -43,4 +43,4 @@ jobs: git commit -m "Auto-update blog post date" git push origin main env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PHARMAVERSE_BOT }} From d95f638225a52d82223e5b5c1eaea97d51d200f6 Mon Sep 17 00:00:00 2001 From: Edoardo Mancini Date: Wed, 16 Oct 2024 13:27:03 +0000 Subject: [PATCH 4/9] Update CICD dates --- .github/workflows/update_post_dates.yml | 33 +++++++++---------------- 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/.github/workflows/update_post_dates.yml b/.github/workflows/update_post_dates.yml index e2649f30..2bbf4cdf 100644 --- a/.github/workflows/update_post_dates.yml +++ b/.github/workflows/update_post_dates.yml @@ -14,7 +14,8 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 with: - ref: main # replace with the branch you want to checkout + ref: main + token: ${{ secrets.PHARMAVERSE_BOT }} - name: Run update_post_dates run: Rscript R/update_post_dates.R # running the R script with Rscript @@ -22,25 +23,13 @@ jobs: - name: Configure Git safe directory run: git config --global --add safe.directory /__w/blog/blog - - name: Check for changes - id: check_changes - shell: bash - run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' - if [[ `git status --porcelain` ]]; then - echo "changes detected" - echo "changes=true" >> $GITHUB_ENV - else - echo "no changes" - echo "changes=false" >> $GITHUB_ENV - fi - - - name: Commit results + - name: Commit and push changes if: env.changes == 'true' - run: | - git add . - git commit -m "Auto-update blog post date" - git push origin main - env: - GITHUB_TOKEN: ${{ secrets.PHARMAVERSE_BOT }} + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "[skip actions] Auto-update blog post date" + file_pattern: "." + commit_user_name: github-actions + commit_user_email: >- + 41898282+github-actions[bot]@users.noreply.github.com + continue-on-error: true From b602c706383763eeb9f1d58c3bb0f0958f3ecbf8 Mon Sep 17 00:00:00 2001 From: Edoardo Mancini Date: Wed, 16 Oct 2024 13:28:59 +0000 Subject: [PATCH 5/9] Update CICD dates --- .github/workflows/update_post_dates.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/update_post_dates.yml b/.github/workflows/update_post_dates.yml index 2bbf4cdf..d34359ef 100644 --- a/.github/workflows/update_post_dates.yml +++ b/.github/workflows/update_post_dates.yml @@ -24,7 +24,6 @@ jobs: run: git config --global --add safe.directory /__w/blog/blog - name: Commit and push changes - if: env.changes == 'true' uses: stefanzweifel/git-auto-commit-action@v5 with: commit_message: "[skip actions] Auto-update blog post date" From b7ae4a8422d37d1ea7b60dc885a9b691ccaf162c Mon Sep 17 00:00:00 2001 From: manciniedoardo Date: Wed, 16 Oct 2024 13:29:43 +0000 Subject: [PATCH 6/9] [skip actions] Auto-update blog post date --- .../appendix.R | 0 .../cache_execution.rds | Bin .../mirai_workflow.R | 0 .../mirai_workflow.log | 0 ...__reproducibility_vs.__parallelization.qmd | 24 ++++++++---------- 5 files changed, 10 insertions(+), 14 deletions(-) rename posts/{zzz_DO_NOT_EDIT_the__tensio... => 2024-10-16_the__tensio...}/appendix.R (100%) rename posts/{zzz_DO_NOT_EDIT_the__tensio... => 2024-10-16_the__tensio...}/cache_execution.rds (100%) rename posts/{zzz_DO_NOT_EDIT_the__tensio... => 2024-10-16_the__tensio...}/mirai_workflow.R (100%) rename posts/{zzz_DO_NOT_EDIT_the__tensio... => 2024-10-16_the__tensio...}/mirai_workflow.log (100%) rename posts/{zzz_DO_NOT_EDIT_the__tensio... => 2024-10-16_the__tensio...}/the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd (95%) diff --git a/posts/zzz_DO_NOT_EDIT_the__tensio.../appendix.R b/posts/2024-10-16_the__tensio.../appendix.R similarity index 100% rename from posts/zzz_DO_NOT_EDIT_the__tensio.../appendix.R rename to posts/2024-10-16_the__tensio.../appendix.R diff --git a/posts/zzz_DO_NOT_EDIT_the__tensio.../cache_execution.rds b/posts/2024-10-16_the__tensio.../cache_execution.rds similarity index 100% rename from posts/zzz_DO_NOT_EDIT_the__tensio.../cache_execution.rds rename to posts/2024-10-16_the__tensio.../cache_execution.rds diff --git a/posts/zzz_DO_NOT_EDIT_the__tensio.../mirai_workflow.R b/posts/2024-10-16_the__tensio.../mirai_workflow.R similarity index 100% rename from posts/zzz_DO_NOT_EDIT_the__tensio.../mirai_workflow.R rename to posts/2024-10-16_the__tensio.../mirai_workflow.R diff --git a/posts/zzz_DO_NOT_EDIT_the__tensio.../mirai_workflow.log b/posts/2024-10-16_the__tensio.../mirai_workflow.log similarity index 100% rename from posts/zzz_DO_NOT_EDIT_the__tensio.../mirai_workflow.log rename to posts/2024-10-16_the__tensio.../mirai_workflow.log diff --git a/posts/zzz_DO_NOT_EDIT_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd b/posts/2024-10-16_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd similarity index 95% rename from posts/zzz_DO_NOT_EDIT_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd rename to posts/2024-10-16_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd index be355758..9e12fbeb 100644 --- a/posts/zzz_DO_NOT_EDIT_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd +++ b/posts/2024-10-16_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd @@ -1,19 +1,15 @@ --- -title: "The Tension of High-Performance Computing: Reproducibility vs. Parallelization" +title: 'The Tension of High-Performance Computing: Reproducibility vs. Parallelization' author: - - name: Alexandros Kouretsis - - name: APPSILON -description: "Discover how to manage parallel processing and ensure - reproducibility in drug development using the {mirai} package and other HPC - tools." -# Note that the date below will be auto-updated when the post is merged. -date: "2024-12-01" -# Please do not use any non-default categories. -# You can find the default categories in the repository README.md -categories: [Submissions, Technical] -# Feel free to change the image -image: "pharmaverse.png" - +- name: Alexandros Kouretsis +- name: APPSILON +description: Discover how to manage parallel processing and ensure reproducibility + in drug development using the {mirai} package and other HPC tools. +date: '2024-10-16' +categories: +- Submissions +- Technical +image: pharmaverse.png --- From 50ee96c65e9de9feec40df2d3147cc4fe020bacf Mon Sep 17 00:00:00 2001 From: Edoardo Mancini <53403957+manciniedoardo@users.noreply.github.com> Date: Wed, 16 Oct 2024 14:13:29 +0000 Subject: [PATCH 7/9] Update publish.yml to add mirai --- .github/workflows/publish.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index d7563e15..c2022b66 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -51,6 +51,7 @@ jobs: teal riskmetric tidyCDISC + mirai - name: Install tinytex run: quarto install tool tinytex From 9e4c31b89e8cf809047a9c9acde7c996b8a40b7a Mon Sep 17 00:00:00 2001 From: Edoardo Mancini Date: Wed, 16 Oct 2024 15:24:55 +0000 Subject: [PATCH 8/9] Update cover image for mirai post --- ...rmance__computing:__reproducibility_vs.__parallelization.qmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posts/2024-10-16_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd b/posts/2024-10-16_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd index 9e12fbeb..1964a354 100644 --- a/posts/2024-10-16_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd +++ b/posts/2024-10-16_the__tensio.../the__tension_of__high-_performance__computing:__reproducibility_vs.__parallelization.qmd @@ -9,7 +9,7 @@ date: '2024-10-16' categories: - Submissions - Technical -image: pharmaverse.png +image: pharmaverse.PNG --- From e3b8418f6a36778876f950831494a69f8ee14dc7 Mon Sep 17 00:00:00 2001 From: Edoardo Mancini Date: Wed, 16 Oct 2024 15:51:43 +0000 Subject: [PATCH 9/9] Update cover image for Mirai post --- posts/2024-10-16_the__tensio.../pharmaverse.PNG | Bin 0 -> 20440 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 posts/2024-10-16_the__tensio.../pharmaverse.PNG diff --git a/posts/2024-10-16_the__tensio.../pharmaverse.PNG b/posts/2024-10-16_the__tensio.../pharmaverse.PNG new file mode 100644 index 0000000000000000000000000000000000000000..7ee40c66663af65701ffddeb9d0a21f7932056ef GIT binary patch literal 20440 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 literal 0 HcmV?d00001