Skip to content

Commit dc47e2a

Browse files
Merge pull request #359 from NNPDF/capi-set-subgrid
Add `set_subgrid` interface to the C-API
2 parents 41e8af5 + 6f4c6f9 commit dc47e2a

4 files changed

Lines changed: 330 additions & 2 deletions

File tree

examples/cpp/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ PROGRAMS = \
1414
convolve-grid-deprecated \
1515
convolve-grid \
1616
get-subgrids \
17+
set-subgrids \
1718
evolve-grid \
1819
evolve-grid-identity \
1920
deprecated \
@@ -57,6 +58,9 @@ deprecated: deprecated.cpp
5758
get-subgrids: get-subgrids.cpp
5859
$(CXX) $(CXXFLAGS) $< $(PINEAPPL_DEPS) -o $@
5960

61+
set-subgrids: set-subgrids.cpp
62+
$(CXX) $(CXXFLAGS) $< $(PINEAPPL_DEPS) -o $@
63+
6064
evolve-grid: evolve-grid.cpp
6165
$(CXX) $(CXXFLAGS) $< $(LHAPDF_DEPS) $(PINEAPPL_DEPS) -o $@
6266

examples/cpp/set-subgrids.cpp

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
#include <cstdint>
2+
#include <pineappl_capi.h>
3+
4+
#include <cassert>
5+
#include <cmath>
6+
#include <cstddef>
7+
#include <iostream>
8+
#include <random>
9+
#include <string>
10+
#include <tuple>
11+
#include <vector>
12+
13+
using KinematicsTuple = std::tuple<double, double, double>;
14+
15+
struct KinInterpolation {
16+
std::vector<double> x_interp;
17+
std::vector<double> z_interp;
18+
};
19+
20+
template<typename T>
21+
std::vector<T> geomspace(T start, T stop, int num, bool endpoint = false) {
22+
std::vector<T> result(num);
23+
24+
if (num == 1) {
25+
result[0] = start;
26+
return result;
27+
}
28+
29+
T log_start = std::log(start);
30+
T log_stop = std::log(stop);
31+
T step = (log_stop - log_start) / (endpoint ? (num - 1) : num);
32+
33+
for (int i = 0; i < num; ++i) {
34+
result[i] = std::exp(log_start + i * step);
35+
}
36+
37+
return result;
38+
}
39+
40+
KinInterpolation kinematics_interpolation_points() {
41+
std::vector<double> x = geomspace(1e-5, 1.0, 50);
42+
std::vector<double> z = geomspace(1e-5, 1.0, 50);
43+
44+
return {x, x};
45+
}
46+
47+
std::vector<double> generate_subgrid_arrays(
48+
std::mt19937& rng,
49+
std::vector<double> x,
50+
std::vector<double> z
51+
) {
52+
std::size_t kin_length = x.size() * z.size();
53+
std::vector<double> subgrid(kin_length);
54+
55+
// NOTE: `subgrid` is a flatten matrix whose layout was `[q2=1][x][z]`.
56+
// The order of the kinematics shoud match the kinematics declaration.
57+
for (std::size_t i = 0; i != kin_length; i++) {
58+
subgrid[i] = std::generate_canonical<double, 53>(rng);
59+
}
60+
61+
return subgrid;
62+
}
63+
64+
void fill_grid(pineappl_grid* grid, std::vector<KinematicsTuple> kin_tuples) {
65+
auto rng = std::mt19937();
66+
67+
auto* channels = pineappl_grid_channels(grid);
68+
std::size_t n_bins = pineappl_grid_bin_count(grid);
69+
std::size_t n_orders = pineappl_grid_order_count(grid);
70+
std::size_t n_channels = pineappl_channels_count(channels);
71+
72+
// Get the kinematics
73+
KinInterpolation kins = kinematics_interpolation_points();
74+
std::vector<double> x_interp = kins.x_interp;
75+
std::vector<double> z_interp = kins.z_interp;
76+
77+
// Extract the shape of the subgrid - Q2 always passed as an array of ONE element
78+
std::vector<std::size_t> subgrid_shape = {1, x_interp.size(), z_interp.size()};
79+
80+
for (std::size_t b = 0; b != n_bins; b++) {
81+
for (std::size_t o = 0; o != n_orders; o++) {
82+
for (std::size_t c = 0; c != n_channels; c++) {
83+
// Construct the node values of {Q2, x_inter, z_interp}
84+
// NOTE: Pay attention to the order, it should match the kinematics declaration
85+
// and how the subgrid was constructed (see `generate_subgrid_array`).
86+
std::vector<double> node_values = { std::get<0>(kin_tuples[b]) };
87+
node_values.insert(node_values.end(), x_interp.begin(), x_interp.end());
88+
node_values.insert(node_values.end(), z_interp.begin(), z_interp.end());
89+
90+
// Mock the subgrids for a given bin, order, and channel
91+
std::vector<double> subgrid_arrays = generate_subgrid_arrays(rng, x_interp, z_interp);
92+
93+
// set the subgrids
94+
pineappl_grid_set_subgrid(
95+
grid,
96+
b, o, c, // kinematics index
97+
node_values.data(),
98+
subgrid_arrays.data(),
99+
subgrid_shape.data()
100+
);
101+
}
102+
}
103+
}
104+
105+
// Remove channels object from memory
106+
pineappl_channels_delete(channels);
107+
}
108+
109+
int main() {
110+
// ---
111+
// Create all channels
112+
113+
std::size_t nb_convolutions = 2;
114+
auto* channels = pineappl_channels_new(nb_convolutions);
115+
116+
int32_t pids1[] = { 21, 21 };
117+
double factors1[] = { 1.0 };
118+
// define the channel #0
119+
pineappl_channels_add(channels, 1, pids1, factors1);
120+
121+
// create another channel this channel is the down-type-antidown-type quark channel; here we
122+
int32_t pids2[] = { 1, -1, 3, -3, 5, -5 };
123+
// define the channel #1
124+
pineappl_channels_add(channels, 3, pids2, nullptr);
125+
126+
// ---
127+
// Specify the perturbative orders that will be filled into the grid
128+
std::vector<uint8_t> orders = {
129+
1, 0, 0, 0, 0, // order #0: LO QCD
130+
2, 0, 0, 0, 0, // order #1: NLO QCD
131+
};
132+
133+
// ---
134+
// Specify the bin limits
135+
136+
// In SIDIS, a bin is defined as a tuple (Q2, x, z) values (3D).
137+
std::vector<KinematicsTuple> kin_obs = {std::make_tuple(1e3, 1e-5, 1e-2), std::make_tuple(1e4, 1e-2, 1e-3)};
138+
// We are going to define some placeholder 1D bins that we'll overwrite later.
139+
std::vector<double> bins;
140+
for (std::size_t i = 0; i < kin_obs.size() + 1; ++i) {
141+
bins.push_back(static_cast<float>(i));
142+
}
143+
144+
// ---
145+
// Construct the objects that are needed to fill the Grid
146+
147+
pineappl_pid_basis pid_basis = PINEAPPL_PID_BASIS_EVOL;
148+
pineappl_conv convs[] = {
149+
{ PINEAPPL_CONV_TYPE_UNPOL_PDF, 2212 },
150+
{ PINEAPPL_CONV_TYPE_UNPOL_FF, 211 }, // Assumes Pion
151+
};
152+
153+
// Define the kinematics required for this process. In the following example we have ONE single
154+
// scale and two momentum fractions (corresponding to the two initial- and final-state hadrons).
155+
// The format of the kinematics is: { type, value }.
156+
pineappl_kinematics scales = { PINEAPPL_KINEMATICS_SCALE, 0 };
157+
pineappl_kinematics x1 = { PINEAPPL_KINEMATICS_X, 0 };
158+
pineappl_kinematics x2 = { PINEAPPL_KINEMATICS_X, 1 };
159+
pineappl_kinematics kinematics[3] = { scales, x1, x2 };
160+
161+
// Define the specificities of the interpolations for each of the kinematic variables.
162+
pineappl_reweight_meth scales_reweight = PINEAPPL_REWEIGHT_METH_NO_REWEIGHT; // Reweighting method
163+
pineappl_reweight_meth moment_reweight = PINEAPPL_REWEIGHT_METH_APPL_GRID_X;
164+
pineappl_map scales_mapping = PINEAPPL_MAP_APPL_GRID_H0; // Mapping method
165+
pineappl_map moment_mapping = PINEAPPL_MAP_APPL_GRID_F2;
166+
pineappl_interp_meth interpolation_meth = PINEAPPL_INTERP_METH_LAGRANGE;
167+
pineappl_interp interpolations[3] = {
168+
{ 1e2, 1e8, 40, 3, scales_reweight, scales_mapping, interpolation_meth }, // Interpolation fo `scales`
169+
{ 2e-7, 1.0, 50, 3, moment_reweight, moment_mapping, interpolation_meth }, // Interpolation fo `x1`
170+
{ 2e-7, 1.0, 50, 3, moment_reweight, moment_mapping, interpolation_meth }, // Interpolation fo `x2`
171+
};
172+
173+
// Define the unphysical scale objects
174+
pineappl_scale_func_form scale_mu = { PINEAPPL_SCALE_FUNC_FORM_SCALE, 0 };
175+
pineappl_scale_func_form mu_scales[3] = { scale_mu, scale_mu, scale_mu };
176+
177+
// ---
178+
// Create the grid using the previously set information about orders, bins and channels
179+
180+
auto* grid = pineappl_grid_new2(bins.size() - 1, bins.data(), orders.size() / 5, orders.data(),
181+
channels, pid_basis, convs, 3, interpolations, kinematics, mu_scales);
182+
183+
pineappl_channels_delete(channels);
184+
185+
// ---
186+
// Fill the grid with phase-space points
187+
fill_grid(grid, kin_obs);
188+
189+
// ---
190+
// NOTE: We now have to remap the bins to 3D
191+
192+
// We need to flatten the array of `KinematicsTuple` and define the normalizations
193+
std::vector<double> flat_kin_obs;
194+
for (const auto& kin : kin_obs) {
195+
flat_kin_obs.push_back(std::get<0>(kin));
196+
flat_kin_obs.push_back(std::get<1>(kin));
197+
flat_kin_obs.push_back(std::get<2>(kin));
198+
}
199+
std::vector<double> normalizations(bins.size() - 1, 1.0);
200+
pineappl_grid_set_bwfl(
201+
grid,
202+
flat_kin_obs.data(), // lower bin limits
203+
flat_kin_obs.data(), // upper bin limits
204+
bins.size() - 1, // number of bins
205+
3, // Dimension of the bins (Q2, x, z)
206+
normalizations.data()
207+
);
208+
pineappl_grid_optimize(grid);
209+
210+
// ---
211+
// Write the grid to disk - the filename can be anything ...
212+
std::string filename = "sidis-toygrid.pineappl.lz4";
213+
pineappl_grid_write(grid, filename.c_str());
214+
215+
// destroy the object
216+
pineappl_grid_delete(grid);
217+
218+
std::cout << "Generated " << filename << " containing a toy SIDIS.\n\n"
219+
"Try running the following command to check the bins:\n"
220+
" - pineappl read --bins " << filename << "\n";
221+
}

examples/cpp/set-subgrids.output

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Generated sidis-toygrid.pineappl.lz4 containing a toy SIDIS.
2+
3+
Try running the following command to check the bins:
4+
- pineappl read --bins sidis-toygrid.pineappl.lz4

pineappl_capi/src/lib.rs

Lines changed: 101 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,10 @@ use pineappl::evolution::{AlphasTable, OperatorSliceInfo};
6363
use pineappl::fk_table::{FkAssumptions, FkTable};
6464
use pineappl::grid::{Grid, GridOptFlags};
6565
use pineappl::interpolation::{Interp as InterpMain, InterpMeth, Map, ReweightMeth};
66-
use pineappl::packed_array::ravel_multi_index;
66+
use pineappl::packed_array::PackedArray;
67+
use pineappl::packed_array::{ravel_multi_index, unravel_index};
6768
use pineappl::pids::PidBasis;
68-
use pineappl::subgrid::Subgrid;
69+
use pineappl::subgrid::{ImportSubgridV1, Subgrid};
6970
use std::collections::HashMap;
7071
use std::ffi::{CStr, CString};
7172
use std::fs::File;
@@ -2114,6 +2115,104 @@ pub unsafe extern "C" fn pineappl_grid_subgrid_array(
21142115
}
21152116
}
21162117

2118+
/// Set the subgrid of a Grid for a given bin, order, and channel.
2119+
///
2120+
/// # Safety
2121+
///
2122+
/// If `grid` does not point to a valid `Grid` object, for example when `grid` is the null pointer,
2123+
/// this function is not safe to call.
2124+
#[no_mangle]
2125+
pub unsafe extern "C" fn pineappl_grid_set_subgrid(
2126+
grid: *mut Grid,
2127+
bin: usize,
2128+
order: usize,
2129+
channel: usize,
2130+
node_values: *mut f64,
2131+
subgrid_array: *mut f64,
2132+
subgrid_shape: *mut usize,
2133+
) {
2134+
let grid = unsafe { &mut *grid };
2135+
let num_kins = grid.kinematics().len();
2136+
2137+
let subgrid_shape = unsafe { slice::from_raw_parts(subgrid_shape, num_kins) };
2138+
let subgrid_array =
2139+
unsafe { slice::from_raw_parts(subgrid_array, subgrid_shape.iter().product()) };
2140+
2141+
let node_values: Vec<Vec<f64>> = {
2142+
let mut offset = 0;
2143+
subgrid_shape
2144+
.iter()
2145+
.map(|&dim_size| {
2146+
let dim_nodes =
2147+
unsafe { slice::from_raw_parts(node_values.add(offset), dim_size) }.to_vec();
2148+
offset += dim_size;
2149+
dim_nodes
2150+
})
2151+
.collect()
2152+
};
2153+
2154+
let mut sparse_array: PackedArray<f64> =
2155+
PackedArray::new(node_values.iter().map(Vec::len).collect());
2156+
2157+
for (index, value) in subgrid_array
2158+
.iter()
2159+
.enumerate()
2160+
.filter(|(_, value)| **value != 0.0)
2161+
{
2162+
let index_unravel = unravel_index(index, subgrid_shape);
2163+
sparse_array[index_unravel.as_slice()] = *value;
2164+
}
2165+
2166+
let subgrid = ImportSubgridV1::new(sparse_array, node_values);
2167+
grid.subgrids_mut()[[order, bin, channel]] = subgrid.into();
2168+
}
2169+
2170+
/// Redefine the bin representation of the Grid; generalization of `pineappl_grid_set_remapper`.
2171+
///
2172+
/// # Panics
2173+
///
2174+
/// TODO
2175+
///
2176+
/// # Safety
2177+
///
2178+
/// If `grid` does not point to a valid `Grid` object, for example when `grid` is the null pointer,
2179+
/// this function is not safe to call.
2180+
#[no_mangle]
2181+
pub unsafe extern "C" fn pineappl_grid_set_bwfl(
2182+
grid: *mut Grid,
2183+
bins_lower: *mut f64,
2184+
bins_upper: *mut f64,
2185+
n_bins: usize,
2186+
bin_dim: usize,
2187+
normalizations: *mut f64,
2188+
) {
2189+
let grid = unsafe { &mut *grid };
2190+
2191+
let bins_lower = unsafe { slice::from_raw_parts(bins_lower, bin_dim * n_bins) };
2192+
let bins_upper = unsafe { slice::from_raw_parts(bins_upper, bin_dim * n_bins) };
2193+
let normalizations = unsafe { slice::from_raw_parts(normalizations, n_bins) };
2194+
2195+
let limits: Vec<Vec<(f64, f64)>> = bins_lower
2196+
.chunks(bin_dim)
2197+
.zip(bins_upper.chunks(bin_dim))
2198+
.map(|(bl_chunk, bu_chunk)| {
2199+
bl_chunk
2200+
.iter()
2201+
.zip(bu_chunk.iter())
2202+
.map(|(&a, &b)| (a, b))
2203+
.collect()
2204+
})
2205+
.collect();
2206+
2207+
grid.set_bwfl(
2208+
BinsWithFillLimits::from_limits_and_normalizations(limits, normalizations.to_vec())
2209+
// UNWRAP: error handling in the CAPI is to abort
2210+
.unwrap(),
2211+
)
2212+
// UNWRAP: error handling in the CAPI is to abort
2213+
.unwrap();
2214+
}
2215+
21172216
/// Get the shape of the objects represented in the evolve info.
21182217
///
21192218
/// # Safety

0 commit comments

Comments
 (0)