use cvxrust::prelude::*;
const TOL: f64 = 1e-4;
#[test]
fn test_exp_basic() {
let x = variable(());
let obj = exp(&x);
let solution = Problem::minimize(obj)
.subject_to([x.ge(constant(0.0))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let val = solution.value.unwrap();
assert!((val - 1.0).abs() < TOL, "Expected 1.0, got {}", val);
let x_val = solution.value(&x);
assert!(x_val.abs() < TOL, "Expected x=0, got {}", x_val);
}
#[test]
fn test_exp_with_constraint() {
let x = variable(());
let solution = Problem::maximize(x.clone())
.subject_to([exp(&x).le(constant(5.0))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let x_val = solution.value(&x);
let expected = 5.0_f64.ln();
assert!(
(x_val - expected).abs() < TOL,
"Expected {}, got {}",
expected,
x_val
);
}
#[test]
fn test_log_basic() {
let x = variable(());
let obj = log(&x);
let solution = Problem::maximize(obj)
.subject_to([x.le(constant(2.0)), x.ge(constant(0.01))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let val = solution.value.unwrap();
let expected = 2.0_f64.ln();
assert!(
(val - expected).abs() < TOL,
"Expected {}, got {}",
expected,
val
);
}
#[test]
fn test_log_concave() {
let x = variable(());
let obj = -log(&x);
let solution = Problem::minimize(obj)
.subject_to([x.ge(constant(0.1)), x.le(constant(1.0))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let x_val = solution.value(&x);
assert!((x_val - 1.0).abs() < TOL, "Expected x=1, got {}", x_val);
}
#[test]
fn test_entropy_basic() {
let x = variable(());
let solution = Problem::maximize(entropy(&x))
.subject_to([x.le(constant(0.5)), x.ge(constant(0.1))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
}
#[test]
fn test_power_p_half() {
let x = variable(());
let obj = -sqrt(&x);
let solution = Problem::minimize(obj)
.subject_to([x.le(constant(4.0)), x.ge(constant(0.01))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let x_val = solution.value(&x);
assert!((x_val - 4.0).abs() < TOL, "Expected x=4, got {}", x_val);
}
#[test]
fn test_power_p_greater_than_1() {
let x = variable(());
let obj = power(&x, 3.0);
let solution = Problem::minimize(obj)
.subject_to([x.ge(constant(1.0))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let x_val = solution.value(&x);
assert!((x_val - 1.0).abs() < TOL, "Expected x=1, got {}", x_val);
}
#[test]
fn test_power_p_less_than_1() {
let x = variable(());
let obj = power(&x, 0.3);
let solution = Problem::maximize(obj)
.subject_to([x.le(constant(8.0)), x.ge(constant(0.1))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let x_val = solution.value(&x);
assert!((x_val - 8.0).abs() < TOL, "Expected x=8, got {}", x_val);
}
#[test]
fn test_sqrt_vector() {
let x = variable(3);
let obj = sum(&sqrt(&x));
let solution = Problem::maximize(obj)
.subject_to([
x.le(constant_vec(vec![1.0, 4.0, 9.0])),
x.ge(constant(0.01)),
])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
if let Some(Array::Dense(x_vals)) = solution.get_value(x.variable_id().unwrap()) {
assert!((x_vals[(0, 0)] - 1.0).abs() < TOL);
assert!((x_vals[(1, 0)] - 4.0).abs() < TOL);
assert!((x_vals[(2, 0)] - 9.0).abs() < TOL);
} else {
panic!("Expected dense array for x");
}
}
#[test]
fn test_cumsum_basic() {
let x = variable(3);
let y = cumsum(&x);
let solution = Problem::minimize(sum(&y))
.subject_to([x.eq(constant_vec(vec![1.0, 2.0, 3.0]))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let val = solution.value.unwrap();
assert!((val - 10.0).abs() < TOL, "Expected 10.0, got {}", val);
}
#[test]
fn test_cumsum_optimization() {
let x = variable(3);
let y = cumsum(&x);
let solution = Problem::minimize(sum(&y))
.subject_to([sum(&x).eq(constant(6.0)), x.ge(constant(0.0))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let val = solution.value.unwrap();
assert!((val - 6.0).abs() < TOL, "Expected 6.0, got {}", val);
if let Some(Array::Dense(x_vals)) = solution.get_value(x.variable_id().unwrap()) {
assert!(x_vals[(0, 0)] < TOL);
assert!(x_vals[(1, 0)] < TOL);
assert!((x_vals[(2, 0)] - 6.0).abs() < TOL);
}
}
#[test]
fn test_diag_basic() {
let x = variable(3);
let d = diag(&x);
let solution = Problem::minimize(trace(&d))
.subject_to([x.eq(constant_vec(vec![1.0, 2.0, 3.0]))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let val = solution.value.unwrap();
assert!((val - 6.0).abs() < TOL, "Expected 6.0, got {}", val);
}
#[test]
fn test_dual_values_simple() {
let x = variable(());
let solution = Problem::minimize(x.clone())
.subject_to([x.ge(constant(1.0))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
assert!(solution.has_duals());
let duals = solution.duals().unwrap();
assert!(!duals.is_empty(), "Should have dual values");
let dual_0 = solution.constraint_dual(0).unwrap();
assert!(
dual_0.abs() > TOL,
"Dual should be non-zero for binding constraint"
);
}
#[test]
fn test_dual_values_shadow_price() {
let x = variable(());
let y = variable(());
let solution = Problem::minimize(&x + &y)
.subject_to([
(&x + &y).ge(constant(10.0)),
x.ge(constant(0.0)),
y.ge(constant(0.0)),
])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
if let Some(duals) = solution.duals() {
println!("Dual values: {:?}", duals);
}
}
#[test]
fn test_exp_dcp_rules() {
use cvxrust::dcp::Curvature;
let x = variable(());
let e = exp(&x);
assert_eq!(e.curvature(), Curvature::Convex);
assert!(e.is_nonneg());
}
#[test]
fn test_log_dcp_rules() {
use cvxrust::dcp::Curvature;
let x = variable(());
let l = log(&x);
assert_eq!(l.curvature(), Curvature::Concave);
}
#[test]
fn test_power_dcp_rules() {
use cvxrust::dcp::Curvature;
let x = variable(());
assert_eq!(power(&x, 2.5).curvature(), Curvature::Convex);
assert_eq!(power(&x, 0.7).curvature(), Curvature::Concave);
assert_eq!(sqrt(&x).curvature(), Curvature::Concave);
assert_eq!(power(&x, 1.0).curvature(), Curvature::Affine);
}
#[test]
fn test_cumsum_dcp() {
use cvxrust::dcp::Curvature;
let x = variable(3);
let cs = cumsum(&x);
assert_eq!(cs.curvature(), Curvature::Affine);
}
#[test]
fn test_diag_dcp() {
use cvxrust::dcp::Curvature;
let x = variable(3);
let d = diag(&x);
assert_eq!(d.curvature(), Curvature::Affine);
}
#[test]
fn test_logistic_regression_setup() {
let x = variable(());
let loss = exp(&x) + x.clone();
assert!(loss.is_convex(), "exp(x) + x should be convex");
let solution = Problem::minimize(loss)
.subject_to([x.ge(constant(-2.0))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let x_val = solution.value(&x);
assert!((x_val - (-2.0)).abs() < TOL, "Expected x=-2, got {}", x_val);
}
#[test]
fn test_geometric_program_equivalent() {
let x = variable(());
let y = variable(());
let x_sq = power(&x, 2.0);
let solution = Problem::minimize(x_sq)
.subject_to([x.ge(constant(1.0)), y.ge(constant(1.0))])
.solve()
.expect("Should solve");
assert_eq!(solution.status, SolveStatus::Optimal);
let x_val = solution.value(&x);
assert!((x_val - 1.0).abs() < TOL);
}