raddy/sparse/objective.rs
1use crate::{make::var, types::advec, Ad};
2use faer::{
3 sparse::{CreationError, SparseColMat},
4 Col,
5};
6use itertools::Itertools;
7
8/// Represents the computed results of an objective function evaluation
9/// including the function value, gradient, and Hessian triplets.
10///
11/// ## Type Parameters
12/// - `N`: The problem size/dimension of a single objective
13///
14/// ## Fields
15/// - `value`: The computed objective function value
16/// - `grad`: The gradient vector (first derivatives)
17/// - `hess_trips`: Hessian matrix entries stored as (row, col, value) triplets
18pub struct ComputedObjective<const N: usize> {
19 pub value: f64,
20 pub grad: Col<f64>,
21 pub hess_trips: Vec<(usize, usize, f64)>,
22}
23
24/// Defines the interface for sparse objective functions
25///
26/// ## Type Parameters
27/// - `N`: The problem size/dimension of a single objective
28///
29/// ## Associated Types
30/// - `EvalArgs`: Additional arguments needed for objective evaluation
31///
32/// ## Example
33/// ```ignore
34/// struct MyObjective;
35///
36/// impl Objective<3> for MyObjective {
37/// type EvalArgs = f64; // Example args type
38///
39/// fn eval(&self, variables: &advec<3, 3>, args: &f64) -> Ad<3> {
40/// todo!("Your implementation")
41/// }
42/// }
43/// ```
44pub trait Objective<const N: usize> {
45 type EvalArgs;
46
47 /// Evaluates the objective function for given variables
48 ///
49 /// ## Arguments
50 /// - `variables`: The input variables as an advec
51 /// - `args`: Additional evaluation arguments
52 ///
53 /// ## Returns
54 /// An `Ad<N>` containing the function value, gradient and Hessian
55 fn eval(&self, variables: &advec<N, N>, args: &Self::EvalArgs) -> Ad<N>;
56
57 /// Helper method to evaluate objective for given indices
58 ///
59 /// ## Arguments
60 /// - `global_inds`: Global indices of variables to evaluate
61 /// - `x`: The full variable vector
62 /// - `args`: Additional evaluation arguments
63 ///
64 /// ## Returns
65 /// An `Ad<N>` containing the local evaluation results
66 fn evaluate_for_indices(
67 &self,
68 global_inds: [usize; N],
69 x: &Col<f64>,
70 args: &Self::EvalArgs,
71 ) -> Ad<N> {
72 let vals = global_inds.map(|i| x[i]);
73 let vals_slice = vals.as_slice();
74 let vars = var::vector_from_slice(vals_slice);
75 self.eval(&vars, args)
76 }
77
78 /// Computes value, gradient and Hessian triplets in one operation
79 ///
80 /// ## Arguments
81 /// - `x`: The full variable vector, may be large
82 /// - `operand_indices`: Slice of indices of variables to evaluate
83 /// - `args`: Additional evaluation arguments
84 ///
85 /// ## Returns
86 /// A `ComputedObjective<N>` containing all computed results
87 fn compute(
88 &self,
89 x: &Col<f64>,
90 operand_indices: &[[usize; N]],
91 args: &Self::EvalArgs,
92 ) -> ComputedObjective<N> {
93 let mut value = 0.0;
94 let mut grad = Col::zeros(x.nrows());
95 let mut hess_trips = Vec::new();
96
97 for &global_inds in operand_indices {
98 let obj = self.evaluate_for_indices(global_inds, x, args);
99
100 let ind = global_inds.into_iter().enumerate();
101
102 value += obj.value;
103
104 ind.clone()
105 .for_each(|(ilocal, iglobal)| grad[iglobal] += obj.grad[ilocal]);
106
107 ind.clone().cartesian_product(ind).for_each(
108 |((ixlocal, ixglobal), (iylocal, iyglobal))| {
109 hess_trips.push((ixglobal, iyglobal, obj.hess[(ixlocal, iylocal)]));
110 },
111 );
112 }
113
114 ComputedObjective {
115 value,
116 grad,
117 hess_trips,
118 }
119 }
120
121 /// Computes just the objective function value
122 ///
123 /// ## Arguments
124 /// - `x`: The full variable vector
125 /// - `operand_indices`: Slice of indices of variables to evaluate
126 /// - `args`: Additional evaluation arguments
127 ///
128 /// ## Returns
129 /// The computed objective function value
130 fn value(&self, x: &Col<f64>, operand_indices: &[[usize; N]], args: &Self::EvalArgs) -> f64 {
131 let mut res = 0.0;
132
133 operand_indices.iter().for_each(|&ind| {
134 let obj = self.evaluate_for_indices(ind, x, args);
135 res += obj.value;
136 });
137
138 res
139 }
140
141 /// Computes just the gradient vector
142 ///
143 /// ## Arguments
144 /// - `x`: The full variable vector
145 /// - `operand_indices`: Slice of indices of variables to evaluate
146 /// - `args`: Additional evaluation arguments
147 ///
148 /// ## Returns
149 /// The computed gradient vector
150 fn grad(
151 &self,
152 x: &Col<f64>,
153 operand_indices: &[[usize; N]],
154 args: &Self::EvalArgs,
155 ) -> Col<f64> {
156 let mut res = Col::zeros(x.nrows());
157
158 operand_indices.iter().for_each(|&ind| {
159 let obj = self.evaluate_for_indices(ind, x, args);
160 ind.into_iter()
161 .enumerate()
162 .for_each(|(ilocal, iglobal)| res[iglobal] += obj.grad[ilocal]);
163 });
164
165 res
166 }
167
168 /// Computes Hessian matrix entries as triplets
169 ///
170 /// ## Arguments
171 /// - `x`: The full variable vector
172 /// - `operand_indices`: Slice of indices of variables to evaluate
173 /// - `args`: Additional evaluation arguments
174 ///
175 /// ## Returns
176 /// Vector of (row, col, value) triplets representing the Hessian matrix
177 fn hess_trips(
178 &self,
179 x: &Col<f64>,
180 operand_indices: &[[usize; N]],
181 args: &Self::EvalArgs,
182 ) -> Vec<(usize, usize, f64)> {
183 let mut trips = Vec::new();
184
185 operand_indices.iter().for_each(|&ind| {
186 let obj = self.evaluate_for_indices(ind, x, args);
187 let ind = ind.into_iter().enumerate();
188
189 ind.clone().cartesian_product(ind).for_each(
190 |((ixlocal, ixglobal), (iylocal, iyglobal))| {
191 trips.push((ixglobal, iyglobal, obj.hess[(ixlocal, iylocal)]));
192 },
193 );
194 });
195
196 trips
197 }
198
199 /// Computes the Hessian matrix as a sparse matrix
200 ///
201 /// ## Arguments
202 /// - `x`: The full variable vector
203 /// - `operand_indices`: Slice of indices of variables to evaluate
204 /// - `args`: Additional evaluation arguments
205 ///
206 /// ## Returns
207 /// A sparse matrix representation of the Hessian
208 fn hess(
209 &self,
210 x: &Col<f64>,
211 operand_indices: &[[usize; N]],
212 args: &Self::EvalArgs,
213 ) -> Result<SparseColMat<usize, f64>, CreationError> {
214 let n = x.nrows();
215 SparseColMat::try_new_from_triplets(n, n, &self.hess_trips(x, operand_indices, args))
216 }
217}