use numr::runtime::cpu::{CpuClient, CpuRuntime};
use numr::tensor::Tensor;
use crate::optimize::error::OptimizeResult;
use crate::optimize::qp::impl_generic::{active_set_qp_impl, interior_point_qp_impl};
use crate::optimize::qp::traits::{QpAlgorithms, QpMethod, QpOptions, QpResult};
impl QpAlgorithms<CpuRuntime> for CpuClient {
fn solve_qp(
&self,
q: &Tensor<CpuRuntime>,
c: &Tensor<CpuRuntime>,
a_eq: Option<&Tensor<CpuRuntime>>,
b_eq: Option<&Tensor<CpuRuntime>>,
a_ineq: Option<&Tensor<CpuRuntime>>,
b_ineq: Option<&Tensor<CpuRuntime>>,
options: &QpOptions,
) -> OptimizeResult<QpResult<CpuRuntime>> {
match options.method {
QpMethod::ActiveSet => {
active_set_qp_impl(self, q, c, a_eq, b_eq, a_ineq, b_ineq, options)
}
QpMethod::InteriorPoint => {
interior_point_qp_impl(self, q, c, a_eq, b_eq, a_ineq, b_ineq, options)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use numr::runtime::cpu::CpuDevice;
fn setup() -> (CpuDevice, CpuClient) {
let device = CpuDevice::new();
let client = CpuClient::new(device.clone());
(device, client)
}
#[test]
fn test_qp_unconstrained() {
let (device, client) = setup();
let q = Tensor::<CpuRuntime>::from_slice(&[1.0, 0.0, 0.0, 1.0], &[2, 2], &device);
let c = Tensor::<CpuRuntime>::from_slice(&[-1.0, -1.0], &[2], &device);
let result = client
.solve_qp(&q, &c, None, None, None, None, &QpOptions::default())
.unwrap();
assert!(result.converged);
let sol: Vec<f64> = result.x.to_vec();
assert!((sol[0] - 1.0).abs() < 1e-6, "x[0] = {}", sol[0]);
assert!((sol[1] - 1.0).abs() < 1e-6, "x[1] = {}", sol[1]);
assert!((result.fun - (-1.0)).abs() < 1e-6, "fun = {}", result.fun);
}
#[test]
fn test_qp_equality_constrained() {
let (device, client) = setup();
let q = Tensor::<CpuRuntime>::from_slice(&[1.0, 0.0, 0.0, 1.0], &[2, 2], &device);
let c = Tensor::<CpuRuntime>::from_slice(&[0.0, 0.0], &[2], &device);
let a_eq = Tensor::<CpuRuntime>::from_slice(&[1.0, 1.0], &[1, 2], &device);
let b_eq = Tensor::<CpuRuntime>::from_slice(&[1.0], &[1], &device);
let result = client
.solve_qp(
&q,
&c,
Some(&a_eq),
Some(&b_eq),
None,
None,
&QpOptions::default(),
)
.unwrap();
assert!(result.converged);
let sol: Vec<f64> = result.x.to_vec();
assert!((sol[0] - 0.5).abs() < 1e-4, "x[0] = {}", sol[0]);
assert!((sol[1] - 0.5).abs() < 1e-4, "x[1] = {}", sol[1]);
}
#[test]
fn test_qp_inequality_constrained() {
let (device, client) = setup();
let q = Tensor::<CpuRuntime>::from_slice(&[1.0, 0.0, 0.0, 1.0], &[2, 2], &device);
let c = Tensor::<CpuRuntime>::from_slice(&[-1.0, -1.0], &[2], &device);
let a_ineq =
Tensor::<CpuRuntime>::from_slice(&[1.0, 1.0, 1.0, 0.0, 0.0, 1.0], &[3, 2], &device);
let b_ineq = Tensor::<CpuRuntime>::from_slice(&[0.0, 0.0, 0.0], &[3], &device);
let result = client
.solve_qp(
&q,
&c,
None,
None,
Some(&a_ineq),
Some(&b_ineq),
&QpOptions::default(),
)
.unwrap();
let sol: Vec<f64> = result.x.to_vec();
assert!((sol[0] - 1.0).abs() < 0.1, "x[0] = {}", sol[0]);
assert!((sol[1] - 1.0).abs() < 0.1, "x[1] = {}", sol[1]);
}
}