ATLAS Offline Software
Loading...
Searching...
No Matches
AutoencoderExampleAlgorithm.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
3*/
4
5// Demonstration to install an Example Autoencoder as new a dqm algorithm
6// Author: Cary David Randazzo, November 2025, Louisiana Tech University
7
8
9// Demonstration Note:
10// The following is required by all new algorithms.
11#include <dqm_algorithms/AutoencoderExampleAlgorithm.h> // Your new algorithm's header
12#include <dqm_core/AlgorithmManager.h> // the manager
13
14// Algorithm specific
15// This should change depending on your algorithm's implementation.
16#include <iostream>
17#include <TH2.h>
18#include <TClass.h> // Required for line using object.IsA()->InheritsFrom
19#include <cmath>
20#include <cstdint>
21#include <algorithm>
22#include <ers/ers.h>
24
25
26
28// DEFINE FUNCTIONS and CLASSES //
31
32// Algorithm specific functions
33// (Will depend on what you want to do, this extracts histogram data directly from a TH2)
34
35std::vector<std::vector<float>> dqm_algorithms::extract_histogram_data(const TH2* hist) {
36 std::vector<std::vector<float>> data;
37 int n_bins_x = hist->GetNbinsX();
38 int n_bins_y = hist->GetNbinsY();
39
40 data.reserve(n_bins_x * n_bins_y);
41
42 for (int x = 1; x <= n_bins_x; ++x) {
43 for (int y = 1; y <= n_bins_y; ++y) {
44 data.push_back({static_cast<float>(x-1), static_cast<float>(y-1), static_cast<float>(hist->GetBinContent(x, y))});
45 }
46 }
47 return data;
48}
49
50
52// Algorithm Setup //
54
55// Demonstration Note:
56// The following is required for all new algorithms.
57
58// Initialize ONNX Runtime in the constructor (efficient).
60: env(ORT_LOGGING_LEVEL_WARNING, "test"),
62{
63
64 dqm_core::AlgorithmManager::instance().registerAlgorithm("AutoencoderExampleAlgorithm", this);
65
66 // Demonstration Note:
67 // The following code and some code in the execute method utilize the onnx cxx api.
68 // Please refer to that documentation as you prepare cxx code for your algorithm.
69
70 // Configure session options before use.
71 session_options.SetIntraOpNumThreads(1);
72
73 // Load the model.
74 // eos does not have guaranteed availability, so we use PathResolver to find asg-calib/dev for non-production algorithms.
75 const std::string model_path = PathResolverFindCalibFile("dev/ONNXfiles/autoencoder_model.onnx");
76 session = std::make_unique<Ort::Session>(env, model_path.c_str(), session_options);
77
78 // Prepare and, if desired, print the input node names.
79 size_t num_input_nodes = session->GetInputCount();
80 Ort::AllocatorWithDefaultOptions allocator;
81 for (size_t i = 0; i < num_input_nodes; ++i)
82 {
83 std::unique_ptr<char, Ort::detail::AllocatedFree> input_name_ptr = session->GetInputNameAllocated(i, allocator);
84 char* input_name = input_name_ptr.get();
85 std::cout << "Input " << i << ": name=" << input_name << std::endl;
86 input_names.push_back(input_name);
87 }
88
89 // Prepare and, if desired, print the output node names.
90 size_t num_output_nodes = session->GetOutputCount();
91 for (size_t i = 0; i < num_output_nodes; ++i)
92 {
93 std::unique_ptr<char, Ort::detail::AllocatedFree> output_name_ptr = session->GetOutputNameAllocated(i, allocator);
94 char* output_name = output_name_ptr.get();
95 std::cout << "Output " << i << ": name=" << output_name << std::endl;
96 output_names.push_back(output_name);
97 }
98
99}
100
101
107
108
110// Main/Execute //
112
113// Demonstration Note:
114// The following is required by all new algorithms.
115// "execute" function can be thought of similar main() in your program - implement algorithm logic here.
116dqm_core::Result *
118 const TObject& object,
119 const dqm_core::AlgorithmConfig& )
120{
121 // Using the TObject object, check if it is of TH2 type, throw error if not the right type.
122 // Demonstration Note:
123 // The TObject object can vary depending on what kind of TObject you wish to utilize for your algorithm.
124 // The remainder of the execute method assumes the goals of this particular new algorithm. Modify as needed.
125 const TH2* histogram;
126 if (object.IsA()->InheritsFrom("TH2"))
127 {
128 histogram = dynamic_cast<const TH2*>(&object);
129 if (histogram->GetDimension() < 2)
130 {
131 throw dqm_core::BadConfig( ERS_HERE, name, "dimension of histogram < 2");
132 }
133 }
134 else
135 {
136 throw dqm_core::BadConfig( ERS_HERE, name, "does not inherit from TH2" );
137 }
138
139 // Extract data from histogram.
140 std::vector<std::vector<float>> hist_data = extract_histogram_data(histogram);
141
142 // Run inference for the data using the model.
143 std::vector<std::vector<float>> output_data;
144 std::vector<int64_t> input_dims = {1, 3}; // Input here is as a single data point with 3 features
145
146 Ort::MemoryInfo memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);
147 const char* input_names_cstr[] = { input_names[0].c_str() };
148 const char* output_names_cstr[] = { output_names[0].c_str() };
149 for (auto& input_data : hist_data) {
150 // Create the input tensor.
151 Ort::Value input_tensor = Ort::Value::CreateTensor<float>(memory_info, input_data.data(), input_data.size(), input_dims.data(), input_dims.size());
152
153 // Run the inference.
154 auto output_tensors = session->Run(Ort::RunOptions{nullptr}, input_names_cstr, &input_tensor, 1, output_names_cstr, 1);
155
156 // Check if the std::vector or its output_tensors do or do not have valid values for existence.
157 if (output_tensors.empty() || output_tensors[0].HasValue())
158 {
159 throw dqm_core::BadConfig(ERS_HERE, name, "Output tensor does not have values or does not exist");
160 }
161
162 // Check Tensor Size.
163 const size_t tensor_size = output_tensors[0].GetTensorTypeAndShapeInfo().GetElementCount();
164
165 if (tensor_size < 3)
166 {
167 throw dqm_core::BadConfig(ERS_HERE, name, "Output tensor < 3 elements");
168 }
169
170 // Prepare the output_data.
171 float* raw_output = output_tensors[0].GetTensorMutableData<float>();
172 std::vector<float> output_point(raw_output, raw_output + 3);
173 output_data.push_back(output_point);
174 }
175
176 // Compute the reconstruction error.
177 // (Algorithm specific logic, post inference)
178 std::vector<float> reconstruction_errors;
179 for (size_t i = 0; i < hist_data.size(); ++i) {
180 float error = 0.0;
181 for (size_t j = 0; j < 3; ++j) {
182 error += std::pow(hist_data[i][j] - output_data[i][j], 2);
183 }
184 reconstruction_errors.push_back(std::sqrt(error));
185 }
186
187 // Calculate threshold based on the 99th percentile of reconstruction errors
188 // (Algorithm specific logic, post inference)
189 std::nth_element(reconstruction_errors.begin(), reconstruction_errors.begin() + std::roundl(reconstruction_errors.size() * (99. / 100.)), reconstruction_errors.end());
190 float threshold = reconstruction_errors[reconstruction_errors.size() * 99 / 100];
191
192 // Identify anomalies based on threshold vs reconstruction_error.
193 // (Algorithm specific logic, post inference)
194 std::vector<bool> anomalies;
195 for (const auto& error : reconstruction_errors) {
196 anomalies.push_back(error > threshold);
197 }
198
199 // Demonstration Note:
200 // There are a lot of variations on how the result of the dqm_algorithm including how it interacts with the test display.
201 // I suggest looking at least at other algorithms for inspiration.
202 // The documentation and examples of that is outside of scope of this template.
203 // However, this template demonstrates at least the minimum required to setup the new algorithm and on the test display
204 // with perhaps some information of interest to this particular new algorithm.
205
206 // Prepare the resulthisto
207 TH2* resulthisto;
208 if (histogram->InheritsFrom("TH2"))
209 {
210 resulthisto=static_cast<TH2*>(histogram->Clone());
211 }
212 else
213 {
214 throw dqm_core::BadConfig( ERS_HERE, name, "does not inherit from TH2" );
215 }
216 resulthisto->Reset();
217
218 // Loop through anomalies to set result, print, etc.
219 std::cout << "Anomalies:" << std::endl;
220 for (size_t i = 0; i < anomalies.size(); ++i) {
221 if (anomalies[i])
222 {
223 // Set the resulting histogram to the bin content
224 resulthisto->SetBinContent(hist_data[i][0], hist_data[i][1], hist_data[i][2]);
225 std::cout << "Input (" << hist_data[i][0] << ", " << hist_data[i][1] << ", " << hist_data[i][2] << "): ";
226 std::cout << "Output (" << output_data[i][0] << ", " << output_data[i][1] << ", " << output_data[i][2] << ")";
227 std::cout << " Reconstruction Error: " << reconstruction_errors[i] << std::endl;
228 }
229 }
230
231 // Prepare the result of the main/execute method
232 dqm_core::Result* result = new dqm_core::Result();
233
234 // For the result, set NBins to the number of anomalies
235 result->tags_["NBins"] = anomalies.size();
236
237 // For the result, set the resulting histogram to the bin content we SetBinContent above with
238 result->object_ = boost::shared_ptr<TObject>(resulthisto);
239
240 // Set the thresholds
241 double gthreshold = 5000;
242 double rthreshold = 10000;
243
244 // Determine if the overall histogram is green, yellow, or red and send that to the result
245 if (anomalies.size() <= gthreshold)
246 {
247 result->status_ = dqm_core::Result::Green;
248 }
249 else if (anomalies.size() < rthreshold)
250 {
251 result->status_ = dqm_core::Result::Yellow;
252 }
253 else
254 {
255 result->status_ = dqm_core::Result::Red;
256 }
257
258 // For debugging, remove when done
259 //dqm_core::Result *result = new dqm_core::Result(dqm_core::Result::Undefined);
260 return result;
261}
262
263// Warning:
264// This algorithm is not production ready for anomaly detection or autoencoder specific work.
265// The errors are related to the results and calculations relative to the chosen histogram
266// and are irrelevant for the pipeline itself.
267// This example serves as an "example only" (AutoencoderExampleALG) for installing a new algorithm.
268
269// Demonstration Note:
270// The following is required for all new algorithms.
271void
273{
274 out<<"AutoencoderExampleAlgorithm: Load and process an onnx autoencoder model given the AutoencoderExampleAlgorithm.cxx "<<std::endl;
275}
static dqm_algorithms::AutoencoderExampleAlgorithm myInstance
std::string PathResolverFindCalibFile(const std::string &logical_file_name)
#define y
#define x
std::string histogram
Definition chains.cxx:52
std::vector< std::vector< float > > extract_histogram_data(const TH2 *hist)
virtual AutoencoderExampleAlgorithm * clone() override
virtual dqm_core::Result * execute(const std::string &, const TObject &, const dqm_core::AlgorithmConfig &) override
#define IsA
Declare the TObject style functions.