From 16ecfc61463f8be71232ad276c7599c48289c0f6 Mon Sep 17 00:00:00 2001 From: fdobad <29801096+fdobad@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:38:09 -0300 Subject: [PATCH 1/8] remove boost:algorithm dependency --- Cell2Fire/ReadCSV.cpp | 31 ++++++++++++++++++++++++++----- Cell2Fire/ReadCSV.h | 1 - Cell2Fire/WriteCSV.cpp | 1 - Cell2Fire/WriteCSV.h | 1 - 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/Cell2Fire/ReadCSV.cpp b/Cell2Fire/ReadCSV.cpp index 8ede4cdf..245bfb32 100644 --- a/Cell2Fire/ReadCSV.cpp +++ b/Cell2Fire/ReadCSV.cpp @@ -5,7 +5,6 @@ #include "tiffio.h" #include -#include #include #include #include @@ -15,6 +14,30 @@ #include #include +// Local minimal replacement for boost::algorithm::split with is_any_of semantics. +// When compress == false, preserves empty tokens (equivalent to token_compress_off). +// When compress == true, collapses consecutive delimiters. +static std::vector +split_any_of(std::string_view s, std::string_view delims, bool compress) +{ + std::vector out; + const size_t n = s.size(); + size_t i = 0; + while (i <= n) { + size_t j = s.find_first_of(delims, i); + if (j == std::string_view::npos) j = n; + out.emplace_back(s.substr(i, j - i)); + if (j == n) break; + if (compress) { + i = j + 1; + while (i < n && delims.find(s[i]) != std::string_view::npos) ++i; + } else { + i = j + 1; + } + } + return out; +} + /** * Creates an instance of CSVReader. * @param filename name of file to read @@ -104,8 +127,7 @@ CSVReader::getData(string filename) } else { - std::vector vec; - boost::algorithm::split(vec, line, boost::is_any_of(this->delimeter)); + std::vector vec = split_any_of(line, this->delimeter, false); dataList.push_back(vec); } } @@ -206,8 +228,7 @@ CSVReader::getData(string filename) { while (getline(file, line)) { - std::vector vec; - boost::algorithm::split(vec, line, boost::is_any_of(this->delimeter)); + std::vector vec = split_any_of(line, this->delimeter, false); dataList.push_back(vec); } } diff --git a/Cell2Fire/ReadCSV.h b/Cell2Fire/ReadCSV.h index 7fe5ebe1..27c0a08a 100644 --- a/Cell2Fire/ReadCSV.h +++ b/Cell2Fire/ReadCSV.h @@ -7,7 +7,6 @@ #include "tiffio.h" #include -#include #include #include #include diff --git a/Cell2Fire/WriteCSV.cpp b/Cell2Fire/WriteCSV.cpp index 46b980e2..743a6286 100644 --- a/Cell2Fire/WriteCSV.cpp +++ b/Cell2Fire/WriteCSV.cpp @@ -1,7 +1,6 @@ #include "WriteCSV.h" #include -#include #include #include #include diff --git a/Cell2Fire/WriteCSV.h b/Cell2Fire/WriteCSV.h index 356ffd81..43f75512 100644 --- a/Cell2Fire/WriteCSV.h +++ b/Cell2Fire/WriteCSV.h @@ -2,7 +2,6 @@ #define WRITECSV #include -#include #include #include #include From ea097620e4800df08f56de5598ced5c23a80a43e Mon Sep 17 00:00:00 2001 From: fdobad <29801096+fdobad@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:38:27 -0300 Subject: [PATCH 2/8] proof --- test/tech/split_any_of_compare.Containerfile | 14 +++ test/tech/split_any_of_compare.cpp | 101 +++++++++++++++++++ test/tech/split_any_of_compare.md | 15 +++ 3 files changed, 130 insertions(+) create mode 100644 test/tech/split_any_of_compare.Containerfile create mode 100644 test/tech/split_any_of_compare.cpp create mode 100644 test/tech/split_any_of_compare.md diff --git a/test/tech/split_any_of_compare.Containerfile b/test/tech/split_any_of_compare.Containerfile new file mode 100644 index 00000000..d69ac7fc --- /dev/null +++ b/test/tech/split_any_of_compare.Containerfile @@ -0,0 +1,14 @@ +FROM debian:stable-slim + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + g++ \ + make \ + libboost-all-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /workspace + +COPY split_any_of_compare.cpp . + +CMD bash -c "g++ -std=c++17 -O2 split_any_of_compare.cpp -o split_any_of_compare && ./split_any_of_compare" diff --git a/test/tech/split_any_of_compare.cpp b/test/tech/split_any_of_compare.cpp new file mode 100644 index 00000000..ba1bfd1c --- /dev/null +++ b/test/tech/split_any_of_compare.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include + +#include + +static std::vector split_any_of(std::string_view s, + std::string_view delims, + bool compress) +{ + std::vector out; + const size_t n = s.size(); + size_t i = 0; + while (i <= n) { + size_t j = s.find_first_of(delims, i); + if (j == std::string_view::npos) j = n; + out.emplace_back(s.substr(i, j - i)); + if (j == n) break; + if (compress) { + i = j + 1; + while (i < n && delims.find(s[i]) != std::string_view::npos) ++i; + } else { + i = j + 1; + } + } + return out; +} + +static bool equal_vectors(const std::vector& a, + const std::vector& b) +{ + if (a.size() != b.size()) return false; + for (size_t i = 0; i < a.size(); ++i) { + if (a[i] != b[i]) return false; + } + return true; +} + +int main() +{ + struct Case { std::string line; std::string delims; bool compress; }; + std::vector cases = { + {"a,b,c", ",", false}, + {"a,,b,,,c", ",", false}, + {"a,,b,,,c", ",", true}, + {"a;b,c|d", ",;|", false}, + {"a;;b,,|c", ",;|", true}, + {"", ",;|", false}, + {",|;", ",;|", false}, + {",|;", ",;|", true}, + {" a b c ", " ", false}, + {" a b c ", " ", true}, + }; + + int failures = 0; + + for (const auto& cs : cases) { + // Boost split + std::vector boost_out; + if (cs.compress) { + boost::algorithm::split(boost_out, cs.line, + boost::is_any_of(cs.delims), + boost::token_compress_on); + } else { + boost::algorithm::split(boost_out, cs.line, + boost::is_any_of(cs.delims), + boost::token_compress_off); + } + + // std replacement + auto std_out = split_any_of(cs.line, cs.delims, cs.compress); + + const bool ok = equal_vectors(boost_out, std_out); + if (!ok) { + ++failures; + std::cout << "Mismatch for line='" << cs.line << "' delims='" << cs.delims + << "' compress=" << (cs.compress ? "on" : "off") << "\n"; + std::cout << " boost: ["; + for (size_t i = 0; i < boost_out.size(); ++i) { + if (i) std::cout << ", "; + std::cout << '"' << boost_out[i] << '"'; + } + std::cout << "]\n std : ["; + for (size_t i = 0; i < std_out.size(); ++i) { + if (i) std::cout << ", "; + std::cout << '"' << std_out[i] << '"'; + } + std::cout << "]\n"; + } + } + + if (failures == 0) { + std::cout << "All cases matched." << std::endl; + return 0; + } else { + std::cout << failures << " mismatches." << std::endl; + return 1; + } +} diff --git a/test/tech/split_any_of_compare.md b/test/tech/split_any_of_compare.md new file mode 100644 index 00000000..d4ff7709 --- /dev/null +++ b/test/tech/split_any_of_compare.md @@ -0,0 +1,15 @@ +# shrinking boost dependency proof + +`split_any_of_compare.cpp` implements a comparison of boost::split_any_of with a custom split_any_of function. +The code tests both functions on the same input and compares their outputs to ensure they behave identically. +Then we can remove boost::algorithm from our codebase with confidence, lightening our dependencies and build times. + +To run in isolation: + + podman build -f split_any_of_compare.Containerfile -t fire2a-split-any-of-test . + podman run --rm fire2a-split-any-of-test + +Not isolated: + + g++ -std=c++17 -I./include split_any_of_compare.cpp -o split_any_of_compare + ./split_any_of_compare From 6a72e3604a17645c85157fefa944c59a8909affd Mon Sep 17 00:00:00 2001 From: fdobad <29801096+fdobad@users.noreply.github.com> Date: Fri, 19 Dec 2025 16:38:09 -0300 Subject: [PATCH 3/8] string_view->string & #include cstring --- Cell2Fire/ReadCSV.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Cell2Fire/ReadCSV.cpp b/Cell2Fire/ReadCSV.cpp index 245bfb32..98bee05b 100644 --- a/Cell2Fire/ReadCSV.cpp +++ b/Cell2Fire/ReadCSV.cpp @@ -9,28 +9,30 @@ #include #include #include +#include #include #include #include #include // Local minimal replacement for boost::algorithm::split with is_any_of semantics. +// C++14-friendly (uses std::string, not std::string_view). // When compress == false, preserves empty tokens (equivalent to token_compress_off). // When compress == true, collapses consecutive delimiters. static std::vector -split_any_of(std::string_view s, std::string_view delims, bool compress) +split_any_of(const std::string& s, const std::string& delims, bool compress) { std::vector out; const size_t n = s.size(); size_t i = 0; while (i <= n) { size_t j = s.find_first_of(delims, i); - if (j == std::string_view::npos) j = n; + if (j == std::string::npos) j = n; out.emplace_back(s.substr(i, j - i)); if (j == n) break; if (compress) { i = j + 1; - while (i < n && delims.find(s[i]) != std::string_view::npos) ++i; + while (i < n && delims.find(s[i]) != std::string::npos) ++i; } else { i = j + 1; } From 214621f61f1785420a2202b660755f23acf9541f Mon Sep 17 00:00:00 2001 From: fdobad <29801096+fdobad@users.noreply.github.com> Date: Fri, 19 Dec 2025 20:17:12 -0300 Subject: [PATCH 4/8] update containers build --- .containerignore | 8 ++++ .github/workflows/ci.yml | 2 +- README.md | 30 +++++++-------- container/Containerfile | 21 +++++++++++ container/Containerfile+tests | 28 ++++++++++++++ container/Dockerfile | 2 +- container/README.md | 71 +++++++++++++++++++++++++++-------- 7 files changed, 128 insertions(+), 34 deletions(-) create mode 100644 .containerignore create mode 100644 container/Containerfile create mode 100644 container/Containerfile+tests diff --git a/.containerignore b/.containerignore new file mode 100644 index 00000000..f1ec957e --- /dev/null +++ b/.containerignore @@ -0,0 +1,8 @@ +* +!Cell2Fire/*.cpp +!Cell2Fire/*.h +!Cell2Fire/makefile +!test/unit_tests/*.cpp +!test/model +!test/target_results.zip +!test/test.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cb5cbe3..310472a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: - name: Install Dependencies run: | sudo apt-get update - sudo apt-get install -y g++ make libboost-dev libtiff-dev catch2 + sudo apt-get install -y g++ make libboost-random-dev libtiff-dev catch2 - name: Build run: | diff --git a/README.md b/README.md index e61ceb36..0bb67b8f 100644 --- a/README.md +++ b/README.md @@ -105,34 +105,30 @@ Cell2Fire --final-grid --output-messages --out-ros --sim S --nsims 2 --seed 123 # check the results: to convert to tiff or see the results in QGIS, use the plugin ``` -### Get the container - -![Tutorial here](container/README.md), TL;DR: - +### Containerized +TL;DR: ```bash -# have or install podman (or docker) -sudo apt install podman - -# download the container [Dockerfile](https://github.com/fire2a/C2F-W/raw/refs/heads/feature-containerize/container/Dockerfile) -wget https://github.com/fire2a/C2F-W/raw/refs/heads/feature-containerize/container/Dockerfile - -# build -podman build -t c2f -f Dockerfile . - -# Done! Usage mounting the instance and results directories into the container +podman build -t cell2fire -f container/Containerfile . mkdir results -podman run -v $(pwd):/mnt c2f --input-instance-folder /mnt/data/Kitral/Portillo-tif --output-folder /mnt/results --nsims 3 --sim K --grids | tee results/log.txt +podman run -v $(pwd):/mnt cell2fire \ + --input-instance-folder /mnt/data/ScottAndBurgan/Vilopriu_2013-tif \ + --output-folder /mnt/results \ + --nsims 3 --sim S \ + --output-messages --ignitionsLog | tee results/log.txt +rm -r results/* ``` +[More options and tutorial here](container/README.md) + ## Collaborative Compile it ```bash # dependencies -sudo apt install g++-12 libboost-all-dev libeigen3-dev libtiff-dev +sudo apt install g++ libboost-random-dev libtiff-dev # or brew -brew install gcc@12 libomp eigen boost libtiff # llvm ? +brew install gcc@12 libomp boost libtiff # llvm ? # fork & clone git clone git@github.com:/C2F-W.git diff --git a/container/Containerfile b/container/Containerfile new file mode 100644 index 00000000..0c892c31 --- /dev/null +++ b/container/Containerfile @@ -0,0 +1,21 @@ +FROM debian:stable-slim + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + g++ \ + libboost-random-dev \ + libtiff-dev \ + make && \ + rm -rf /var/lib/apt/lists/* + +COPY Cell2Fire Cell2Fire + +WORKDIR /Cell2Fire + +RUN make clean && \ + make + +ENTRYPOINT ["/Cell2Fire/Cell2Fire"] + diff --git a/container/Containerfile+tests b/container/Containerfile+tests new file mode 100644 index 00000000..b6a772dd --- /dev/null +++ b/container/Containerfile+tests @@ -0,0 +1,28 @@ +FROM debian:stable-slim + +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + catch2 \ + g++ \ + libboost-random-dev \ + libtiff-dev \ + make \ + unzip && \ + rm -rf /var/lib/apt/lists/* + +COPY Cell2Fire Cell2Fire + +COPY test /test + +WORKDIR /Cell2Fire + +RUN make clean && \ + make tests && \ + make + +RUN cd /test && ./test.sh + +ENTRYPOINT ["/Cell2Fire/Cell2Fire"] + diff --git a/container/Dockerfile b/container/Dockerfile index 95addc43..bc5ff3ec 100644 --- a/container/Dockerfile +++ b/container/Dockerfile @@ -2,7 +2,7 @@ FROM debian:stable LABEL authors="matilde" ENV DEBIAN_FRONTEND=noninteractive -RUN apt-get update && apt-get install -y g++-12 libboost-all-dev libeigen3-dev libtiff-dev git make unzip lsb-release catch2 +RUN apt-get update && apt-get install -y g++ libboost-random-dev libtiff-dev git make unzip catch2 RUN git clone https://github.com/fire2a/C2F-W.git diff --git a/container/README.md b/container/README.md index 2d09fc84..6f85892f 100644 --- a/container/README.md +++ b/container/README.md @@ -1,31 +1,72 @@ -This folder contains a containerized version of Cell2Fire. The container runs the latest version of Cell2Fire without having to manually build and configure the application on your system. The container is compatible with both Podman and Docker. We recommend using Podman, but the instructions found here are easily translated into Docker. +# Cell2Fire Containers -## TL;DR +This folder contains a containerized versions of Cell2Fire. The containers runs the latest version of Cell2Fire without having to manually build and configure the application on your system. + + +### Requirement +You only need Podman installed on your system to build and run the container. Replace `podman` with `docker` if needed. +```bash +sudo apt install podman ``` -sudo apt install podman git -git clone git@github.com:Cell2Fire/C2F-W.git -cd C2F-W/container -podman build -t c2f . -cd ../test + +## Usage +After building the container image (named `cell2fire` in the next example), you can run Cell2Fire simulations using the container. The container accepts the same parameters as if you were running the compiled binary directly. + +The simplest use is by mounting the instance and results directories into the container using volumes (`-v $(pwd):/mnt`) and deleting the container after the run (`--rm`). +```bash mkdir results -~/C2F-W/test$ podman run -v $(pwd):/mnt c2f --input-instance-folder /mnt/model/fbp-asc --output-folder /mnt/results --nsims 3 --sim C --grids +podman run --rm -v $(pwd):/mnt cell2fire \ + --input-instance-folder /mnt/data/Kitral/Portezuelo-tif \ + --output-folder /mnt/results \ + --output-messages \ + --ignitionsLog \ + --nsims 3 --sim K --grids | tee results/log.txt ls results +rm -r results/* +``` + +## Select your preferred build -# unnecesary if correctly addressing the output folder: -# enter the container interactively, overriding the entrypoint: -podman run -it --entrypoint /bin/bash c2f +### 1. Lightweight +``` +git clone git@github.com:Cell2Fire/C2F-W.git +cd C2F-W +podman build -t cell2fire -f container/Containerfile . ``` -## Folder Structure +### 2. Run tests too +``` +git clone git@github.com:Cell2Fire/C2F-W.git +cd C2F-W +podman build -t cell2firetests -f container/Containerfile+tests . +``` + +### 3. Minimal dependencies but heavier +Also wget and unzip is needed to get the Dockerfile and a test instance. +```bash +# get the Dockerfile +wget https://github.com/fire2a/C2F-W/raw/main/container/Dockerfile + +# build +podman build -t cell2fire -f Dockerfile . + +# get an instance +wget https://github.com/fire2a/C2F-W/releases/download/v1.0.1/Kitral-tif.zip +unzip Kitral-tif.zip -d data +``` + +## Learn More + +### Folder Structure - `container/`: Contains the files used to build and run the container. - `Dockerfile`: Defines the build instructions for the container image. -## Prerequisites +### Prerequisites Ensure you have Podman installed on your system. You can find installation instructions in the [official Podman documentation](https://podman.io/docs/installation). -## Building the Image +### Building the Image To build the container image using Podman, navigate to the `container/` directory and run the following command: @@ -35,7 +76,7 @@ podman build -t -f Dockerfile . ``` This command builds the container image and tags it as . -## Running Cell2Fire +### Running Cell2Fire Once the image is built, you can run the simulation using Podman. It accepts the same parameters as if you were running the compiled binary directly. In order for the container to have access to the input data files, we must use volumes. Simply put the `-v` or `--volume` tag followed by the path to your input files From 7247de67bedd0b876c67ccb5db7c1267b9ad3c25 Mon Sep 17 00:00:00 2001 From: fdobad <29801096+fdobad@users.noreply.github.com> Date: Tue, 2 Dec 2025 17:56:50 -0300 Subject: [PATCH 5/8] boost-random cross-os test/proof --- .github/workflows/mt19937_boost.yml | 101 ++++++++++++++++++++++++++++ .github/workflows/mt19937_std.yml | 88 ++++++++++++++++++++++++ test/tech/mt19937.md | 32 +++++++++ test/tech/mt19937_boost.cpp | 30 +++++++++ test/tech/mt19937_std.cpp | 30 +++++++++ 5 files changed, 281 insertions(+) create mode 100644 .github/workflows/mt19937_boost.yml create mode 100644 .github/workflows/mt19937_std.yml create mode 100644 test/tech/mt19937.md create mode 100644 test/tech/mt19937_boost.cpp create mode 100644 test/tech/mt19937_std.cpp diff --git a/.github/workflows/mt19937_boost.yml b/.github/workflows/mt19937_boost.yml new file mode 100644 index 00000000..7379b862 --- /dev/null +++ b/.github/workflows/mt19937_boost.yml @@ -0,0 +1,101 @@ +name: mt19937 boost cross-os congruence +on: + workflow_dispatch: +jobs: + build-and-run: + name: Build and run Boost emitter (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + # no job-level env needed + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies (Ubuntu) + if: startsWith(matrix.os, 'ubuntu') + run: | + sudo apt-get update + sudo apt-get install -y build-essential libboost-random-dev #clang + shell: bash + + - name: Install dependencies (macOS) + if: startsWith(matrix.os, 'macos') + run: | + brew update + brew install llvm + # Homebrew provides Boost as a single formula; we'll use headers/libs from it + brew install boost + echo "Using clang++ from Homebrew" + echo "CXX=$(brew --prefix)/opt/llvm/bin/clang++" >> $GITHUB_ENV + + - name: Set up Visual Studio shell + if: startsWith(matrix.os, 'windows') + uses: egor-tensin/vs-shell@v2 + with: + arch: x64 + + - name: Set up Vcpkg (Windows) + if: startsWith(matrix.os, 'windows') + shell: powershell + run: | + # Use preinstalled vcpkg from runner images + & "$env:VCPKG_INSTALLATION_ROOT\vcpkg.exe" install boost-random:x64-windows + Write-Host "VCPKG include: $env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\include" + "$env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\include" | Out-File -FilePath include_path.txt -Encoding ascii + + - name: Build (Ubuntu) + if: startsWith(matrix.os, 'ubuntu') + run: | + c++ -std=c++17 test/tech/mt19937_boost.cpp -o mt19937_boost + + - name: Build (macOS) + if: startsWith(matrix.os, 'macos') + run: | + "$CXX" -std=c++17 -I"$(brew --prefix)/include" -L"$(brew --prefix)/lib" test/tech/mt19937_boost.cpp -o mt19937_boost + + - name: Build (Windows) + if: startsWith(matrix.os, 'windows') + shell: powershell + run: | + # Add include path and compile + $include = Join-Path $env:VCPKG_INSTALLATION_ROOT 'installed\x64-windows\include' + $env:INCLUDE = "$include;$env:INCLUDE" + cmd /c "cl /std:c++17 /EHsc /I `"$include`" test\tech\mt19937_boost.cpp /Fe:mt19937_boost.exe" + + - name: Run emitter + shell: bash + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + ./mt19937_boost.exe > mt19937_${{ runner.os }}.txt + else + ./mt19937_boost > mt19937_${{ runner.os }}.txt + fi + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: mt19937_${{ runner.os }} + path: mt19937_${{ runner.os }}.txt + + compare: + name: Compare outputs (${{ runner.os }}) + runs-on: ubuntu-latest + needs: build-and-run + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Diff Ubuntu vs macOS + run: | + diff -u artifacts/mt19937_Linux/mt19937_Linux.txt artifacts/mt19937_macOS/mt19937_macOS.txt + + - name: Diff Ubuntu vs Windows (normalize CRLF) + run: | + sudo apt-get install -y dos2unix + dos2unix artifacts/mt19937_Windows/mt19937_Windows.txt + diff -u artifacts/mt19937_Windows/mt19937_Windows.txt artifacts/mt19937_Linux/mt19937_Linux.txt diff --git a/.github/workflows/mt19937_std.yml b/.github/workflows/mt19937_std.yml new file mode 100644 index 00000000..82983a5d --- /dev/null +++ b/.github/workflows/mt19937_std.yml @@ -0,0 +1,88 @@ +name: mt19937 std cross-os congruence +on: + workflow_dispatch: +jobs: + build-and-run: + name: Build and run Boost emitter (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + # no job-level env needed + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup MSBuild (Windows) + if: startsWith(matrix.os, 'windows') + uses: microsoft/setup-msbuild@v2 + + - name: Ensure compiler (Ubuntu) + if: startsWith(matrix.os, 'ubuntu') + run: | + sudo apt-get update + sudo apt-get install -y build-essential + shell: bash + + - name: Ensure compiler (macOS) + if: startsWith(matrix.os, 'macos') + run: | + xcode-select --install || true + shell: bash + + - name: Build (Linux/macOS) + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') + run: | + c++ -std=c++17 test/tech/mt19937_std.cpp -o mt19937_std + shell: bash + + - name: Set up Visual Studio shell + if: startsWith(matrix.os, 'windows') + uses: egor-tensin/vs-shell@v2 + with: + arch: x64 + + - name: Build (Windows) + if: startsWith(matrix.os, 'windows') + run: | + cl /std:c++17 test\tech\mt19937_std.cpp /Fe:mt19937_std.exe + shell: pwsh + + - name: Run and capture (Linux/macOS) + if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') + run: | + ./mt19937_std > mt19937_${{ runner.os }}.txt + shell: bash + + - name: Run and capture (Windows) + if: startsWith(matrix.os, 'windows') + run: | + .\mt19937_std.exe | Out-File -FilePath mt19937_${{ runner.os }}.txt -Encoding ascii + shell: pwsh + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: mt19937_${{ runner.os }} + path: mt19937_${{ runner.os }}.txt + + compare: + name: Compare outputs (${{ runner.os }}) + runs-on: ubuntu-latest + needs: build-and-run + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + + - name: Diff Ubuntu vs macOS + run: | + diff -u artifacts/mt19937_Linux/mt19937_Linux.txt artifacts/mt19937_macOS/mt19937_macOS.txt + + - name: Diff Ubuntu vs Windows (normalize CRLF) + run: | + sudo apt-get install -y dos2unix + dos2unix artifacts/mt19937_Windows/mt19937_Windows.txt + diff -u artifacts/mt19937_Windows/mt19937_Windows.txt artifacts/mt19937_Linux/mt19937_Linux.txt diff --git a/test/tech/mt19937.md b/test/tech/mt19937.md new file mode 100644 index 00000000..42e4b984 --- /dev/null +++ b/test/tech/mt19937.md @@ -0,0 +1,32 @@ +## mt19937 random number generator + +The purpose of these tests is to assure that random generated numbers are cross platform consistent. + +Two tests are included, running the same distributions used in Cell2Fire, but changing the origin of the random library. + +One uses boost::random the other std::random. + +These tests can be manually run on + +- https://github.com/fdobad/C2F-W/actions/workflows/mt19937_std.yml + +- https://github.com/fdobad/C2F-W/actions/workflows/mt19937_boost.yml + +as they have a workflow_dispatch directive. + +### example of running locally + +```bash +cd test/tech +g++ -std=c++11 -O2 mt19937_std.cpp -o mt19937_std +./mt19937_std | tee mt19937_std.txt +g++ -std=c++11 -O2 mt19937_boost.cpp -o mt19937_boost -lboost_random -lboost_system +./mt19937_boost | tee mt19937_boost.txt +diff mt19937_std.txt mt19937_boost.txt # not expected to be the same!! +``` + +## Results + +As of 2025-12-02 Only boost is cross platform consistent! + +When std::random get's consistent then boost dependency can be dropped easing the building times and dependencies! diff --git a/test/tech/mt19937_boost.cpp b/test/tech/mt19937_boost.cpp new file mode 100644 index 00000000..9a24c976 --- /dev/null +++ b/test/tech/mt19937_boost.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +int +main() +{ + const uint32_t seed = 123456789u; + boost::random::mt19937 eng(seed); + + boost::random::uniform_int_distribution udist(1, 1000000); + boost::random::normal_distribution ndist(0.0, 1.0); + + std::cout << "# boost::mt19937 uniform_int [1,1000000]" << std::endl; + for (int i = 0; i < 1000; ++i) + { + std::cout << udist(eng) << '\n'; + } + + // Reset engine for normal distribution to ensure same sequence base + eng.seed(seed); + std::cout << "# boost::mt19937 normal mean=0 stddev=1" << std::endl; + std::cout << std::setprecision(17); + for (int i = 0; i < 1000; ++i) + { + std::cout << ndist(eng) << '\n'; + } + + return 0; +} diff --git a/test/tech/mt19937_std.cpp b/test/tech/mt19937_std.cpp new file mode 100644 index 00000000..fedb2aaa --- /dev/null +++ b/test/tech/mt19937_std.cpp @@ -0,0 +1,30 @@ +#include +#include +#include + +int +main() +{ + const uint32_t seed = 123456789u; + std::mt19937 eng(seed); + + std::uniform_int_distribution udist(1, 1000000); + std::normal_distribution ndist(0.0, 1.0); + + std::cout << "# std::mt19937 uniform_int [1,1000000]" << std::endl; + for (int i = 0; i < 1000; ++i) + { + std::cout << udist(eng) << '\n'; + } + + // Reset engine for normal distribution to ensure same sequence base + eng.seed(seed); + std::cout << "# std::mt19937 normal mean=0 stddev=1" << std::endl; + std::cout << std::setprecision(17); + for (int i = 0; i < 1000; ++i) + { + std::cout << ndist(eng) << '\n'; + } + + return 0; +} From 91afb9125364fbf0d476a3ad90e1e9f4df7edae5 Mon Sep 17 00:00:00 2001 From: fdo Date: Wed, 24 Dec 2025 00:07:41 -0300 Subject: [PATCH 6/8] boost Rename workflow and update job names --- .github/workflows/mt19937_boost.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/mt19937_boost.yml b/.github/workflows/mt19937_boost.yml index 7379b862..81668f59 100644 --- a/.github/workflows/mt19937_boost.yml +++ b/.github/workflows/mt19937_boost.yml @@ -1,9 +1,9 @@ -name: mt19937 boost cross-os congruence +name: rng mt19937 boost cross-os congruence on: workflow_dispatch: jobs: build-and-run: - name: Build and run Boost emitter (${{ matrix.os }}) + name: Build and run Boost (${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -17,7 +17,7 @@ jobs: - name: Install dependencies (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: | - sudo apt-get update + sudo apt-get update -q sudo apt-get install -y build-essential libboost-random-dev #clang shell: bash @@ -81,7 +81,7 @@ jobs: path: mt19937_${{ runner.os }}.txt compare: - name: Compare outputs (${{ runner.os }}) + name: Compare outputs runs-on: ubuntu-latest needs: build-and-run steps: From e03d6789508ba2eae7d547832414067aca4caa16 Mon Sep 17 00:00:00 2001 From: fdo Date: Wed, 24 Dec 2025 00:08:58 -0300 Subject: [PATCH 7/8] std Rename workflow and update output comparison name --- .github/workflows/mt19937_std.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/mt19937_std.yml b/.github/workflows/mt19937_std.yml index 82983a5d..fc966539 100644 --- a/.github/workflows/mt19937_std.yml +++ b/.github/workflows/mt19937_std.yml @@ -1,4 +1,4 @@ -name: mt19937 std cross-os congruence +name: rng mt19937 std cross-os congruence on: workflow_dispatch: jobs: @@ -21,7 +21,7 @@ jobs: - name: Ensure compiler (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: | - sudo apt-get update + sudo apt-get update -q sudo apt-get install -y build-essential shell: bash @@ -68,7 +68,7 @@ jobs: path: mt19937_${{ runner.os }}.txt compare: - name: Compare outputs (${{ runner.os }}) + name: Compare outputs runs-on: ubuntu-latest needs: build-and-run steps: From f80024fbebce991d6de4b0629bc559c37b4067be Mon Sep 17 00:00:00 2001 From: fdobad <29801096+fdobad@users.noreply.github.com> Date: Wed, 24 Dec 2025 00:42:25 -0300 Subject: [PATCH 8/8] rng workflow fix --- .github/workflows/mt19937_boost.yml | 44 +++++++++++------------------ .github/workflows/mt19937_std.yml | 29 +++++++------------ 2 files changed, 26 insertions(+), 47 deletions(-) diff --git a/.github/workflows/mt19937_boost.yml b/.github/workflows/mt19937_boost.yml index 81668f59..356a8170 100644 --- a/.github/workflows/mt19937_boost.yml +++ b/.github/workflows/mt19937_boost.yml @@ -1,42 +1,38 @@ -name: rng mt19937 boost cross-os congruence +--- +name: rng boost::mt19937 cross-os congruence on: workflow_dispatch: jobs: build-and-run: - name: Build and run Boost (${{ matrix.os }}) + name: Build and run (${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - # no job-level env needed + os: + - ubuntu-latest + - macos-latest + - windows-latest steps: - name: Checkout uses: actions/checkout@v4 - - name: Install dependencies (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get update -q - sudo apt-get install -y build-essential libboost-random-dev #clang - shell: bash - + sudo apt-get install -y build-essential libboost-random-dev - name: Install dependencies (macOS) if: startsWith(matrix.os, 'macos') run: | brew update - brew install llvm - # Homebrew provides Boost as a single formula; we'll use headers/libs from it - brew install boost + brew install llvm boost echo "Using clang++ from Homebrew" echo "CXX=$(brew --prefix)/opt/llvm/bin/clang++" >> $GITHUB_ENV - - name: Set up Visual Studio shell if: startsWith(matrix.os, 'windows') uses: egor-tensin/vs-shell@v2 with: - arch: x64 - + arch: x64 - name: Set up Vcpkg (Windows) if: startsWith(matrix.os, 'windows') shell: powershell @@ -45,26 +41,22 @@ jobs: & "$env:VCPKG_INSTALLATION_ROOT\vcpkg.exe" install boost-random:x64-windows Write-Host "VCPKG include: $env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\include" "$env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\include" | Out-File -FilePath include_path.txt -Encoding ascii - - name: Build (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: | c++ -std=c++17 test/tech/mt19937_boost.cpp -o mt19937_boost - - name: Build (macOS) if: startsWith(matrix.os, 'macos') run: | "$CXX" -std=c++17 -I"$(brew --prefix)/include" -L"$(brew --prefix)/lib" test/tech/mt19937_boost.cpp -o mt19937_boost - - name: Build (Windows) if: startsWith(matrix.os, 'windows') shell: powershell run: | - # Add include path and compile - $include = Join-Path $env:VCPKG_INSTALLATION_ROOT 'installed\x64-windows\include' - $env:INCLUDE = "$include;$env:INCLUDE" - cmd /c "cl /std:c++17 /EHsc /I `"$include`" test\tech\mt19937_boost.cpp /Fe:mt19937_boost.exe" - + # Add include path and compile + $include = Join-Path $env:VCPKG_INSTALLATION_ROOT 'installed\x64-windows\include' + $env:INCLUDE = "$include;$env:INCLUDE" + cmd /c "cl /std:c++17 /EHsc /I `"$include`" test\tech\mt19937_boost.cpp /Fe:mt19937_boost.exe" - name: Run emitter shell: bash run: | @@ -73,13 +65,11 @@ jobs: else ./mt19937_boost > mt19937_${{ runner.os }}.txt fi - - name: Upload artifact uses: actions/upload-artifact@v4 with: name: mt19937_${{ runner.os }} path: mt19937_${{ runner.os }}.txt - compare: name: Compare outputs runs-on: ubuntu-latest @@ -89,13 +79,11 @@ jobs: uses: actions/download-artifact@v4 with: path: artifacts - - name: Diff Ubuntu vs macOS run: | diff -u artifacts/mt19937_Linux/mt19937_Linux.txt artifacts/mt19937_macOS/mt19937_macOS.txt - - name: Diff Ubuntu vs Windows (normalize CRLF) - run: | + run: |- sudo apt-get install -y dos2unix dos2unix artifacts/mt19937_Windows/mt19937_Windows.txt - diff -u artifacts/mt19937_Windows/mt19937_Windows.txt artifacts/mt19937_Linux/mt19937_Linux.txt + diff -u artifacts/mt19937_Windows/mt19937_Windows.txt artifacts/mt19937_Linux/mt19937_Linux.txt diff --git a/.github/workflows/mt19937_std.yml b/.github/workflows/mt19937_std.yml index fc966539..f15d934d 100644 --- a/.github/workflows/mt19937_std.yml +++ b/.github/workflows/mt19937_std.yml @@ -1,72 +1,65 @@ -name: rng mt19937 std cross-os congruence +--- +name: rng std::mt19937 cross-os congruence on: workflow_dispatch: jobs: build-and-run: - name: Build and run Boost emitter (${{ matrix.os }}) + name: Build and run (${{ matrix.os }}) runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - # no job-level env needed + os: + - ubuntu-latest + - macos-latest + - windows-latest steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup MSBuild (Windows) if: startsWith(matrix.os, 'windows') uses: microsoft/setup-msbuild@v2 - - name: Ensure compiler (Ubuntu) if: startsWith(matrix.os, 'ubuntu') run: | sudo apt-get update -q sudo apt-get install -y build-essential shell: bash - - name: Ensure compiler (macOS) if: startsWith(matrix.os, 'macos') run: | xcode-select --install || true shell: bash - - name: Build (Linux/macOS) if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') run: | c++ -std=c++17 test/tech/mt19937_std.cpp -o mt19937_std shell: bash - - name: Set up Visual Studio shell if: startsWith(matrix.os, 'windows') uses: egor-tensin/vs-shell@v2 with: - arch: x64 - + arch: x64 - name: Build (Windows) if: startsWith(matrix.os, 'windows') run: | cl /std:c++17 test\tech\mt19937_std.cpp /Fe:mt19937_std.exe shell: pwsh - - name: Run and capture (Linux/macOS) if: startsWith(matrix.os, 'ubuntu') || startsWith(matrix.os, 'macos') run: | ./mt19937_std > mt19937_${{ runner.os }}.txt shell: bash - - name: Run and capture (Windows) if: startsWith(matrix.os, 'windows') run: | .\mt19937_std.exe | Out-File -FilePath mt19937_${{ runner.os }}.txt -Encoding ascii shell: pwsh - - name: Upload artifact uses: actions/upload-artifact@v4 with: name: mt19937_${{ runner.os }} path: mt19937_${{ runner.os }}.txt - compare: name: Compare outputs runs-on: ubuntu-latest @@ -76,13 +69,11 @@ jobs: uses: actions/download-artifact@v4 with: path: artifacts - - name: Diff Ubuntu vs macOS run: | diff -u artifacts/mt19937_Linux/mt19937_Linux.txt artifacts/mt19937_macOS/mt19937_macOS.txt - - name: Diff Ubuntu vs Windows (normalize CRLF) - run: | + run: |- sudo apt-get install -y dos2unix dos2unix artifacts/mt19937_Windows/mt19937_Windows.txt - diff -u artifacts/mt19937_Windows/mt19937_Windows.txt artifacts/mt19937_Linux/mt19937_Linux.txt + diff -u artifacts/mt19937_Windows/mt19937_Windows.txt artifacts/mt19937_Linux/mt19937_Linux.txt