1use super::boc::{Channel, Kinematics, Order};
4use super::convolutions::ConvolutionCache;
5use super::error::{Error, Result};
6use super::grid::{Grid, GridOptFlags};
7use super::pids::{OptRules, PidBasis};
8use super::subgrid::{self, EmptySubgridV1, Subgrid};
9use ndarray::{s, ArrayD};
10use std::collections::BTreeMap;
11use std::fmt::{self, Display, Formatter};
12use std::iter;
13use std::str::FromStr;
14
15#[repr(transparent)]
26pub struct FkTable {
27 grid: Grid,
28}
29
30#[repr(C)]
35#[derive(Debug, Clone, Copy, Eq, PartialEq)]
36pub enum FkAssumptions {
37 Nf6Ind,
39 Nf6Sym,
42 Nf5Ind,
45 Nf5Sym,
48 Nf4Ind,
51 Nf4Sym,
55 Nf3Ind,
58 Nf3Sym,
61}
62
63impl Display for FkAssumptions {
64 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
65 write!(
66 f,
67 "{}",
68 match self {
69 Self::Nf6Ind => "Nf6Ind",
70 Self::Nf6Sym => "Nf6Sym",
71 Self::Nf5Ind => "Nf5Ind",
72 Self::Nf5Sym => "Nf5Sym",
73 Self::Nf4Ind => "Nf4Ind",
74 Self::Nf4Sym => "Nf4Sym",
75 Self::Nf3Ind => "Nf3Ind",
76 Self::Nf3Sym => "Nf3Sym",
77 }
78 )
79 }
80}
81
82impl FromStr for FkAssumptions {
83 type Err = Error;
84
85 fn from_str(s: &str) -> Result<Self> {
86 Ok(match s {
87 "Nf6Ind" => Self::Nf6Ind,
88 "Nf6Sym" => Self::Nf6Sym,
89 "Nf5Ind" => Self::Nf5Ind,
90 "Nf5Sym" => Self::Nf5Sym,
91 "Nf4Ind" => Self::Nf4Ind,
92 "Nf4Sym" => Self::Nf4Sym,
93 "Nf3Ind" => Self::Nf3Ind,
94 "Nf3Sym" => Self::Nf3Sym,
95 _ => {
96 return Err(Error::General(format!(
97 "unknown variant for FkAssumptions: {s}",
98 )));
99 }
100 })
101 }
102}
103
104impl FkTable {
105 #[must_use]
107 pub const fn grid(&self) -> &Grid {
108 &self.grid
109 }
110
111 #[must_use]
117 pub fn into_grid(self) -> Grid {
118 self.grid
119 }
120
121 #[must_use]
128 pub fn table(&self) -> ArrayD<f64> {
129 let x_grid = self.x_grid();
130
131 let mut dim = vec![self.grid.bwfl().len(), self.grid.channels().len()];
132 dim.extend(iter::repeat(x_grid.len()).take(self.grid.convolutions().len()));
133 let mut idx = vec![0; dim.len()];
134 let mut result = ArrayD::zeros(dim);
135
136 for ((_, bin, channel), subgrid) in
137 self.grid().subgrids().indexed_iter().filter(|(_, subgrid)|
138 !subgrid.is_empty())
140 {
141 let indices: Vec<Vec<_>> = self
142 .grid
143 .convolutions()
144 .iter()
145 .enumerate()
146 .map(|(index, _)| {
147 subgrid
148 .node_values()
149 .iter()
150 .zip(self.grid.kinematics())
151 .find_map(|(node_values, kin)| {
152 matches!(kin, Kinematics::X(i) if *i == index).then(|| {
153 node_values
154 .iter()
155 .map(|&s| {
156 x_grid
157 .iter()
158 .position(|&x| subgrid::node_value_eq(s, x))
159 .unwrap()
161 })
162 .collect()
163 })
164 })
165 .unwrap()
167 })
168 .collect();
169
170 for (index, value) in subgrid.indexed_iter() {
171 assert_eq!(index[0], 0);
172 idx[0] = bin;
173 idx[1] = channel;
174 for i in 2..result.shape().len() {
175 idx[i] = indices[i - 2][index[i - 1]];
176 }
177 result[idx.as_slice()] = value;
178 }
179 }
180
181 result
182 }
183
184 #[must_use]
186 pub fn channels(&self) -> Vec<Vec<i32>> {
187 self.grid
188 .channels()
189 .iter()
190 .map(|entry| entry.entry()[0].0.clone())
191 .collect()
192 }
193
194 #[must_use]
196 pub fn fac0(&self) -> Option<f64> {
197 let fac1 = self.grid.evolve_info(&[true]).fac1;
198
199 if let [fac0] = fac1[..] {
200 Some(fac0)
201 } else {
202 assert!(fac1.is_empty());
204
205 None
206 }
207 }
208
209 #[must_use]
211 pub fn frg0(&self) -> Option<f64> {
212 let frg1 = self.grid.evolve_info(&[true]).frg1;
213
214 if let [frg0] = frg1[..] {
215 Some(frg0)
216 } else {
217 assert!(frg1.is_empty());
219
220 None
221 }
222 }
223
224 pub fn metadata_mut(&mut self) -> &mut BTreeMap<String, String> {
226 self.grid.metadata_mut()
227 }
228
229 #[must_use]
231 pub fn x_grid(&self) -> Vec<f64> {
232 self.grid.evolve_info(&[true]).x1
233 }
234
235 pub fn convolve(
238 &self,
239 convolution_cache: &mut ConvolutionCache,
240 bin_indices: &[usize],
241 channel_mask: &[bool],
242 ) -> Vec<f64> {
243 self.grid.convolve(
244 convolution_cache,
245 &[],
246 bin_indices,
247 channel_mask,
248 &[(1.0, 1.0, 1.0)],
249 )
250 }
251
252 pub fn rotate_pid_basis(&mut self, pid_basis: PidBasis) {
254 self.grid.rotate_pid_basis(pid_basis);
255 self.grid.split_channels();
256 self.grid.merge_channel_factors();
257 self.grid.optimize_using(GridOptFlags::all());
258 }
259
260 pub fn optimize(&mut self, assumptions: FkAssumptions) {
263 let OptRules(sum, delete) = self.grid.pid_basis().opt_rules(assumptions);
264
265 for idx in 0..self.grid.channels().len() {
266 let &[(ref pids, factor)] = self.grid.channels()[idx].entry() else {
267 unreachable!()
269 };
270 let mut pids = pids.clone();
271
272 for pid in &mut pids {
273 if delete.contains(pid) {
274 for subgrid in self.grid.subgrids_mut().slice_mut(s![.., .., idx]) {
275 *subgrid = EmptySubgridV1.into();
276 }
277 } else if let Some(replace) = sum
278 .iter()
279 .find_map(|&(search, replace)| (*pid == search).then_some(replace))
280 {
281 *pid = replace;
282 }
283 }
284
285 self.grid.channels_mut()[idx] = Channel::new(vec![(pids, factor)]);
286 }
287
288 self.grid.optimize();
289
290 self.grid
292 .metadata_mut()
293 .insert("fk_assumptions".to_owned(), assumptions.to_string());
294 }
295}
296
297impl TryFrom<Grid> for FkTable {
298 type Error = Error;
299
300 fn try_from(grid: Grid) -> Result<Self> {
301 let mut muf2 = -1.0;
302
303 if grid.orders()
304 != [Order {
305 alphas: 0,
306 alpha: 0,
307 logxir: 0,
308 logxif: 0,
309 logxia: 0,
310 }]
311 {
312 return Err(Error::General("multiple orders detected".to_owned()));
313 }
314
315 for subgrid in grid.subgrids() {
316 if subgrid.is_empty() {
317 continue;
318 }
319
320 let [fac] = grid
321 .scales()
322 .fac
323 .calc(&subgrid.node_values(), grid.kinematics())[..]
324 else {
325 return Err(Error::General("multiple scales detected".to_owned()));
326 };
327
328 if muf2 < 0.0 {
329 muf2 = fac;
330 } else if !subgrid::node_value_eq(muf2, fac) {
331 return Err(Error::General("multiple scales detected".to_owned()));
332 }
333 }
334
335 for channel in grid.channels() {
336 let entry = channel.entry();
337
338 if entry.len() != 1 || !subgrid::node_value_eq(entry[0].1, 1.0) {
339 return Err(Error::General(
340 "complicated channel function detected".to_owned(),
341 ));
342 }
343 }
344
345 if (1..grid.channels().len())
346 .any(|i| grid.channels()[i..].contains(&grid.channels()[i - 1]))
347 {
348 return Err(Error::General(
349 "complicated channel function detected".to_owned(),
350 ));
351 }
352
353 Ok(Self { grid })
354 }
355}
356
357#[cfg(test)]
358mod tests {
359 use super::*;
360
361 #[test]
362 fn fk_assumptions_try_from() {
363 assert_eq!(
364 FkAssumptions::from_str("Nf6Ind").unwrap(),
365 FkAssumptions::Nf6Ind
366 );
367 assert_eq!(
368 FkAssumptions::from_str("Nf6Sym").unwrap(),
369 FkAssumptions::Nf6Sym
370 );
371 assert_eq!(
372 FkAssumptions::from_str("Nf5Ind").unwrap(),
373 FkAssumptions::Nf5Ind
374 );
375 assert_eq!(
376 FkAssumptions::from_str("Nf5Sym").unwrap(),
377 FkAssumptions::Nf5Sym
378 );
379 assert_eq!(
380 FkAssumptions::from_str("Nf4Ind").unwrap(),
381 FkAssumptions::Nf4Ind
382 );
383 assert_eq!(
384 FkAssumptions::from_str("Nf4Sym").unwrap(),
385 FkAssumptions::Nf4Sym
386 );
387 assert_eq!(
388 FkAssumptions::from_str("Nf3Ind").unwrap(),
389 FkAssumptions::Nf3Ind
390 );
391 assert_eq!(
392 FkAssumptions::from_str("Nf3Sym").unwrap(),
393 FkAssumptions::Nf3Sym
394 );
395 assert_eq!(
396 FkAssumptions::from_str("XXXXXX").unwrap_err().to_string(),
397 "unknown variant for FkAssumptions: XXXXXX"
398 );
399 }
400
401 #[test]
402 fn fk_assumptions_display() {
403 assert_eq!(format!("{}", FkAssumptions::Nf6Ind), "Nf6Ind");
404 assert_eq!(format!("{}", FkAssumptions::Nf6Sym), "Nf6Sym");
405 assert_eq!(format!("{}", FkAssumptions::Nf5Ind), "Nf5Ind");
406 assert_eq!(format!("{}", FkAssumptions::Nf5Sym), "Nf5Sym");
407 assert_eq!(format!("{}", FkAssumptions::Nf4Ind), "Nf4Ind");
408 assert_eq!(format!("{}", FkAssumptions::Nf4Sym), "Nf4Sym");
409 assert_eq!(format!("{}", FkAssumptions::Nf3Ind), "Nf3Ind");
410 assert_eq!(format!("{}", FkAssumptions::Nf3Sym), "Nf3Sym");
411 }
412}