Skip to content

Commit

Permalink
fix: Several tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
jetuk committed Sep 22, 2023
1 parent c87fa92 commit f4b5bdb
Show file tree
Hide file tree
Showing 9 changed files with 69 additions and 42 deletions.
6 changes: 3 additions & 3 deletions ipm-ocl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ pub struct Tolerances {
impl Default for Tolerances {
fn default() -> Self {
Self {
primal_feasibility: 1e-6,
dual_feasibility: 1e-6,
optimality: 1e-6,
primal_feasibility: 1e-8,
dual_feasibility: 1e-8,
optimality: 1e-8,
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions ipm-simd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ where
{
fn default() -> Self {
Self {
primal_feasibility: Simd::<T, N>::splat(1e-6.into()),
dual_feasibility: Simd::<T, N>::splat(1e-6.into()),
optimality: Simd::<T, N>::splat(1e-6.into()),
primal_feasibility: Simd::<T, N>::splat(1e-8.into()),
dual_feasibility: Simd::<T, N>::splat(1e-8.into()),
optimality: Simd::<T, N>::splat(1e-8.into()),
}
}
}
Expand Down
2 changes: 0 additions & 2 deletions ipm-simd/src/path_following_direct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ use super::{dual_feasibility, primal_feasibility, Matrix};
use crate::common::{compute_dx_dz_dw, dot_product, normal_eqn_rhs, vector_norm, vector_set, vector_update};
use crate::Tolerances;
use ipm_common::SparseNormalCholeskyIndices;
use nalgebra_sparse::na::SimdBool;
use std::fmt::Debug;
use std::iter::Sum;
use std::ops::{Add, AddAssign, Div, Mul, Neg, Sub};
use std::simd::{
Expand Down
52 changes: 31 additions & 21 deletions src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,11 @@ impl Model {
fn required_features(&self) -> HashSet<SolverFeatures> {
let mut features = HashSet::new();

// Aggregated node feature required if there are any aggregated nodes
if self.aggregated_nodes.len() > 0 {
features.insert(SolverFeatures::AggregatedNode);
}

// Aggregated node factors required if any aggregated node has factors defined.
if self.aggregated_nodes.iter().any(|n| n.get_factors().is_some()) {
features.insert(SolverFeatures::AggregatedNodeFactors);
Expand Down Expand Up @@ -1479,7 +1484,7 @@ mod tests {
use crate::solvers::{ClIpmF64Solver, SimdIpmF64Solver};
use crate::solvers::{ClpSolver, ClpSolverSettings};
use crate::test_utils::{default_timestepper, run_all_solvers, simple_model, simple_storage_model};
use float_cmp::approx_eq;
use float_cmp::{approx_eq, assert_approx_eq};
use ndarray::{Array, Array2};
use std::ops::Deref;

Expand Down Expand Up @@ -1577,39 +1582,44 @@ mod tests {

#[test]
fn test_step() {
let model = simple_model(2);
const NUM_SCENARIOS: usize = 2;
let model = simple_model(NUM_SCENARIOS);

let timestepper = default_timestepper();

let mut timings = RunTimings::default();
let timesteps = timestepper.timesteps();
let mut ts_iter = timesteps.iter();

let ts = ts_iter.next().unwrap();
let (scenario_indices, mut current_state, mut p_internal, _r_internal) = model.setup(&timesteps).unwrap();

let mut solvers = model.setup_solver::<ClpSolver>(&ClpSolverSettings::default()).unwrap();
assert_eq!(current_state.len(), scenario_indices.len());

model
.step(
ts,
&scenario_indices,
&mut solvers,
&mut current_state,
&mut p_internal,
&mut timings,
)
.unwrap();

let output_node = model.get_node_by_name("output", None).unwrap();

let state0 = current_state.get(0).unwrap();
let output_inflow = state0
.get_network_state()
.get_node_in_flow(&output_node.index())
.unwrap();
assert!(approx_eq!(f64, output_inflow, 10.0));
for i in 0..2 {
let ts = ts_iter.next().unwrap();
model
.step(
ts,
&scenario_indices,
&mut solvers,
&mut current_state,
&mut p_internal,
&mut timings,
)
.unwrap();

for j in 0..NUM_SCENARIOS {
let state_j = current_state.get(j).unwrap();
let output_inflow = state_j
.get_network_state()
.get_node_in_flow(&output_node.index())
.unwrap();
assert_approx_eq!(f64, output_inflow, (1.0 + i as f64 + j as f64).min(12.0));
}
}
}

#[test]
Expand All @@ -1620,7 +1630,7 @@ mod tests {

// Set-up assertion for "input" node
let idx = model.get_node_by_name("input", None).unwrap().index();
let expected = Array::from_shape_fn((366, 10), |(i, j)| (i as f64 + j as f64).min(12.0));
let expected = Array::from_shape_fn((366, 10), |(i, j)| (1.0 + i as f64 + j as f64).min(12.0));

let recorder = AssertionRecorder::new("input-flow", Metric::NodeOutFlow(idx), expected.clone(), None, None);
model.add_recorder(Box::new(recorder)).unwrap();
Expand Down
1 change: 0 additions & 1 deletion src/schema/nodes/delay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ mod tests {
use crate::metric::Metric;
use crate::recorders::AssertionRecorder;
use crate::schema::model::PywrModel;
use crate::solvers::{ClpSolver, ClpSolverSettings};
use crate::test_utils::run_all_solvers;
use crate::timestep::Timestepper;
use ndarray::{concatenate, Array2, Axis};
Expand Down
6 changes: 5 additions & 1 deletion src/solvers/clp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,11 @@ impl Solver for ClpSolver {
type Settings = ClpSolverSettings;

fn features() -> &'static [SolverFeatures] {
&[SolverFeatures::AggregatedNodeFactors, SolverFeatures::VirtualStorage]
&[
SolverFeatures::AggregatedNode,
SolverFeatures::AggregatedNodeFactors,
SolverFeatures::VirtualStorage,
]
}

fn setup(model: &Model, settings: &Self::Settings) -> Result<Box<Self>, PywrError> {
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/ipm_ocl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -706,7 +706,7 @@ impl MultiStateSolver for ClIpmF64Solver {
let mut queues = Vec::new();

let num_chunks = settings.num_chunks();
let chunk_size = NonZeroUsize::new(num_scenarios / num_chunks).unwrap();
let chunk_size = NonZeroUsize::new(num_scenarios / num_chunks).unwrap_or(NonZeroUsize::MIN);

for chunk_scenarios in (0..num_scenarios).collect::<Vec<_>>().chunks(chunk_size.get()) {
// Create a queue per chunk.
Expand Down
1 change: 1 addition & 0 deletions src/solvers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl AddAssign for SolverTimings {
/// to solve a given model.
#[derive(PartialEq, Eq, Hash)]
pub enum SolverFeatures {
AggregatedNode,
AggregatedNodeFactors,
VirtualStorage,
}
Expand Down
35 changes: 25 additions & 10 deletions src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn simple_model(num_scenarios: usize) -> Model {
model.connect_nodes(input_node, link_node).unwrap();
model.connect_nodes(link_node, output_node).unwrap();

let inflow = Array::from_shape_fn((366, num_scenarios), |(i, j)| i as f64 + j as f64);
let inflow = Array::from_shape_fn((366, num_scenarios), |(i, j)| 1.0 + i as f64 + j as f64);
let inflow = Array2Parameter::new("inflow", inflow, scenario_idx);

let inflow = model.add_parameter(Box::new(inflow)).unwrap();
Expand Down Expand Up @@ -148,25 +148,40 @@ pub fn run_and_assert_parameter(
}

/// Run a model using each of the in-built solvers.
///
/// The model will only be run if the solver has the required solver features (and
/// is also enabled as a Cargo feature).
pub fn run_all_solvers(model: &Model, timestepper: &Timestepper) {
model
.run::<ClpSolver>(timestepper, &Default::default())
.expect("Failed to solve with CLP");

#[cfg(feature = "highs")]
model
.run::<HighsSolver>(timestepper, &Default::default())
.expect("Failed to solve with Highs");
{
if model.check_solver_features::<HighsSolver>() {
model
.run::<HighsSolver>(timestepper, &Default::default())
.expect("Failed to solve with Highs");
}
}

#[cfg(feature = "ipm-simd")]
model
.run_multi_scenario::<SimdIpmF64Solver<4>>(timestepper, &Default::default())
.expect("Failed to solve with SIMD IPM");
{
if model.check_multi_scenario_solver_features::<SimdIpmF64Solver<4>>() {
model
.run_multi_scenario::<SimdIpmF64Solver<4>>(timestepper, &Default::default())
.expect("Failed to solve with SIMD IPM");
}
}

#[cfg(feature = "ipm-ocl")]
model
.run_multi_scenario::<ClIpmF64Solver>(timestepper, &Default::default())
.expect("Failed to solve with OpenCl IPM");
{
if model.check_multi_scenario_solver_features::<ClIpmF64Solver>() {
model
.run_multi_scenario::<ClIpmF64Solver>(timestepper, &Default::default())
.expect("Failed to solve with OpenCl IPM");
}
}
}

/// Make a simple system with random inputs.
Expand Down

0 comments on commit f4b5bdb

Please sign in to comment.