source: trunk/MagicSoft/Mars/mdata/MDataPhrase.cc@ 8083

Last change on this file since 8083 was 8083, checked in by tbretz, 18 years ago
*** empty log message ***
File size: 19.7 KB
Line 
1/* ======================================================================== *\
2!
3! *
4! * This file is part of MARS, the MAGIC Analysis and Reconstruction
5! * Software. It is distributed to you in the hope that it can be a useful
6! * and timesaving tool in analysing Data of imaging Cerenkov telescopes.
7! * It is distributed WITHOUT ANY WARRANTY.
8! *
9! * Permission to use, copy, modify and distribute this software and its
10! * documentation for any purpose is hereby granted without fee,
11! * provided that the above copyright notice appear in all copies and
12! * that both that copyright notice and this permission notice appear
13! * in supporting documentation. It is provided "as is" without express
14! * or implied warranty.
15! *
16!
17!
18! Author(s): Thomas Bretz, 04/2004 <mailto:tbretz@astro.uni-wuerzburg.de>
19!
20! Copyright: MAGIC Software Development, 2000-2006
21!
22!
23\* ======================================================================== */
24
25/////////////////////////////////////////////////////////////////////////////
26//
27// MDataPhrase
28//
29// A MDataPhrase is a wrapper for TFormula. It supports access to data-
30// members and/or member functions acessible from the parameter list
31// via MDataMember. It supports access to elements of a MMatrix through
32// the parameter list via MDataElement and it sipports variables set
33// by SetVariables via MDataValue.
34//
35// The parsing is done by TFormula. For more information which functions
36// are supported see TFormula, TFormulaPrimitives and TFormulaMathInterface.
37//
38// The support is done by replacing the parameters with access to the
39// parameter list by parameters like [0], [1],... When evaluating
40// the TFormula first the parameters are evaluated and passed to
41// TFormula. Each parameter is evaluated only once and, if necessary,
42// passed more than once to TFormula. To store the MData* classes used
43// for parameter access a TObjArray is used. Its advantage is, that
44// it has an UncheckedAt function which saves some time, because
45// no obsolete sanity checks must be done accessing the array.
46//
47// Because everything supported by TFormula is also supported by
48// MDataPhrase also conditional expression are supported.
49//
50// For supported functions see TFormulaPrimitive and TMathInterface.
51//
52// Examples:
53//
54// Gaus, Gausn, Landau, Landaun, Pol0-Pol10, Pow2-Pow5
55//
56// In the constructor you can give rule, like
57// "HillasSource.fDist / MHillas.fLength"
58// Where MHillas/HillasSource is the name of the parameter container in
59// the parameter list and fDist/fLength is the name of the data members
60// in the containers. The result will be fDist divided by fLength.
61//
62// In case you want to access a data-member which is a data member object
63// you can acces it with (Remark: it must derive from MParContainer):
64// "MCameraLV.fPowerSupplyA.fVoltagePos5V"
65// (THIS FEATURE IS CURRENTLY NOT SUPPORTED)
66//
67// You can also use parantheses:
68// "HillasDource.fDist / (MHillas.fLength + MHillas.fWidth)"
69//
70// Additional implementations:
71//
72// isnan(x) return 1 if x is NaN (Not a Number) otherwise 0
73// finite(x) return 1 if the number is a valid double (not NaN, inf)
74//
75// NaN (Not a Number) means normally a number which is to small to be
76// stored in a floating point variable (eg. 0<x<1e-56 or similar) or
77// a number which function is not defined (like asin(1.5))
78//
79// inf is the symbol for an infinite number.
80//
81//
82// Constants
83// ---------
84//
85// Most constants you might need can be found in TMath, eg:
86// TMath::Pi(), TMath::TwoPi(), TMath::Ln10(), TMath::LogE()
87// TMath::RadToDeg(), TMath::DegToRad();
88//
89//
90// Variable Parameters
91// ------------------------
92// If you want to use variables, eg for fits you can use [0], [1], ...
93// These values are initialized with 0 and set by calling
94// SetVariables(), eg
95// [0]*MHillas.fArea
96//
97//
98// Multi-argument functions
99// ------------------------
100// You can use multi-argument functions, too. Example:
101// "TMath::Hypot(MHillas.fMeanX, MHillas.MeanY)"
102// "pow(MHillas.fMeanX*MHillas.MeanY, -1.2)"
103//
104//
105//
106// To Do:
107// - The possibility to use other objects inheriting from MData
108// is missing.
109//
110/////////////////////////////////////////////////////////////////////////////
111#include "MDataPhrase.h"
112
113#include <TArrayI.h>
114#include <TPRegexp.h>
115#include <TFormula.h>
116
117#include "MLog.h"
118#include "MLogManip.h"
119
120#include "MArrayD.h"
121
122#include "MDataValue.h"
123#include "MDataMember.h"
124#include "MDataElement.h"
125
126ClassImp(MDataPhrase);
127
128using namespace std;
129
130// --------------------------------------------------------------------------
131//
132// Check for the existance of the expression [idx] in the string
133// phrase. If existing a corresponding new MDataValue is added to
134// fMembers and index is increased by one.
135//
136// This makes the use of user-defined variables in the phrase possible.
137//
138Int_t MDataPhrase::CheckForVariable(const TString &phrase, Int_t idx)
139{
140 TPRegexp reg(Form("\\w\\[%d\\]\\w", idx));
141
142 TString mods;
143 TArrayI pos;
144
145 while (reg.Match(phrase, mods, 0, 130, &pos))
146 {
147 // [idx] already existing. Add a corresponding MDataValue
148 fMembers.AddLast(new MDataValue(0, idx));
149 idx++;
150 }
151
152 return idx;
153}
154
155// --------------------------------------------------------------------------
156//
157// Replace all expressions expr (found by a regular expression \\b%s\\b
158// with %s being the expression) in the string phrase by [idx].
159//
160// The length of [idx]9 is returned.
161//
162Int_t MDataPhrase::Substitute(TString &phrase, const TString &expr, Int_t idx) const
163{
164 const TString arg = Form("[%d]", idx);
165
166 TPRegexp reg(expr);
167
168 TString mods;
169 TArrayI pos;
170
171 Int_t p = 0;
172 while (1)
173 {
174 // check whether argument is found
175 if (reg.Match(phrase, mods, p, 130, &pos)==0)
176 break;
177
178 // Replace expression by argument [idx]
179 phrase.Replace(pos[0], pos[1]-pos[0], arg, arg.Length());
180
181 // Jump behind the new string which was just inserted
182 p = pos[0]+arg.Length();
183 }
184 return arg.Length();
185}
186
187TString MDataPhrase::Parse(TString phrase)
188{
189 // This is for backward compatibility with MDataChain
190 phrase.ReplaceAll("gRandom->", "TRandom::");
191 phrase.ReplaceAll("kRad2Deg", "TMath::RadToDeg()");
192 phrase.ReplaceAll("kDeg2Rad", "TMath::DegToRad()");
193 phrase.ReplaceAll(" ", "");
194
195 int idx=0;
196 int p =0;
197
198 TString mods;
199 TArrayI pos;
200/*
201 // Find all functions...
202 // The first \\w+ should also allow ::
203 TPRegexp reg = TPRegexp("[[:word:]:]+\\([^()]*\\)");
204 while (1)
205 {
206 //idx = CheckForVariables(phrase, idx);
207
208 if (reg.Match(phrase, mods, p, 130, &pos)==0)
209 break;
210
211 const Int_t len = pos[1]-pos[0];
212
213 // This is the full function with its argument(s)
214 const TString term = phrase(pos[0], len);
215
216 // Now get rid of the argument(s)
217 const TPRegexp reg3("\\([^()]+\\)");
218
219 TString func(term);
220 reg3.Substitute(func, "");
221
222 // Seems to be a special case whic is not handles by
223 // TFormulaPrimitive by known by TFormula
224 if (func.BeginsWith("TRandom::"))
225 {
226 p = pos[0]+pos[1];
227 continue;
228 }
229
230 // check whether the function is available
231 if (TFormulaPrimitive::FindFormula(func))
232 {
233 p = pos[0]+pos[1];
234 continue;
235 }
236
237 cout << "Unknown: " << func << endl;
238 // p = pos[0]+pos[1];
239 return;
240 }
241*/
242
243 p = 0;
244
245 // Find all data-members in expression such as
246 // MTest.fDataMember. Because also floating point
247 // numbers can contain a dot the result has to
248 // be checked carefully
249 TPRegexp reg = TPRegexp("\\w+[.]\\w+");
250 TPRegexp ishex("^0x[[:xdigit:]]+$");
251
252 while (1)
253 {
254 // If some indices are already existing
255 // initialize them by a flexible MDataValue
256 idx = CheckForVariable(phrase, idx);
257
258 // Check whether expression is found
259 if (reg.Match(phrase, mods, p, 130, &pos)==0)
260 break;
261
262 // Get expression from phrase
263 const TString expr = phrase(pos[0], pos[1]-pos[0]);
264
265 // Also hex-numbers and floats fullfill our condition...
266 if (!expr(ishex).IsNull() || expr.IsFloat())
267 {
268 p = pos[1];
269 continue;
270 }
271
272 // Add a corresponding MDataMember to our list
273 fMembers.AddLast(new MDataMember(expr));
274
275 // Find other occurances of arg by this regexp
276 // and start next search behind first match
277 const TString regexp = Form("\\b%s\\b", expr.Data());
278 p = pos[0] + Substitute(phrase, regexp, idx);
279
280 // Step forward to the next argument
281 idx++;
282 }
283
284 // Now check for matrix elemets as M[5]
285 reg = TPRegexp("\\w+\\[\\d+\\]");
286 while (1)
287 {
288 // If some indices are already existing
289 // initialize them by a MDataValue
290 idx = CheckForVariable(phrase, idx);
291
292 // Check whether expression is found
293 if (reg.Match(phrase, mods, p, 130, &pos)==0)
294 break;
295
296 // Get expression from phrase
297 TString expr = phrase(pos[0], pos[1]-pos[0]);
298
299 // Add a corresponding MDataMember to our list
300 fMembers.AddLast(new MDataElement(expr));
301
302 // Make the expression "Regular expression proofed"
303 expr.ReplaceAll("[", "\\[");
304 expr.ReplaceAll("]", "\\]");
305
306 // Find other occurances of arg by this regexp
307 // and start next search behind first match
308 const TString regexp = Form("\\b%s", expr.Data());
309 p = pos[0] + Substitute(phrase, regexp, idx);
310
311 // Step forward to the next argument
312 idx++;
313 }
314
315 return phrase;
316}
317
318// --------------------------------------------------------------------------
319//
320// Clear Formula and corresponding data members
321//
322void MDataPhrase::Clear(Option_t *)
323{
324 fMembers.Delete();
325 if (!fFormula)
326 return;
327
328 delete fFormula;
329 fFormula = NULL;
330}
331
332// --------------------------------------------------------------------------
333//
334// Set a new phrase/rule. Returns kTRUE on sucess, kFALSE otherwise
335//
336Bool_t MDataPhrase::SetRule(const TString &rule)
337{
338 Clear();
339
340 const TString txt=Parse(rule);
341 if (txt.IsNull())
342 {
343 Clear();
344 return kFALSE;
345 }
346
347 fFormula = new TFormula;
348
349 // Must have a name otherwise all axis labels disappear like a miracle
350 fFormula->SetName(fName.IsNull()?"TFormula":fName.Data());
351 if (fFormula->Compile(txt))
352 {
353 *fLog << err << dbginf << "Syntax Error: TFormula::Compile failed..."<< endl;
354 *fLog << " > " << rule << endl;
355 *fLog << " > " << txt << endl;
356 Clear();
357 return kFALSE;
358 }
359
360 gROOT->GetListOfFunctions()->Remove(fFormula);
361
362 return kTRUE;
363}
364
365namespace MFastFun {
366 //
367 // Namespace with basic primitive functions registered by TFormulaPrimitive
368 // all function registerd by TFormulaPrimitive can be used in TFormula
369 //
370 Double_t Nint(Double_t x){return TMath::Nint(x);}
371 Double_t Sign(Double_t x){return x<0?-1:+1;}
372 Double_t IsNaN(Double_t x){return TMath::IsNaN(x);}
373 Double_t Finite(Double_t x){return TMath::Finite(x);}
374}
375
376// --------------------------------------------------------------------------
377//
378// Default constructor. Set a rule (phrase), see class description for more
379// details. Set a name and title. If no title is given it is set to the rule.
380//
381MDataPhrase::MDataPhrase(const char *rule, const char *name, const char *title) : fFormula(0)
382{
383 // More in TFormulaPrimitive.cxx
384 // More in TFormulaMathInterface
385 if (!TFormulaPrimitive::FindFormula("isnan"))
386 {
387 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("log2", "log2", (TFormulaPrimitive::GenFunc10)TMath::Log2));
388 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("fabs", "fabs", (TFormulaPrimitive::GenFunc10)TMath::Abs));
389 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("floor", "floor", (TFormulaPrimitive::GenFunc10)TMath::Floor));
390 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("ceil", "ceil", (TFormulaPrimitive::GenFunc10)TMath::Ceil));
391
392 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("nint", "nint", (TFormulaPrimitive::GenFunc10)MFastFun::Nint));
393 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("round", "round", (TFormulaPrimitive::GenFunc10)MFastFun::Nint));
394 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("sgn", "sgn", (TFormulaPrimitive::GenFunc10)MFastFun::Sign));
395
396 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("isnan", "isnan", (TFormulaPrimitive::GenFunc10)MFastFun::IsNaN));
397 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("finite", "finite", (TFormulaPrimitive::GenFunc10)MFastFun::Finite));
398 }
399
400 // TFormulaPrimitive is used to get direct acces to the function pointers
401 // GenFunc - pointers to the static function
402 // TFunc - pointers to the data member functions
403 //
404 // The following sufixes are currently used, to describe function arguments:
405 // ------------------------------------------------------------------------
406 // G - generic layout - pointer to double (arguments), pointer to double (parameters)
407 // 10 - double
408 // 110 - double, double
409 // 1110 - double, double, double
410
411 fName = name ? name : "MDataPhrase";
412 fTitle = title ? title : rule;
413
414 fMembers.SetOwner();
415
416 if (rule)
417 SetRule(rule);
418}
419
420// --------------------------------------------------------------------------
421//
422// Destructor
423//
424MDataPhrase::~MDataPhrase()
425{
426 if (fFormula)
427 delete fFormula;
428}
429
430// --------------------------------------------------------------------------
431//
432// CopyConstructor
433//
434MDataPhrase::MDataPhrase(MDataPhrase &ts)
435{
436 TFormula *f = ts.fFormula;
437
438 fName = "MDataPhrase";
439 fTitle = f ? f->GetExpFormula() : (TString)"";
440
441 fFormula = f ? (TFormula*)f->Clone() : 0;
442 gROOT->GetListOfFunctions()->Remove(fFormula);
443
444 fMembers.SetOwner();
445
446 TObject *o = NULL;
447 TIter Next(&ts.fMembers);
448 while ((o=Next()))
449 fMembers.AddLast(o->Clone());
450}
451
452// --------------------------------------------------------------------------
453//
454// Evaluates and returns the result of the member list.
455//
456Double_t MDataPhrase::GetValue() const
457{
458 const Int_t n = fMembers.GetEntriesFast();
459
460 if (fMembers.GetSize()<n)
461 {
462 *fLog << err << "ERROR - MDataPhrase::GetValue: Size mismatch!" << endl;
463 return 0;
464 }
465
466 // This is to get rid of the cost-qualifier for this->fStorage
467 Double_t *arr = fStorage.GetArray();
468
469 // Evaluate parameters (the access with TObjArray::UncheckedAt is
470 // roughly two times faster than with a TIter and rougly three
471 // times than accessing a TOrdCollection. However this might still
472 // be quite harmless compared to the time needed by GetValue)
473 for (Int_t i=0; i<n; i++)
474 arr[i] = static_cast<MData*>(fMembers.UncheckedAt(i))->GetValue();
475
476 // Evaluate function
477 return fFormula->EvalPar(0, arr);
478}
479
480// --------------------------------------------------------------------------
481//
482// Returns kTRUE if all members of fMemebers are valid and fFormula!=NULL
483//
484Bool_t MDataPhrase::IsValid() const
485{
486 TIter Next(&fMembers);
487
488 MData *data = NULL;
489 while ((data=(MData*)Next()))
490 if (!data->IsValid())
491 return kFALSE;
492
493 return fFormula ? kTRUE : kFALSE;
494}
495
496// --------------------------------------------------------------------------
497//
498// Checks whether at least one member has the ready-to-save flag.
499//
500Bool_t MDataPhrase::IsReadyToSave() const
501{
502 TIter Next(&fMembers);
503
504 MData *data = NULL;
505
506 while ((data=(MData*)Next()))
507 if (data->IsReadyToSave())
508 return kTRUE;
509
510 return kFALSE;
511}
512
513// --------------------------------------------------------------------------
514//
515// PreProcesses all members in the list
516//
517Bool_t MDataPhrase::PreProcess(const MParList *plist)
518{
519 TIter Next(&fMembers);
520
521 MData *member=NULL;
522
523 //
524 // loop over all members
525 //
526 while ((member=(MData*)Next()))
527 if (!member->PreProcess(plist))
528 {
529 *fLog << err << "Error - Preprocessing Data Member ";
530 *fLog << member->GetName() << " in " << fName << endl;
531 return kFALSE;
532 }
533
534 // For speed reasons MArrayD instead of TArrayD is used
535 // (no range check is done when accessing). The storage is
536 // allocated (Set) only when its size is changing. If GetValue
537 // is called many times this should improve the speed significantly
538 // because the necessary memory is already allocated and doesn't need
539 // to be freed. (Just a static variable is not enough, because there
540 // may be several independant objects of this class)
541 fStorage.Set(fMembers.GetSize());
542
543 return kTRUE;
544}
545
546// --------------------------------------------------------------------------
547//
548// Builds a rule from all the list members. This is a rule which could
549// be used to rebuild the list using the constructor of a MDataChain
550//
551TString MDataPhrase::GetRule() const
552{
553 if (!fFormula)
554 return "<empty>";
555
556 TString rule = fFormula->GetTitle(); //fFormula->GetExpFormula();
557
558 MData *member = NULL;
559
560 Int_t i=0;
561 TIter Next(&fMembers);
562 while ((member=(MData*)Next()))
563 {
564 TString r = member->GetRule();
565 r.ReplaceAll("]", "\\]");
566 rule.ReplaceAll(Form("[%d]", i++), r);
567 }
568 rule.ReplaceAll("\\]", "]");
569
570 return rule;
571}
572/*
573// --------------------------------------------------------------------------
574//
575// Return a comma seperated list of all data members used in the chain.
576// This is mainly used in MTask::AddToBranchList
577//
578TString MDataPhrase::GetDataMember() const
579{
580 TString str;
581
582 TIter Next(&fMembers);
583
584 MData *member=(MData*)Next();
585
586 if (!member->GetDataMember().IsNull())
587 str += member->GetDataMember();
588
589 while ((member=(MData*)Next()))
590 {
591 if (!member->GetDataMember().IsNull())
592 {
593 str += ",";
594 str += member->GetDataMember();
595 }
596 }
597
598 return str;
599}
600*/
601void MDataPhrase::SetVariables(const TArrayD &arr)
602{
603 fMembers.R__FOR_EACH(MData, SetVariables)(arr);
604}
605
606// --------------------------------------------------------------------------
607//
608// Check for corresponding entries in resource file and setup data phrase.
609//
610// Assuming your MDataChain is called (Set/GetName): MyData
611//
612// Now setup the condition, eg:
613// MyData.Rule: log10(MHillas.fSize)
614// or
615// MyData.Rule: log10(MHillas.fSize) - 4.1
616//
617// If you want to use more difficult rules you can split the
618// condition into subrules. Subrules are identified
619// by {}-brackets. Avoid trailing 0's! For example:
620//
621// MyData.Rule: log10(MHillas.fSize) + {0} - {1}
622// MyData.0: 5.5*MHillas.fSize
623// MyData.1: 2.3*log10(MHillas.fSize)
624//
625// The numbering must be continous and start with 0. You can use
626// a subrules more than once. All {}-brackets are simply replaced
627// by the corresponding conditions. The rules how conditions can
628// be written can be found in the class description of MDataChain.
629//
630Int_t MDataPhrase::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
631{
632 Bool_t rc = kFALSE;
633 if (!IsEnvDefined(env, prefix, "Rule", print))
634 return rc;
635
636 TString rule = GetEnvValue(env, prefix, "Rule", "");
637
638 Int_t idx=0;
639 while (1)
640 {
641 TString cond;
642 if (IsEnvDefined(env, prefix, Form("%d", idx), print))
643 {
644 cond += "(";
645 cond += GetEnvValue(env, prefix, Form("%d", idx), "");
646 cond += ")";
647 }
648
649 if (cond.IsNull())
650 break;
651
652 rule.ReplaceAll(Form("{%d}", idx), cond);
653 idx++;
654 }
655
656 if (rule.IsNull())
657 {
658 *fLog << warn << "MDataPhrase::ReadEnv - ERROR: Empty rule found." << endl;
659 return kERROR;
660 }
661
662 if (!SetRule(rule))
663 return kERROR;
664
665 if (print)
666 *fLog << inf << "found: " << GetRule() << endl;
667
668 return kTRUE;
669}
Note: See TracBrowser for help on using the repository browser.