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
44 changes: 44 additions & 0 deletions build/scripts/miri.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#
# SPDX-License-Identifier: MIT
# Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
#

set -o errexit
set -o nounset
set -o pipefail

CRATES=(
"deep_causality"
"deep_causality_algorithms"
"deep_causality_ast"
"deep_causality_calculus"
"deep_causality_cfd"
"deep_causality_core"
"deep_causality_data_structures"
"deep_causality_discovery"
"deep_causality_ethos"
"deep_causality_fft"
"deep_causality_haft"
"deep_causality_macros"
"deep_causality_metric"
"deep_causality_multivector"
"deep_causality_num"
"deep_causality_par"
"deep_causality_rand"
"deep_causality_physics"
"deep_causality_sparse"
"deep_causality_tensor"
"deep_causality_topology"
"deep_causality_uncertain"
"ultragraph"
)

for CRATE_NAME in "${CRATES[@]}"; do
echo "Running MIRI for crate: $CRATE_NAME"
if ! cargo miri test -p "$CRATE_NAME" --lib --tests
then
echo "Failed to run MIRI for $CRATE_NAME"
fi
done

echo "MIRI complete."
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ fn mixture_with_all_anomalous_collapses_to_the_present_expert() {
}

#[test]
#[cfg_attr(miri, ignore)]
fn mixture_is_finite_and_deterministic() {
let node = vec![0.0, 1.0, 2.0, 8.0, 9.0, 10.0];
let parents = vec![
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
*/
#[cfg(test)]
mod augment_tests;
// Disabled under Miri
#[cfg(test)]
#[cfg(not(miri))]
mod boss_bootstrap_tests;
#[cfg(test)]
mod boss_config_tests;
Expand Down Expand Up @@ -33,12 +35,15 @@ mod gate_tests;
#[cfg(test)]
mod gaussian_tests;
#[cfg(test)]
#[cfg(not(miri))]
mod mapconfig_tests;
#[cfg(test)]
#[cfg(not(miri))]
mod mec_tests;
#[cfg(test)]
mod meek_tests;
#[cfg(test)]
#[cfg(not(miri))]
mod update_tests;
#[cfg(test)]
mod validity_tests;
2 changes: 2 additions & 0 deletions deep_causality_algorithms/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
* Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
*/
mod causal_discovery;
// MIRI takes forever on DAG sampling
#[cfg(not(miri))]
mod dag_sampling;
mod feature_selection;
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ fn march(solver: &DecNsSolver<'_, 2, f64>, seed: &SolenoidalField<f64>, steps: u
}

#[test]
#[cfg_attr(miri, ignore)]
fn body_force_zone_marches_bit_identically_to_the_legacy_solver() {
let (m, h) = channel(9, [true, false]);
let dt = 0.5 * h * h / (4.0 * NU);
Expand Down
2 changes: 2 additions & 0 deletions deep_causality_cfd/tests/solvers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,7 @@
* Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
*/

#[cfg(test)]
#[cfg(not(miri))]
mod dec;
mod dec_config;
1 change: 1 addition & 0 deletions deep_causality_cfd/tests/types/flow/coupling_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ fn a_stage_error_short_circuits_the_coupling() {
}

#[test]
#[cfg_attr(miri, ignore)]
fn coupled_viscosity_feedback_changes_the_flow_dynamics() {
// A 3D Taylor–Green vortex decay marched twice: once single-physics, once with a hot-wall
// thermal relaxation feeding ν(T) back through the ambient. The coupled run dissipates
Expand Down
7 changes: 7 additions & 0 deletions deep_causality_cfd/tests/types/flow/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,26 @@ where
pub mod coupling_tests;
#[cfg(test)]
pub mod frequency_tests;
// IO operations are unsupported under MIRI.
#[cfg(test)]
#[cfg(not(miri))]
pub mod io_tests;
#[cfg(test)]
#[cfg(not(miri))]
pub mod march_case_tests;
#[cfg(test)]
#[cfg(not(miri))]
pub mod march_run_tests;
#[cfg(test)]
pub mod mms_tests;
#[cfg(test)]
#[cfg(not(miri))]
pub mod operator_study_tests;
#[cfg(test)]
pub mod report_tests;
// IO operations are unsupported under MIRI.
#[cfg(test)]
#[cfg(not(miri))]
pub mod uncertain_march_run_tests;
#[cfg(test)]
pub mod verify_tests;
2 changes: 2 additions & 0 deletions deep_causality_physics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub(crate) mod error;
pub(crate) mod kernels;
pub mod quantities;
pub mod theories;
#[cfg(feature = "alloc")]
pub mod utils_tests;

pub use crate::constants::*;

Expand Down
110 changes: 110 additions & 0 deletions deep_causality_physics/src/utils_tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* SPDX-License-Identifier: MIT
* Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
*/

//! Shared test fixtures for the physics test suite.
//!
//! These helpers build discrete manifolds and cochains used by the
//! `SolenoidalField` tests. They live in the source tree (rather than inside
//! the `tests/` folder) because the Bazel `rust_test_suite` compiles each
//! `*_tests.rs` file as a standalone crate, so test files cannot share helpers
//! across one another — only the crate under test is visible to all of them.

use alloc::{vec, vec::Vec};
use deep_causality_num::{FromPrimitive, RealField};
use deep_causality_tensor::CausalTensor;
use deep_causality_topology::{ChainComplex, CubicalReggeGeometry, LatticeComplex, Manifold};

/// A 2D square-torus lattice manifold of side `n` with unit cubical metric.
pub fn unit_manifold<R>(n: usize) -> Manifold<LatticeComplex<2, R>, R>
where
R: RealField
+ deep_causality_par::MaybeParallel
+ FromPrimitive
+ Default
+ PartialEq
+ core::fmt::Debug
+ core::fmt::Display,
{
let lattice: LatticeComplex<2, R> = LatticeComplex::square_torus(n);
let total: usize = (0..=2).map(|k| lattice.num_cells(k)).sum();
let data = CausalTensor::new(vec![R::zero(); total], vec![total]).unwrap();
let metric: CubicalReggeGeometry<2, R> = CubicalReggeGeometry::unit();
Manifold::from_cubical_with_metric(lattice, data, metric, 0)
}

/// A deterministic pseudo-random cochain of length `len` in `[-1, 1]`.
pub fn random_cochain<R: RealField + FromPrimitive>(len: usize, seed: u64) -> Vec<R> {
let mut state = seed
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
(0..len)
.map(|_| {
state = state
.wrapping_mul(6364136223846793005)
.wrapping_add(1442695040888963407);
let unit = (state >> 11) as f64 / (1u64 << 53) as f64;
R::from_f64(2.0 * unit - 1.0).expect("[-1,1] lifts")
})
.collect()
}

/// Discrete divergence of an edge cochain: place at grade 1, apply δ.
pub fn divergence<R>(manifold: &Manifold<LatticeComplex<2, R>, R>, one_form: &[R]) -> Vec<R>
where
R: RealField
+ deep_causality_par::MaybeParallel
+ FromPrimitive
+ Default
+ PartialEq
+ core::fmt::Debug
+ core::fmt::Display,
{
let lattice = LatticeComplex::<2, R>::square_torus(
// shape is square by fixture construction
manifold.complex().shape()[0],
);
let total: usize = (0..=2).map(|g| lattice.num_cells(g)).sum();
let n0 = lattice.num_cells(0);
let mut data = vec![R::zero(); total];
data[n0..n0 + one_form.len()].copy_from_slice(one_form);
let tensor = CausalTensor::new(data, vec![total]).unwrap();
let metric: CubicalReggeGeometry<2, R> = CubicalReggeGeometry::unit();
let m = Manifold::from_cubical_with_metric(lattice, tensor, metric, 0);
m.codifferential(1).as_slice().to_vec()
}

/// Supremum (max-abs) norm of a slice.
pub fn sup_norm<R: RealField>(v: &[R]) -> R {
v.iter()
.map(|x| x.abs())
.fold(R::zero(), |m, x| if x > m { x } else { m })
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_sup_norm() {
assert_eq!(sup_norm(&[-3.0_f64, 1.0, 2.0]), 3.0);
assert_eq!(sup_norm::<f64>(&[]), 0.0);
}

#[test]
fn test_random_cochain_is_bounded_and_deterministic() {
let a = random_cochain::<f64>(16, 7);
let b = random_cochain::<f64>(16, 7);
assert_eq!(a, b);
assert!(a.iter().all(|&x| (-1.0..=1.0).contains(&x)));
}

#[test]
fn test_unit_manifold_and_divergence_dimensions() {
let manifold = unit_manifold::<f64>(4);
let n1 = manifold.complex().num_cells(1);
let div = divergence(&manifold, &random_cochain::<f64>(n1, 1));
assert_eq!(div.len(), manifold.complex().num_cells(0));
}
}
2 changes: 1 addition & 1 deletion deep_causality_physics/tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ rust_test_suite(
rust_test_suite(
name = "quantities",
srcs = glob([
"quantities/*_tests.rs",
"quantities/*/*_tests.rs",
]),
crate_features = [
"std",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* SPDX-License-Identifier: MIT
* Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
*/

mod body_force_one_form_tests;
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* SPDX-License-Identifier: MIT
* Copyright (c) 2023 - 2026. The DeepCausality Authors and Contributors. All Rights Reserved.
*/

const TOLERANCE: f64 = 1e-12;
use deep_causality_physics::{CentralBody, EARTH_GM, EARTH_J2, EARTH_RADIUS_EQUATORIAL};

// =============================================================================
// CentralBody — construction
// =============================================================================

#[test]
fn test_central_body_new_basic() {
let body = CentralBody::<f64>::new(1.0e14, 6.0e6, 1.0e-3);
assert!((body.gm - 1.0e14).abs() < TOLERANCE);
assert!((body.equatorial_radius_m - 6.0e6).abs() < TOLERANCE);
assert!((body.j2 - 1.0e-3).abs() < TOLERANCE);
}

#[test]
fn test_central_body_new_zero_j2() {
// J2 = 0 corresponds to a perfectly spherical body — used for forward
// modelling tests where the J2 correction is intentionally disabled.
let body = CentralBody::<f64>::new(EARTH_GM, EARTH_RADIUS_EQUATORIAL, 0.0);
assert_eq!(body.j2, 0.0);
}

#[test]
fn test_central_body_new_mars_like() {
// Mars: GM ≈ 4.28e13, R_eq ≈ 3.396e6, J2 ≈ 1.96e-3
let mars = CentralBody::<f64>::new(4.28e13, 3.396e6, 1.96e-3);
assert!((mars.gm - 4.28e13).abs() < TOLERANCE);
assert!((mars.equatorial_radius_m - 3.396e6).abs() < TOLERANCE);
assert!((mars.j2 - 1.96e-3).abs() < TOLERANCE);
}

// =============================================================================
// CentralBody — EARTH_JGM3 constant
// =============================================================================

#[test]
fn test_central_body_earth_jgm3_gm() {
let body = CentralBody::EARTH_JGM3;
assert_eq!(body.gm, EARTH_GM);
}

#[test]
fn test_central_body_earth_jgm3_radius() {
let body = CentralBody::EARTH_JGM3;
assert_eq!(body.equatorial_radius_m, EARTH_RADIUS_EQUATORIAL);
}

#[test]
fn test_central_body_earth_jgm3_j2() {
let body = CentralBody::EARTH_JGM3;
assert_eq!(body.j2, EARTH_J2);
}

#[test]
fn test_central_body_earth_jgm3_consistency() {
// The published EARTH_GM, EARTH_RADIUS_EQUATORIAL, and EARTH_J2 values must
// all be present and reasonable. This guards against accidental constant
// drift in the source tree.
let body = CentralBody::EARTH_JGM3;
assert!(body.gm > 3.9e14 && body.gm < 4.0e14);
assert!(body.equatorial_radius_m > 6.3e6 && body.equatorial_radius_m < 6.4e6);
assert!(body.j2 > 1.0e-3 && body.j2 < 2.0e-3);
}

// =============================================================================
// CentralBody — derive macros
// =============================================================================

#[test]
fn test_central_body_clone() {
let original = CentralBody::EARTH_JGM3;
let cloned = Clone::clone(&original);
assert_eq!(original, cloned);
}

#[test]
fn test_central_body_copy() {
let original = CentralBody::EARTH_JGM3;
let copied = original; // Copy semantics
assert_eq!(original, copied);
}

#[test]
fn test_central_body_debug() {
let body = CentralBody::EARTH_JGM3;
let debug_str = format!("{:?}", body);
assert!(debug_str.contains("CentralBody"));
assert!(debug_str.contains("gm"));
assert!(debug_str.contains("j2"));
}

#[test]
fn test_central_body_partial_eq_equal() {
let a = CentralBody::<f64>::new(1.0e14, 6.0e6, 1.0e-3);
let b = CentralBody::<f64>::new(1.0e14, 6.0e6, 1.0e-3);
assert_eq!(a, b);
}

#[test]
fn test_central_body_partial_eq_different_gm() {
let a = CentralBody::<f64>::new(1.0e14, 6.0e6, 1.0e-3);
let b = CentralBody::<f64>::new(2.0e14, 6.0e6, 1.0e-3);
assert_ne!(a, b);
}

#[test]
fn test_central_body_partial_eq_different_radius() {
let a = CentralBody::<f64>::new(1.0e14, 6.0e6, 1.0e-3);
let b = CentralBody::<f64>::new(1.0e14, 7.0e6, 1.0e-3);
assert_ne!(a, b);
}

#[test]
fn test_central_body_partial_eq_different_j2() {
let a = CentralBody::<f64>::new(1.0e14, 6.0e6, 1.0e-3);
let b = CentralBody::<f64>::new(1.0e14, 6.0e6, 2.0e-3);
assert_ne!(a, b);
}
Loading
Loading