RustedSciThe 0.4.20

Rust framework for symbolic and numerical computing:BVP ( Newton-Raphson frozen/damped/with collocations ), IVP( BDF, Radau, Backward Euler, LSODE, LSODA, RK45, DoPri), nonlinear equations ( Levenberg, Gavin) and more
//! Thin nonlinear-system bridge into the generic AOT lifecycle.
//!
//! This module keeps `Nonlinear_systems` code from manually rebuilding the
//! generic AOT stack every time it wants to use symbolic dense codegen.
//! It provides a narrow set of helpers:
//! - prepare a generic dense `PreparedProblem`,
//! - assemble a generated AOT crate from one symbolic nonlinear problem,
//! - materialize a build request on disk.
//!
//! In other words, this is the lifecycle-facing adapter for the nonlinear
//! family: symbolic code calls into it, but generic codegen/build layers remain
//! unaware of nonlinear-specific setup details.

use crate::numerical::Nonlinear_systems::symbolic::{
    PreparedSymbolicNonlinearAotProblem, SymbolicDenseAotOptions, SymbolicLambdifyFrontend,
    SymbolicNonlinearProblem,
};
use crate::numerical::Nonlinear_systems::symbolic_atom_aot::generated_atom_dense_crate_for_prepared;
use crate::symbolic::codegen::codegen_aot_driver::generated_aot_crate_from_prepared_problem;
use crate::symbolic::codegen::rust_backend::codegen_aot_build::{
    AotBuildProfile, AotBuildRequest, AotBuildResult, AotCompileConfig,
};
use crate::symbolic::codegen::rust_backend::codegen_aot_crate::GeneratedAotCrate;
use std::io;
use std::path::Path;

/// Prepares the nonlinear dense AOT bridge owned by the symbolic problem layer.
///
/// The returned bridge owns the flattened input ordering and the runtime-plan
/// buffers that the generic `PreparedDenseProblem` borrows from. Callers that
/// only need the generic shape can use `bridge.as_prepared_problem()`.
pub fn prepared_problem_from_symbolic_nonlinear_problem<'a>(
    problem: &'a SymbolicNonlinearProblem,
    options: SymbolicDenseAotOptions,
) -> PreparedSymbolicNonlinearAotProblem<'a> {
    problem.prepare_dense_aot_problem(options)
}

/// Builds a generated AOT crate directly from a symbolic nonlinear problem.
pub fn generated_aot_crate_from_symbolic_nonlinear_problem(
    crate_name: &str,
    module_name: &str,
    problem: &SymbolicNonlinearProblem,
    options: SymbolicDenseAotOptions,
) -> GeneratedAotCrate {
    let prepared = prepared_problem_from_symbolic_nonlinear_problem(problem, options);
    let prepared_problem = crate::symbolic::codegen::codegen_provider_api::PreparedProblem::dense(
        prepared.as_prepared_problem(),
    );
    generated_aot_crate_from_prepared_problem(crate_name, module_name, &prepared_problem)
}

/// Materializes a build request for one symbolic nonlinear AOT crate.
pub fn materialize_symbolic_nonlinear_aot_build(
    crate_name: &str,
    module_name: &str,
    problem: &SymbolicNonlinearProblem,
    options: SymbolicDenseAotOptions,
    output_parent_dir: &Path,
    profile: AotBuildProfile,
) -> io::Result<AotBuildResult> {
    materialize_symbolic_nonlinear_aot_build_with_compile_config(
        crate_name,
        module_name,
        problem,
        options,
        output_parent_dir,
        profile,
        AotCompileConfig::default(),
    )
}

/// Materializes a nonlinear AOT build with explicit Rust compile settings.
///
/// The compile settings affect only the external cold build. They do not
/// change the generated ABI, runtime plan, or numerical evaluation path.
pub fn materialize_symbolic_nonlinear_aot_build_with_compile_config(
    crate_name: &str,
    module_name: &str,
    problem: &SymbolicNonlinearProblem,
    options: SymbolicDenseAotOptions,
    output_parent_dir: &Path,
    profile: AotBuildProfile,
    compile_config: AotCompileConfig,
) -> io::Result<AotBuildResult> {
    let prepared = prepared_problem_from_symbolic_nonlinear_problem(problem, options);
    let crate_spec = match problem.lambdify_frontend() {
        SymbolicLambdifyFrontend::ExprLegacy => {
            generated_aot_crate_from_symbolic_nonlinear_problem(
                crate_name,
                module_name,
                problem,
                options,
            )
        }
        SymbolicLambdifyFrontend::AtomViewNative => generated_atom_dense_crate_for_prepared(
            crate_name,
            module_name,
            problem,
            &prepared,
            options,
        )
        .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error.to_string()))?,
    };
    AotBuildRequest::new(crate_spec, output_parent_dir, profile)
        .with_compile_config(compile_config)
        .materialize()
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::numerical::Nonlinear_systems::symbolic::SymbolicProblemOptions;
    use crate::symbolic::symbolic_engine::Expr;
    use tempfile::tempdir;

    fn elementary_problem() -> SymbolicNonlinearProblem {
        SymbolicNonlinearProblem::from_expressions_with_options(
            vec![
                Expr::parse_expression("x^2+y^2-10"),
                Expr::parse_expression("x-y-4"),
            ],
            SymbolicProblemOptions::new().with_variables(vec!["x".to_string(), "y".to_string()]),
        )
        .expect("problem should build")
    }

    #[test]
    fn nonlinear_symbolic_aot_bridge_builds_prepared_problem_with_aot_backend() {
        let problem = elementary_problem();
        let prepared = prepared_problem_from_symbolic_nonlinear_problem(
            &problem,
            SymbolicDenseAotOptions::default(),
        );
        let generic = prepared.as_prepared_problem();

        assert_eq!(
            generic.backend_kind,
            crate::symbolic::codegen::codegen_provider_api::BackendKind::Aot
        );
        assert_eq!(
            generic.matrix_backend,
            crate::symbolic::codegen::codegen_provider_api::MatrixBackend::Dense
        );
        assert_eq!(prepared.residual_len(), 2);
        assert_eq!(prepared.jacobian_shape(), (2, 2));
    }

    #[test]
    fn nonlinear_symbolic_aot_bridge_builds_generated_crate() {
        let problem = elementary_problem();
        let crate_spec = generated_aot_crate_from_symbolic_nonlinear_problem(
            "generated_nonlinear_symbolic_fixture",
            "generated_nonlinear_symbolic_module",
            &problem,
            SymbolicDenseAotOptions::default(),
        );

        assert_eq!(
            crate_spec.crate_name,
            "generated_nonlinear_symbolic_fixture"
        );
        assert_eq!(crate_spec.manifest.backend_kind.as_str(), "aot");
        assert_eq!(crate_spec.manifest.matrix_backend.as_str(), "dense");
        assert!(crate_spec
            .module_source
            .contains("pub mod generated_nonlinear_symbolic_module"));
        assert!(crate_spec
            .module_source
            .contains("pub fn eval_nonlinear_residual"));
        assert!(crate_spec
            .module_source
            .contains("pub fn eval_nonlinear_jacobian"));
    }

    #[test]
    fn nonlinear_symbolic_aot_bridge_materializes_release_build_request() {
        let problem = elementary_problem();
        let dir = tempdir().expect("tempdir should exist");
        let result = materialize_symbolic_nonlinear_aot_build(
            "generated_nonlinear_symbolic_build",
            "generated_nonlinear_symbolic_build_module",
            &problem,
            SymbolicDenseAotOptions::default(),
            dir.path(),
            AotBuildProfile::Release,
        )
        .expect("build request should materialize");

        assert_eq!(result.cargo_command_line(), "cargo build --release");
        assert!(result.written.cargo_toml.exists());
        assert!(result.written.generated_rs.exists());
        assert_eq!(
            result
                .expected_rlib
                .file_name()
                .and_then(|name| name.to_str()),
            Some("libgenerated_nonlinear_symbolic_build.rlib")
        );
    }

    #[test]
    fn nonlinear_symbolic_aot_bridge_exposes_explicit_fast_build_profile() {
        let problem = elementary_problem();
        let dir = tempdir().expect("tempdir should exist");
        let result = materialize_symbolic_nonlinear_aot_build_with_compile_config(
            "generated_nonlinear_symbolic_fast_build",
            "generated_nonlinear_symbolic_fast_build_module",
            &problem,
            SymbolicDenseAotOptions::default(),
            dir.path(),
            AotBuildProfile::Release,
            AotCompileConfig::fast_build(),
        )
        .expect("fast AOT build request should materialize");

        assert_eq!(
            result.cargo_env,
            vec![(
                "RUSTFLAGS".to_string(),
                "-C opt-level=1 -C codegen-units=16".to_string()
            )]
        );
        assert_eq!(result.cargo_command_line(), "cargo build --release");
    }
}