Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
16 changes: 9 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ tracing-subscriber = { version = "0.3.19", features = [
"time",
] }
faer = "0.24.0"
pharmsol = { version = "=0.28.8" }
pharmsol = { version = "=0.29.0" }
anyhow = "1.0.100"
rayon = "1.10.0"
rand = "0.10.1"
Expand All @@ -37,10 +37,7 @@ rand = "0.10.1"
default = []
json = []
exa = []
dsl-core = ["pharmsol/dsl-core"]
dsl-jit = ["pharmsol/dsl-jit"]
dsl-aot = ["pharmsol/dsl-aot"]
dsl-aot-load = ["pharmsol/dsl-aot-load"]
dsl = ["pharmsol/dsl"]

[profile.release]
codegen-units = 1
Expand All @@ -59,8 +56,13 @@ name = "bimodal_ke"
harness = false

# The BestDose example defines its model from a DSL text string, which requires
# a runtime DSL backend. Build/run it with: `--features dsl-jit`.
# a runtime DSL backend. Build/run it with: `--features dsl`.
[[example]]
name = "bestdose"
path = "examples/bestdose/main.rs"
required-features = ["dsl-jit"]
required-features = ["dsl"]

[[example]]
name = "bimodal_ke_backend_compare"
path = "examples/bimodal_ke_backend_compare.rs"
required-features = ["dsl"]
5 changes: 1 addition & 4 deletions examples/bestdose/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use anyhow::Result;
use pharmsol::dsl::{
compile_module_source_to_runtime, CompiledRuntimeModel, RuntimeCompilationTarget,
};
use pharmsol::dsl::{compile_module_source_to_runtime, CompiledRuntimeModel};
use pharmsol::Equation;
use pmcore::bestdose::{BestDoseOptions, BestDoseProblem, DoseRange, Target};
use pmcore::prelude::*;
Expand Down Expand Up @@ -34,7 +32,6 @@ fn main() -> Result<()> {
let eq = match compile_module_source_to_runtime(
MODEL_SOURCE,
Some("bestdose_one_compartment"),
RuntimeCompilationTarget::Jit,
|_, _| {},
)
.map_err(|e| anyhow::anyhow!("failed to compile DSL model: {e}"))?
Expand Down
168 changes: 17 additions & 151 deletions examples/bimodal_ke_backend_compare.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,12 @@
use std::time::{Duration, Instant};

use anyhow::{anyhow, Result};
use pharmsol::dsl::{compile_module_source_to_runtime, CompiledRuntimeModel, RuntimeOdeModel};
use pharmsol::Cache;
use pmcore::prelude::*;

#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
))]
use anyhow::{bail, Context};
#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
))]
use pharmsol::dsl::{self, CompiledRuntimeModel, RuntimeBackend, RuntimeCompilationTarget};
#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
))]
use std::fs;
#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
))]
use std::path::PathBuf;
#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
))]
use std::time::{SystemTime, UNIX_EPOCH};

const DATA_PATH: &str = "examples/bimodal_ke/bimodal_ke.csv";
#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
))]

const BIMODAL_KE_DSL: &str = r#"
name = bimodal_ke
kind = ode
Expand All @@ -66,18 +32,7 @@ struct ComparisonResult {
}

fn main() -> Result<()> {
let mut results = Vec::new();
results.push(run_legacy()?);
results.push(run_macro()?);

#[cfg(feature = "dsl-jit")]
results.push(run_runtime_jit()?);

#[cfg(all(feature = "dsl-aot", feature = "dsl-aot-load"))]
results.push(run_runtime_native_aot()?);

#[cfg(feature = "dsl-wasm")]
results.push(run_runtime_wasm()?);
let results = vec![run_legacy()?, run_macro()?, run_dsl()?];

print_summary(&results)?;

Expand Down Expand Up @@ -137,62 +92,16 @@ fn macro_equation() -> equation::ODE {
.with_solver(OdeSolver::ExplicitRk(ExplicitRkTableau::Tsit45))
}

#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
))]
fn artifact_root(name: &str) -> Result<PathBuf> {
let stamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.context("system clock is before unix epoch")?
.as_millis();
let path = std::env::current_dir()?
.join("target")
.join("bimodal_ke_backend_compare")
.join(format!("{name}-{stamp}-{}", std::process::id()));
fs::create_dir_all(&path)?;
Ok(path)
}

#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
))]
fn compile_runtime_ode(
label: &'static str,
expected_backend: RuntimeBackend,
target: RuntimeCompilationTarget,
) -> Result<(pharmsol::dsl::RuntimeOdeModel, Duration)> {
let started = Instant::now();
let compiled = dsl::compile_module_source_to_runtime(
BIMODAL_KE_DSL,
Some("bimodal_ke"),
target,
|_, _| {},
)?;
let compile_time = started.elapsed();
fn dsl_equation() -> Result<RuntimeOdeModel> {
let compiled = compile_module_source_to_runtime(BIMODAL_KE_DSL, Some("bimodal_ke"), |_, _| {})
.map_err(|e| anyhow!("failed to compile DSL model: {e}"))?;

let model = match compiled {
CompiledRuntimeModel::Ode(model) => model
match compiled {
CompiledRuntimeModel::Ode(model) => Ok(model
.enable_cache()
.with_solver(OdeSolver::ExplicitRk(ExplicitRkTableau::Tsit45)),
CompiledRuntimeModel::Analytical(_) => {
bail!("{label} compiled bimodal_ke as analytical instead of ode")
}
CompiledRuntimeModel::Sde(_) => bail!("{label} compiled bimodal_ke as sde instead of ode"),
};

if model.backend() != expected_backend {
bail!(
"{label} compiled to {:?}, expected {:?}",
model.backend(),
expected_backend
);
.with_solver(OdeSolver::ExplicitRk(ExplicitRkTableau::Tsit45))),
other => Err(anyhow!("expected an ODE model, got {:?}", other.kind())),
}

Ok((model, compile_time))
}

fn run_case<E: pharmsol::Equation + Clone + Send + 'static + EquationMetadataSource>(
Expand All @@ -214,15 +123,6 @@ fn run_case<E: pharmsol::Equation + Clone + Send + 'static + EquationMetadataSou
.fit_with(NonParametricAlgorithm::npag())?;
let fit_time = fit_started.elapsed();

summarize_result(label, compile_time, fit_time, &result)
}

fn summarize_result<E: pharmsol::Equation>(
label: &'static str,
compile_time: Duration,
fit_time: Duration,
result: &NonParametricResult<E>,
) -> Result<ComparisonResult> {
Ok(ComparisonResult {
label,
compile_time,
Expand All @@ -235,7 +135,7 @@ fn summarize_result<E: pharmsol::Equation>(
fn run_legacy() -> Result<ComparisonResult> {
let started = Instant::now();
let equation = legacy_equation();
run_case("legacy-handwritten", started.elapsed(), equation)
run_case("handwritten", started.elapsed(), equation)
}

fn run_macro() -> Result<ComparisonResult> {
Expand All @@ -244,55 +144,21 @@ fn run_macro() -> Result<ComparisonResult> {
run_case("macro-ode", started.elapsed(), equation)
}

#[cfg(feature = "dsl-jit")]
fn run_runtime_jit() -> Result<ComparisonResult> {
let (equation, compile_time) = compile_runtime_ode(
"dsl-jit",
RuntimeBackend::Jit,
RuntimeCompilationTarget::Jit,
)?;
run_case("dsl-jit", compile_time, equation)
}

#[cfg(all(feature = "dsl-aot", feature = "dsl-aot-load"))]
fn run_runtime_native_aot() -> Result<ComparisonResult> {
let root = artifact_root("native-aot")?;
let (equation, compile_time) = compile_runtime_ode(
"dsl-native-aot",
RuntimeBackend::NativeAot,
RuntimeCompilationTarget::NativeAot(
dsl::NativeAotCompileOptions::new(root.join("build"))
.with_output(root.join("bimodal_ke_runtime_aot.pkm")),
),
)?;
run_case("dsl-native-aot", compile_time, equation)
}

#[cfg(feature = "dsl-wasm")]
fn run_runtime_wasm() -> Result<ComparisonResult> {
let (equation, compile_time) = compile_runtime_ode(
"dsl-wasm",
RuntimeBackend::Wasm,
RuntimeCompilationTarget::Wasm,
)?;
run_case("dsl-wasm", compile_time, equation)
fn run_dsl() -> Result<ComparisonResult> {
let started = Instant::now();
let equation = dsl_equation()?;
run_case("dsl-runtime", started.elapsed(), equation)
}

fn print_summary(results: &[ComparisonResult]) -> Result<()> {
let baseline = results
.iter()
.find(|result| result.label == "legacy-handwritten")
.ok_or_else(|| anyhow!("missing legacy-handwritten baseline"))?;
.find(|result| result.label == "handwritten")
.ok_or_else(|| anyhow!("missing handwritten baseline"))?;

println!("bimodal_ke NPAG backend comparison");
println!("dataset: {DATA_PATH}");
println!("cache: on");
#[cfg(not(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
feature = "dsl-wasm"
)))]
println!("runtime DSL backends skipped; enable dsl-jit, dsl-aot+dsl-aot-load, or dsl-wasm to include them");
println!();
println!(
"{:<18} {:>12} {:>12} {:>12} {:>14} {:>14}",
Expand Down
6 changes: 1 addition & 5 deletions src/model/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,11 +139,7 @@ impl_metadata_opt!(ODE, Analytical, SDE);
macro_rules! impl_metadata_some {
($($t:ty),+) => {
$(
#[cfg(any(
feature = "dsl-jit",
all(feature = "dsl-aot", feature = "dsl-aot-load"),
all(feature = "dsl-wasm", not(all(target_arch = "wasm32", target_os = "unknown")))
))]
#[cfg(feature = "dsl")]
impl EquationMetadataSource for $t {
fn equation_metadata(&self) -> Option<&ValidatedModelMetadata> {
Some(self.metadata())
Expand Down
2 changes: 1 addition & 1 deletion tests/onecomp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ fn test_one_compartment_npod() -> Result<()> {
.fit_with(NonParametricAlgorithm::npod())?;

// Check the results
assert_eq!(result.cycles(), 12);
assert_eq!(result.cycles(), 11);
assert!(result.objf() - 565.7749 < 0.01);

Ok(())
Expand Down
Loading