musrfit 1.10.0
PFunction.cpp
Go to the documentation of this file.
1/***************************************************************************
2
3 PFunction.cpp
4
5 Author: Andreas Suter
6 e-mail: andreas.suter@psi.ch
7
8 Modernized implementation using Boost.Spirit X3 with full PMetaData support.
9
10***************************************************************************/
11
12/***************************************************************************
13 * Copyright (C) 2007-2026 by Andreas Suter *
14 * andreas.suter@psi.ch *
15 * *
16 * This program is free software; you can redistribute it and/or modify *
17 * it under the terms of the GNU General Public License as published by *
18 * the Free Software Foundation; either version 2 of the License, or *
19 * (at your option) any later version. *
20 * *
21 * This program is distributed in the hope that it will be useful, *
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
24 * GNU General Public License for more details. *
25 * *
26 * You should have received a copy of the GNU General Public License *
27 * along with this program; if not, write to the *
28 * Free Software Foundation, Inc., *
29 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
30 ***************************************************************************/
31
32#include <cmath>
33#include <iostream>
34#include <sstream>
35
36#include <boost/spirit/home/x3.hpp>
37#include <boost/variant/apply_visitor.hpp>
38
39#include "PMusr.h"
40#include "PFunction.h"
41
42namespace x3 = boost::spirit::x3;
43
44//--------------------------------------------------------------------------
55PFunction::PFunction(const musrfit::ast::assignment& assignment)
56{
57 fValid = true;
58 fFuncNo = assignment.func_number;
59 fAst = assignment.rhs;
60
61 // Generate human-readable string representation
62 fFuncString = "FUN";
64 fFuncString += " = ";
66}
67
68//--------------------------------------------------------------------------
75{
76 fParam.clear();
77 fMap.clear();
78}
79
80//--------------------------------------------------------------------------
91Bool_t PFunction::CheckMapAndParamRange(UInt_t mapSize, UInt_t paramSize)
92{
93 Bool_t valid = true;
94
95 // Check first operand
96 if (!FindAndCheckMapAndParamRange(fAst.first, mapSize, paramSize)) {
97 valid = false;
98 }
99
100 // Check remaining operations
101 for (const auto& op : fAst.rest) {
102 if (!FindAndCheckMapAndParamRange(op.operand_, mapSize, paramSize)) {
103 valid = false;
104 }
105 }
106
107 return valid;
108}
109
110//--------------------------------------------------------------------------
121Bool_t PFunction::FindAndCheckMapAndParamRange(const musrfit::ast::operand& operand,
122 UInt_t mapSize, UInt_t paramSize)
123{
124 struct CheckVisitor : public boost::static_visitor<Bool_t>
125 {
126 UInt_t fMapSize;
127 UInt_t fParamSize;
128 PFunction* fFunc;
129
130 CheckVisitor(UInt_t mapSize, UInt_t paramSize, PFunction* func)
131 : fMapSize(mapSize), fParamSize(paramSize), fFunc(func) {}
132
133 Bool_t operator()(const musrfit::ast::leer&) const { return true; }
134 Bool_t operator()(double) const { return true; }
135 Bool_t operator()(const musrfit::ast::constant&) const { return true; }
136
137 Bool_t operator()(const musrfit::ast::parameter& p) const {
138 if (p.number < 1 || static_cast<UInt_t>(p.number) > fParamSize) {
139 std::cerr << "**ERROR** Parameter PAR" << p.number << " out of range (1-" << fParamSize << ")" << std::endl;
140 return false;
141 }
142 return true;
143 }
144
145 Bool_t operator()(const musrfit::ast::map_ref& m) const {
146 if (m.number < 1 || static_cast<UInt_t>(m.number) > fMapSize) {
147 std::cerr << "**ERROR** Map MAP" << m.number << " out of range (1-" << fMapSize << ")" << std::endl;
148 return false;
149 }
150 return true;
151 }
152
153 Bool_t operator()(const musrfit::ast::function_call& f) const {
154 return fFunc->FindAndCheckMapAndParamRange(f.arg.first, fMapSize, fParamSize);
155 }
156
157 Bool_t operator()(const musrfit::ast::power_call& p) const {
158 Bool_t valid = true;
159 if (!fFunc->FindAndCheckMapAndParamRange(p.base.first, fMapSize, fParamSize))
160 valid = false;
161 if (!fFunc->FindAndCheckMapAndParamRange(p.pow.first, fMapSize, fParamSize))
162 valid = false;
163 return valid;
164 }
165
166 Bool_t operator()(const musrfit::ast::expression& expr) const {
167 Bool_t valid = true;
168 if (!fFunc->FindAndCheckMapAndParamRange(expr.first, fMapSize, fParamSize))
169 valid = false;
170 for (const auto& op : expr.rest) {
171 if (!fFunc->FindAndCheckMapAndParamRange(op.operand_, fMapSize, fParamSize))
172 valid = false;
173 }
174 return valid;
175 }
176 };
177
178 CheckVisitor checker(mapSize, paramSize, this);
179 return boost::apply_visitor(checker, operand);
180}
181
182//--------------------------------------------------------------------------
193Double_t PFunction::Eval(std::vector<Double_t> param, PMetaData metaData)
194{
195 if (!fValid) return 0.0;
196
197 fParam = param;
198 fMetaData = metaData;
199
200 EvalVisitor evaluator(fMap, fParam, fMetaData);
201
202 // Evaluate first operand
203 Double_t result = boost::apply_visitor(evaluator, fAst.first);
204
205 // Apply operations
206 for (const auto& op : fAst.rest) {
207 Double_t rhs = boost::apply_visitor(evaluator, op.operand_);
208
209 switch (op.operator_) {
210 case musrfit::ast::op_plus:
211 result += rhs;
212 break;
213 case musrfit::ast::op_minus:
214 result -= rhs;
215 break;
216 case musrfit::ast::op_times:
217 result *= rhs;
218 break;
219 case musrfit::ast::op_divide:
220 if (std::abs(rhs) > 1.0e-20)
221 result /= rhs;
222 else
223 result = 0.0;
224 break;
225 }
226 }
227
228 return result;
229}
230
231//--------------------------------------------------------------------------
238TString PFunction::GenerateString(const musrfit::ast::expression& expr)
239{
240 TString result;
241
242 // Check if we have any high-precedence operations (multiplication/division)
243 bool hasHighPrecOps = false;
244 for (const auto& op : expr.rest) {
245 if (op.operator_ == musrfit::ast::op_times || op.operator_ == musrfit::ast::op_divide) {
246 hasHighPrecOps = true;
247 break;
248 }
249 }
250
251 // Generate first operand - check if it's a wrapped expression
252 if (const musrfit::ast::expression* inner = boost::get<musrfit::ast::expression>(&expr.first)) {
253
254 // Check if inner expression has low-precedence ops that need parentheses
255 bool needsParens = false;
256 if (hasHighPrecOps && !inner->rest.empty()) {
257 for (const auto& innerOp : inner->rest) {
258 if (innerOp.operator_ == musrfit::ast::op_plus || innerOp.operator_ == musrfit::ast::op_minus) {
259 needsParens = true;
260 break;
261 }
262 }
263 }
264
265 if (needsParens) {
266 result = "(" + GenerateString(*inner) + ")";
267 } else {
268 result = GenerateString(*inner);
269 }
270 } else {
271 result = GenerateStringOperand(expr.first);
272 }
273
274 // Generate operations and operands
275 for (const auto& op : expr.rest) {
276 switch (op.operator_) {
277 case musrfit::ast::op_plus: result += " + "; break;
278 case musrfit::ast::op_minus: result += " - "; break;
279 case musrfit::ast::op_times: result += " * "; break;
280 case musrfit::ast::op_divide: result += " / "; break;
281 }
282
283 // Check if operand is a wrapped expression
284 if (const musrfit::ast::expression* inner = boost::get<musrfit::ast::expression>(&op.operand_)) {
285
286 // Check if inner expression has low-precedence ops that need parentheses
287 bool needsParens = false;
288 if ((op.operator_ == musrfit::ast::op_times || op.operator_ == musrfit::ast::op_divide) &&
289 !inner->rest.empty()) {
290 for (const auto& innerOp : inner->rest) {
291 if (innerOp.operator_ == musrfit::ast::op_plus || innerOp.operator_ == musrfit::ast::op_minus) {
292 needsParens = true;
293 break;
294 }
295 }
296 }
297
298 if (needsParens) {
299 result += "(" + GenerateString(*inner) + ")";
300 } else {
301 result += GenerateString(*inner);
302 }
303 } else {
304 result += GenerateStringOperand(op.operand_);
305 }
306 }
307
308 return result;
309}
310
311//--------------------------------------------------------------------------
318TString PFunction::GenerateStringOperand(const musrfit::ast::operand& operand)
319{
320 struct StringVisitor : public boost::static_visitor<TString>
321 {
322 PFunction* fFunc;
323 StringVisitor(PFunction* func) : fFunc(func) {}
324
325 TString operator()(const musrfit::ast::leer&) const { return "leer"; }
326
327 TString operator()(double val) const {
328 std::ostringstream oss;
329 oss << val;
330 return TString(oss.str().c_str());
331 }
332
333 TString operator()(const musrfit::ast::constant& c) const {
334 TString str;
335 if (c.sign) str = "-";
336
337 switch (c.const_type) {
338 case musrfit::ast::constant::pi: str += "PI"; break;
339 case musrfit::ast::constant::gamma_mu: str += "GAMMA_MU"; break;
340 case musrfit::ast::constant::field: str += (c.sign ? "B" : "B"); break; // Sign already added
341 case musrfit::ast::constant::energy: str += (c.sign ? "EN" : "EN"); break;
342 case musrfit::ast::constant::temp:
343 str += "T";
344 str += c.index;
345 break;
346 }
347 return str;
348 }
349
350 TString operator()(const musrfit::ast::parameter& p) const {
351 TString str;
352 if (p.sign) str = "-";
353 str += "PAR";
354 str += p.number;
355 return str;
356 }
357
358 TString operator()(const musrfit::ast::map_ref& m) const {
359 TString str;
360 if (m.sign) str = "-";
361 str += "MAP";
362 str += m.number;
363 return str;
364 }
365
366 TString operator()(const musrfit::ast::function_call& f) const {
367 TString str;
368 switch (f.func_id) {
369 case musrfit::ast::fun_cos: str = "COS"; break;
370 case musrfit::ast::fun_sin: str = "SIN"; break;
371 case musrfit::ast::fun_tan: str = "TAN"; break;
372 case musrfit::ast::fun_cosh: str = "COSH"; break;
373 case musrfit::ast::fun_sinh: str = "SINH"; break;
374 case musrfit::ast::fun_tanh: str = "TANH"; break;
375 case musrfit::ast::fun_acos: str = "ACOS"; break;
376 case musrfit::ast::fun_asin: str = "ASIN"; break;
377 case musrfit::ast::fun_atan: str = "ATAN"; break;
378 case musrfit::ast::fun_acosh: str = "ACOSH"; break;
379 case musrfit::ast::fun_asinh: str = "ASINH"; break;
380 case musrfit::ast::fun_atanh: str = "ATANH"; break;
381 case musrfit::ast::fun_log: str = "LOG"; break;
382 case musrfit::ast::fun_ln: str = "LN"; break;
383 case musrfit::ast::fun_exp: str = "EXP"; break;
384 case musrfit::ast::fun_sqrt: str = "SQRT"; break;
385 }
386 str += "(";
387 str += fFunc->GenerateString(f.arg);
388 str += ")";
389 return str;
390 }
391
392 TString operator()(const musrfit::ast::power_call& p) const {
393 TString str = "POW(";
394 str += fFunc->GenerateString(p.base);
395 str += ", ";
396 str += fFunc->GenerateString(p.pow);
397 str += ")";
398 return str;
399 }
400
401 TString operator()(const musrfit::ast::expression& expr) const {
402 // If no operations, just return the first operand without parentheses
403 if (expr.rest.empty()) {
404 return fFunc->GenerateStringOperand(expr.first);
405 } else {
406 // Has operations - this means we have a parenthesized expression from the grammar
407 // (e.g., from factor = '(' expression ')'), so we need to preserve the parentheses
408 return "(" + fFunc->GenerateString(expr) + ")";
409 }
410 }
411 };
412
413 StringVisitor visitor(this);
414 return boost::apply_visitor(visitor, operand);
415}
416
417//==========================================================================
418// EvalVisitor Implementation
419//==========================================================================
420
426Double_t PFunction::EvalVisitor::operator()(const musrfit::ast::leer&) const
427{
428 return 0.0;
429}
430
437Double_t PFunction::EvalVisitor::operator()(double val) const
438{
439 return val;
440}
441
451Double_t PFunction::EvalVisitor::operator()(const musrfit::ast::constant& c) const
452{
453 Double_t val = 0.0;
454
455 switch (c.const_type) {
456 case musrfit::ast::constant::pi:
457 val = 3.14159265358979323846;
458 break;
459
460 case musrfit::ast::constant::gamma_mu:
461 val = GAMMA_BAR_MUON; // From PMusr.h
462 break;
463
464 case musrfit::ast::constant::field:
465 val = fMetaData.fField; // Magnetic field (Gauss)
466 break;
467
468 case musrfit::ast::constant::energy:
469 val = fMetaData.fEnergy; // Energy (keV)
470 break;
471
472 case musrfit::ast::constant::temp:
473 // Temperature array (Kelvin)
474 if (c.index >= 0 && static_cast<UInt_t>(c.index) < fMetaData.fTemp.size()) {
475 val = fMetaData.fTemp[c.index];
476 } else {
477 std::cerr << "**ERROR** Temperature index T" << c.index << " out of range" << std::endl;
478 val = 0.0;
479 }
480 break;
481
482 default:
483 val = 0.0;
484 }
485
486 return c.sign ? -val : val;
487}
488
499Double_t PFunction::EvalVisitor::operator()(const musrfit::ast::parameter& p) const
500{
501 Int_t idx = p.number - 1;
502
503 if (idx < 0 || static_cast<UInt_t>(idx) >= fParam.size()) {
504 std::cerr << "**ERROR** Parameter PAR" << p.number << " out of range during evaluation" << std::endl;
505 return 0.0;
506 }
507
508 Double_t val = fParam[idx];
509 return p.sign ? -val : val;
510}
511
522Double_t PFunction::EvalVisitor::operator()(const musrfit::ast::map_ref& m) const
523{
524 Int_t mapIdx = m.number - 1;
525
526 if (mapIdx < 0 || static_cast<UInt_t>(mapIdx) >= fMap.size()) {
527 std::cerr << "**ERROR** Map MAP" << m.number << " out of range during evaluation" << std::endl;
528 return 0.0;
529 }
530
531 Int_t paramIdx = fMap[mapIdx] - 1;
532
533 if (paramIdx < 0 || static_cast<UInt_t>(paramIdx) >= fParam.size()) {
534 std::cerr << "**ERROR** Mapped parameter out of range during evaluation" << std::endl;
535 return 0.0;
536 }
537
538 Double_t val = fParam[paramIdx];
539 return m.sign ? -val : val;
540}
541
552Double_t PFunction::EvalVisitor::operator()(const musrfit::ast::function_call& f) const
553{
554 // Evaluate argument
555 Double_t arg = boost::apply_visitor(*this, f.arg.first);
556 for (const auto& op : f.arg.rest) {
557 Double_t rhs = boost::apply_visitor(*this, op.operand_);
558 switch (op.operator_) {
559 case musrfit::ast::op_plus: arg += rhs; break;
560 case musrfit::ast::op_minus: arg -= rhs; break;
561 case musrfit::ast::op_times: arg *= rhs; break;
562 case musrfit::ast::op_divide:
563 if (std::abs(rhs) > 1.0e-20) arg /= rhs;
564 break;
565 }
566 }
567
568 // Apply function
569 Double_t result = 0.0;
570 switch (f.func_id) {
571 case musrfit::ast::fun_cos: result = std::cos(arg); break;
572 case musrfit::ast::fun_sin: result = std::sin(arg); break;
573 case musrfit::ast::fun_tan: result = std::tan(arg); break;
574 case musrfit::ast::fun_cosh: result = std::cosh(arg); break;
575 case musrfit::ast::fun_sinh: result = std::sinh(arg); break;
576 case musrfit::ast::fun_tanh: result = std::tanh(arg); break;
577 case musrfit::ast::fun_acos: result = std::acos(arg); break;
578 case musrfit::ast::fun_asin: result = std::asin(arg); break;
579 case musrfit::ast::fun_atan: result = std::atan(arg); break;
580 case musrfit::ast::fun_acosh: result = std::acosh(arg); break;
581 case musrfit::ast::fun_asinh: result = std::asinh(arg); break;
582 case musrfit::ast::fun_atanh: result = std::atanh(arg); break;
583 case musrfit::ast::fun_log: result = std::log10(std::abs(arg)); break;
584 case musrfit::ast::fun_ln: result = std::log(std::abs(arg)); break;
585 case musrfit::ast::fun_exp: result = std::exp(arg); break;
586 case musrfit::ast::fun_sqrt: result = std::sqrt(std::abs(arg)); break;
587 default: result = 0.0;
588 }
589
590 return result;
591}
592
603Double_t PFunction::EvalVisitor::operator()(const musrfit::ast::power_call& p) const
604{
605 // Evaluate base
606 Double_t base = boost::apply_visitor(*this, p.base.first);
607 for (const auto& op : p.base.rest) {
608 Double_t rhs = boost::apply_visitor(*this, op.operand_);
609 switch (op.operator_) {
610 case musrfit::ast::op_plus: base += rhs; break;
611 case musrfit::ast::op_minus: base -= rhs; break;
612 case musrfit::ast::op_times: base *= rhs; break;
613 case musrfit::ast::op_divide:
614 if (std::abs(rhs) > 1.0e-20) base /= rhs;
615 break;
616 }
617 }
618
619 // Evaluate exponent
620 Double_t exponent = boost::apply_visitor(*this, p.pow.first);
621 for (const auto& op : p.pow.rest) {
622 Double_t rhs = boost::apply_visitor(*this, op.operand_);
623 switch (op.operator_) {
624 case musrfit::ast::op_plus: exponent += rhs; break;
625 case musrfit::ast::op_minus: exponent -= rhs; break;
626 case musrfit::ast::op_times: exponent *= rhs; break;
627 case musrfit::ast::op_divide:
628 if (std::abs(rhs) > 1.0e-20) exponent /= rhs;
629 break;
630 }
631 }
632
633 return std::pow(std::abs(base), exponent);
634}
635
649Double_t PFunction::EvalVisitor::operator()(const musrfit::ast::expression& e) const
650{
651 Double_t result = boost::apply_visitor(*this, e.first);
652
653 for (const auto& op : e.rest) {
654 Double_t rhs = boost::apply_visitor(*this, op.operand_);
655 switch (op.operator_) {
656 case musrfit::ast::op_plus: result += rhs; break;
657 case musrfit::ast::op_minus: result -= rhs; break;
658 case musrfit::ast::op_times: result *= rhs; break;
659 case musrfit::ast::op_divide:
660 if (std::abs(rhs) > 1.0e-20) result /= rhs;
661 break;
662 }
663 }
664
665 return result;
666}
#define GAMMA_BAR_MUON
Definition PMusr.h:138
Visitor class for evaluating the AST.
Definition PFunction.h:180
const PMetaData & fMetaData
Definition PFunction.h:199
Double_t operator()(const musrfit::ast::leer &) const
Evaluates a leer (empty) AST node.
const std::vector< Double_t > & fParam
Definition PFunction.h:198
const std::vector< Int_t > & fMap
Definition PFunction.h:197
PMetaData fMetaData
Metadata from experimental data (field, energy, temperature, etc.)
Definition PFunction.h:209
Int_t fFuncNo
Function number extracted from label (x in FUNx)
Definition PFunction.h:207
Bool_t fValid
Validity flag: true if function parsed and initialized successfully.
Definition PFunction.h:206
musrfit::ast::expression fAst
Abstract syntax tree for the expression.
Definition PFunction.h:202
virtual Bool_t CheckMapAndParamRange(UInt_t mapSize, UInt_t paramSize)
Validates that all parameter and map references are within valid ranges.
Definition PFunction.cpp:91
virtual Bool_t FindAndCheckMapAndParamRange(const musrfit::ast::operand &operand, UInt_t mapSize, UInt_t paramSize)
Recursively validates parameter and map references in the AST.
virtual TString GenerateString(const musrfit::ast::expression &expr)
Generates a human-readable string representation of the AST.
virtual TString GenerateStringOperand(const musrfit::ast::operand &operand)
Generates a string representation of an operand.
TString fFuncString
Formatted, human-readable function representation.
Definition PFunction.h:208
PFunction(const musrfit::ast::assignment &assignment)
Constructor that parses and prepares a function for evaluation.
Definition PFunction.cpp:55
virtual ~PFunction()
Destructor that cleans up resources.
Definition PFunction.cpp:74
std::vector< Double_t > fParam
Current fit parameter values for evaluation.
Definition PFunction.h:203
virtual Double_t Eval(std::vector< Double_t > param, PMetaData metaData)
Evaluates the function with given parameters and metadata.
std::vector< Int_t > fMap
Map vector for indirect parameter references.
Definition PFunction.h:204