pounce-nlp 0.4.0

NLP-side glue for POUNCE (port of Ipopt's src/Interfaces): TNLP trait, TNLPAdapter, NLP / IpoptNLP wrappers, return-code enums, IpoptApplication user-facing entry point.
Documentation

pounce-nlp

NLP-side glue for POUNCE. Port of Ipopt's src/Interfaces/. This is the crate Rust users implement against when writing custom NLP problems.

What's in it

  • The user-facing TNLP trait for problem definition (port of IpTNLP.{hpp,cpp}).
  • Return-code enums ApplicationReturnStatus and AlgorithmMode (port of IpReturnCodes_inc.h).
  • SolverReturn (algorithm-side, port of IpAlgTypes.hpp).
  • SolveStatistics per-solve counters.
  • TNLPAdapter and OrigIpoptNlp, the bound/constraint splitter chain that feeds the algorithm-side IPM.

The user-facing IpoptApplication lives in pounce-algorithm (optimize_tnlp orchestrates the algorithm), so pounce-nlp itself stays free of algorithm-side imports. Wire-up direction is pounce-algorithm → pounce-nlp.

Implementing a TNLP

The required methods describe the problem; defaults supply the "do-nothing" behaviour for everything else. A minimal solver call:

use std::cell::RefCell;
use std::rc::Rc;

use pounce_algorithm::application::IpoptApplication;
use pounce_nlp::tnlp::{
    BoundsInfo, IndexStyle, IpoptCq, IpoptData, NlpInfo, Solution,
    SparsityRequest, StartingPoint, TNLP,
};
use pounce_common::types::Number;

/// min (x[0]-3)^2 + (x[1]-4)^2, unconstrained. Optimum (3, 4).
struct Quadratic;

impl TNLP for Quadratic {
    fn get_nlp_info(&mut self) -> Option<NlpInfo> {
        Some(NlpInfo {
            n: 2, m: 0, nnz_jac_g: 0, nnz_h_lag: 2,
            index_style: IndexStyle::C,
        })
    }
    fn get_bounds_info(&mut self, b: BoundsInfo<'_>) -> bool {
        b.x_l.iter_mut().for_each(|v| *v = -1e19);
        b.x_u.iter_mut().for_each(|v| *v =  1e19);
        true
    }
    fn get_starting_point(&mut self, sp: StartingPoint<'_>) -> bool {
        sp.x.iter_mut().for_each(|v| *v = 0.0);
        true
    }
    fn eval_f(&mut self, x: &[Number], _: bool) -> Option<Number> {
        Some((x[0] - 3.0).powi(2) + (x[1] - 4.0).powi(2))
    }
    fn eval_grad_f(&mut self, x: &[Number], _: bool, g: &mut [Number]) -> bool {
        g[0] = 2.0 * (x[0] - 3.0);
        g[1] = 2.0 * (x[1] - 4.0);
        true
    }
    fn eval_g(&mut self, _: &[Number], _: bool, _: &mut [Number]) -> bool { true }
    fn eval_jac_g(&mut self, _: Option<&[Number]>, _: bool, _: SparsityRequest<'_>) -> bool { true }
    fn finalize_solution(&mut self, _: Solution<'_>, _: &IpoptData, _: &IpoptCq) {}
}

let mut app = IpoptApplication::new();
app.initialize().unwrap();
let status = app.optimize_tnlp(Rc::new(RefCell::new(Quadratic)));
assert_eq!(status as i32, 0); // Solve_Succeeded

A second built-in example (rosenbrock) ships in pounce-cli's builtin module.

Sparsity convention

SparsityRequest carries either an (irow, jcol) structure call or a vals values call. POUNCE accepts both 0-based (IndexStyle::C) and 1-based (IndexStyle::Fortran) triplets — same as upstream.

License

EPL-2.0.