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

Last change on this file since 8681 was 8635, checked in by tbretz, 17 years ago
*** empty log message ***
File size: 20.6 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 TString mods;
141 TArrayI pos;
142
143 while (1)
144 {
145 // \\W: matches any non-word character [^a-zA-Z_0-9]
146 TPRegexp reg(Form("\\W\\[0*%d\\]\\W", idx));
147 if (reg.Match(phrase, mods, 0, 130, &pos)==0)
148 break;
149
150 // [idx] already existing. Add a corresponding MDataValue
151 fMembers.AddLast(new MDataValue(0, idx));
152 idx++;
153 }
154
155 return idx;
156}
157
158// --------------------------------------------------------------------------
159//
160// Replace all expressions expr (found by a regular expression \\b%s\\b
161// with %s being the expression) in the string phrase by [idx].
162//
163// The length of [idx] is returned.
164//
165Int_t MDataPhrase::Substitute(TString &phrase, const TString &expr, Int_t idx) const
166{
167 const TString arg = Form("[%d]", idx);
168
169 TPRegexp reg(expr);
170
171 TString mods;
172 TArrayI pos;
173
174 Int_t p = 0;
175 while (1)
176 {
177 // check whether argument is found
178 if (reg.Match(phrase, mods, p, 130, &pos)==0)
179 break;
180
181 // Replace expression by argument [idx]
182 phrase.Replace(pos[0], pos[1]-pos[0], arg, arg.Length());
183
184 // Jump behind the new string which was just inserted
185 p = pos[0]+arg.Length();
186 }
187 return arg.Length();
188}
189
190TString MDataPhrase::Parse(TString phrase)
191{
192 // This is for backward compatibility with MDataChain
193 phrase.ReplaceAll("gRandom->", "TRandom::");
194 phrase.ReplaceAll("kRad2Deg", "TMath::RadToDeg()");
195 phrase.ReplaceAll("kDeg2Rad", "TMath::DegToRad()");
196 phrase.ReplaceAll(" ", "");
197
198 int idx=0;
199 int p =0;
200
201 TString mods;
202 TArrayI pos;
203/*
204 // Find all functions...
205 // The first \\w+ should also allow ::
206 TPRegexp reg = TPRegexp("[[:word:]:]+\\([^()]*\\)");
207 while (1)
208 {
209 //idx = CheckForVariables(phrase, idx);
210
211 if (reg.Match(phrase, mods, p, 130, &pos)==0)
212 break;
213
214 const Int_t len = pos[1]-pos[0];
215
216 // This is the full function with its argument(s)
217 const TString term = phrase(pos[0], len);
218
219 // Now get rid of the argument(s)
220 const TPRegexp reg3("\\([^()]+\\)");
221
222 TString func(term);
223 reg3.Substitute(func, "");
224
225 // Seems to be a special case whic is not handles by
226 // TFormulaPrimitive by known by TFormula
227 if (func.BeginsWith("TRandom::"))
228 {
229 p = pos[0]+pos[1];
230 continue;
231 }
232
233 // check whether the function is available
234 if (TFormulaPrimitive::FindFormula(func))
235 {
236 p = pos[0]+pos[1];
237 continue;
238 }
239
240 cout << "Unknown: " << func << endl;
241 // p = pos[0]+pos[1];
242 return;
243 }
244*/
245
246 p = 0;
247
248 // Find all data-members in expression such as
249 // MTest.fDataMember. Because also floating point
250 // numbers can contain a dot the result has to
251 // be checked carefully
252 // \\w: matches any word character [a-zA-Z_0-9]
253 TPRegexp reg = TPRegexp("\\w+[.]\\w+");
254 TPRegexp ishex("^0x[[:xdigit:]]+$");
255
256 while (1)
257 {
258 // If some indices are already existing
259 // initialize them by a flexible MDataValue
260 idx = CheckForVariable(phrase, idx);
261
262 // Check whether expression is found
263 if (reg.Match(phrase, mods, p, 130, &pos)==0)
264 break;
265
266 // Get expression from phrase
267 const TString expr = phrase(pos[0], pos[1]-pos[0]);
268
269 // Also hex-numbers and floats fullfill our condition...
270 if (!expr(ishex).IsNull() || expr.IsFloat())
271 {
272 p = pos[1];
273 continue;
274 }
275
276 // Add a corresponding MDataMember to our list
277 fMembers.AddLast(new MDataMember(expr));
278
279 // Find other occurances of arg by this regexp
280 // and start next search behind first match
281 const TString regexp = Form("\\b%s\\b", expr.Data());
282 p = pos[0] + Substitute(phrase, regexp, idx);
283
284 // Step forward to the next argument
285 idx++;
286 }
287
288 p = 0;
289
290 // Now check for matrix elemets as M[5]
291 reg = TPRegexp("\\w+\\[\\d+\\]");
292 while (1)
293 {
294 // If some indices are already existing
295 // initialize them by a MDataValue
296 idx = CheckForVariable(phrase, idx);
297
298 // Check whether expression is found
299 if (reg.Match(phrase, mods, p, 130, &pos)==0)
300 break;
301
302 // Get expression from phrase
303 TString expr = phrase(pos[0], pos[1]-pos[0]);
304
305 // Add a corresponding MDataMember to our list
306 fMembers.AddLast(new MDataElement(expr));
307
308 // Make the expression "Regular expression proofed"
309 expr.ReplaceAll("[", "\\[");
310 expr.ReplaceAll("]", "\\]");
311
312 // Find other occurances of arg by this regexp
313 // and start next search behind first match
314 const TString regexp = Form("\\b%s", expr.Data());
315 p = pos[0] + Substitute(phrase, regexp, idx);
316
317 // Step forward to the next argument
318 idx++;
319 }
320
321 // Now we have to check if there are additional indices [idx]
322 // This is mainly important if the rule has indices only!
323 while (1)
324 {
325 const Int_t newidx = CheckForVariable(phrase, idx);
326 if (newidx == idx)
327 break;
328 }
329
330 return phrase;
331}
332
333// --------------------------------------------------------------------------
334//
335// Clear Formula and corresponding data members
336//
337void MDataPhrase::Clear(Option_t *)
338{
339 fMembers.Delete();
340 if (!fFormula)
341 return;
342
343 delete fFormula;
344 fFormula = NULL;
345}
346
347// --------------------------------------------------------------------------
348//
349// Set a new phrase/rule. Returns kTRUE on sucess, kFALSE otherwise
350//
351Bool_t MDataPhrase::SetRule(const TString &rule)
352{
353 Clear();
354
355 const TString txt=Parse(rule);
356 if (txt.IsNull())
357 {
358 Clear();
359 return kFALSE;
360 }
361
362 fFormula = new TFormula;
363
364 // Must have a name otherwise all axis labels disappear like a miracle
365 fFormula->SetName(fName.IsNull()?"TFormula":fName.Data());
366 if (fFormula->Compile(txt))
367 {
368 *fLog << err << dbginf << "Syntax Error: TFormula::Compile failed..."<< endl;
369 *fLog << " Full Rule: " << rule << endl;
370 *fLog << " Parsed Rule: " << txt << endl;
371 Clear();
372 return kFALSE;
373 }
374
375 gROOT->GetListOfFunctions()->Remove(fFormula);
376
377 return kTRUE;
378}
379
380namespace MFastFun {
381 //
382 // Namespace with basic primitive functions registered by TFormulaPrimitive
383 // all function registerd by TFormulaPrimitive can be used in TFormula
384 //
385 Double_t Nint(Double_t x){return TMath::Nint(x);}
386 Double_t Sign(Double_t x){return x<0?-1:+1;}
387 Double_t IsNaN(Double_t x){return TMath::IsNaN(x);}
388 Double_t Finite(Double_t x){return TMath::Finite(x);}
389}
390
391// --------------------------------------------------------------------------
392//
393// Default constructor. Set a rule (phrase), see class description for more
394// details. Set a name and title. If no title is given it is set to the rule.
395//
396MDataPhrase::MDataPhrase(const char *rule, const char *name, const char *title) : fFormula(0)
397{
398 // More in TFormulaPrimitive.cxx
399 // More in TFormulaMathInterface
400 if (!TFormulaPrimitive::FindFormula("isnan"))
401 {
402 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("log2", "log2", (TFormulaPrimitive::GenFunc10)TMath::Log2));
403 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("fabs", "fabs", (TFormulaPrimitive::GenFunc10)TMath::Abs));
404 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("floor", "floor", (TFormulaPrimitive::GenFunc10)TMath::Floor));
405 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("ceil", "ceil", (TFormulaPrimitive::GenFunc10)TMath::Ceil));
406
407 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("nint", "nint", (TFormulaPrimitive::GenFunc10)MFastFun::Nint));
408 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("round", "round", (TFormulaPrimitive::GenFunc10)MFastFun::Nint));
409 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("sgn", "sgn", (TFormulaPrimitive::GenFunc10)MFastFun::Sign));
410
411 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("isnan", "isnan", (TFormulaPrimitive::GenFunc10)MFastFun::IsNaN));
412 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("finite", "finite", (TFormulaPrimitive::GenFunc10)MFastFun::Finite));
413 }
414
415 // TFormulaPrimitive is used to get direct acces to the function pointers
416 // GenFunc - pointers to the static function
417 // TFunc - pointers to the data member functions
418 //
419 // The following sufixes are currently used, to describe function arguments:
420 // ------------------------------------------------------------------------
421 // G - generic layout - pointer to double (arguments), pointer to double (parameters)
422 // 10 - double
423 // 110 - double, double
424 // 1110 - double, double, double
425
426 fName = name ? name : "MDataPhrase";
427 fTitle = title ? title : rule;
428
429 fMembers.SetOwner();
430
431 if (rule)
432 SetRule(rule);
433}
434
435// --------------------------------------------------------------------------
436//
437// Destructor
438//
439MDataPhrase::~MDataPhrase()
440{
441 if (fFormula)
442 delete fFormula;
443}
444
445// --------------------------------------------------------------------------
446//
447// CopyConstructor
448//
449MDataPhrase::MDataPhrase(MDataPhrase &ts)
450{
451 TFormula *f = ts.fFormula;
452
453 fName = "MDataPhrase";
454 fTitle = f ? f->GetExpFormula() : (TString)"";
455
456 fFormula = f ? (TFormula*)f->Clone() : 0;
457 gROOT->GetListOfFunctions()->Remove(fFormula);
458
459 fMembers.SetOwner();
460
461 TObject *o = NULL;
462 TIter Next(&ts.fMembers);
463 while ((o=Next()))
464 fMembers.AddLast(o->Clone());
465}
466
467// --------------------------------------------------------------------------
468//
469// Evaluates and returns the result of the member list.
470//
471Double_t MDataPhrase::GetValue() const
472{
473 const Int_t n = fMembers.GetEntriesFast();
474
475 if (fMembers.GetSize()<n)
476 {
477 *fLog << err << "ERROR - MDataPhrase::GetValue: Size mismatch!" << endl;
478 return 0;
479 }
480
481 // This is to get rid of the cost-qualifier for this->fStorage
482 Double_t *arr = fStorage.GetArray();
483
484 // Evaluate parameters (the access with TObjArray::UncheckedAt is
485 // roughly two times faster than with a TIter and rougly three
486 // times than accessing a TOrdCollection. However this might still
487 // be quite harmless compared to the time needed by GetValue)
488 for (Int_t i=0; i<n; i++)
489 arr[i] = static_cast<MData*>(fMembers.UncheckedAt(i))->GetValue();
490
491 // Evaluate function
492 return fFormula->EvalPar(0, arr);
493}
494
495// --------------------------------------------------------------------------
496//
497// Returns kTRUE if all members of fMemebers are valid and fFormula!=NULL
498//
499Bool_t MDataPhrase::IsValid() const
500{
501 TIter Next(&fMembers);
502
503 MData *data = NULL;
504 while ((data=(MData*)Next()))
505 if (!data->IsValid())
506 return kFALSE;
507
508 return fFormula ? kTRUE : kFALSE;
509}
510
511// --------------------------------------------------------------------------
512//
513// Checks whether at least one member has the ready-to-save flag.
514//
515Bool_t MDataPhrase::IsReadyToSave() const
516{
517 TIter Next(&fMembers);
518
519 MData *data = NULL;
520
521 while ((data=(MData*)Next()))
522 if (data->IsReadyToSave())
523 return kTRUE;
524
525 return kFALSE;
526}
527
528// --------------------------------------------------------------------------
529//
530// PreProcesses all members in the list
531//
532Bool_t MDataPhrase::PreProcess(const MParList *plist)
533{
534 if (!fFormula)
535 {
536 *fLog << err << "Error - So far no valid phrase was setup." << endl;
537 return kFALSE;
538 }
539
540 TIter Next(&fMembers);
541
542 MData *member=NULL;
543
544 //
545 // loop over all members
546 //
547 while ((member=(MData*)Next()))
548 if (!member->PreProcess(plist))
549 {
550 *fLog << err << "Error - Preprocessing Data Member ";
551 *fLog << member->GetName() << " in " << fName << endl;
552 return kFALSE;
553 }
554
555 // For speed reasons MArrayD instead of TArrayD is used
556 // (no range check is done when accessing). The storage is
557 // allocated (Set) only when its size is changing. If GetValue
558 // is called many times this should improve the speed significantly
559 // because the necessary memory is already allocated and doesn't need
560 // to be freed. (Just a static variable is not enough, because there
561 // may be several independant objects of this class)
562 fStorage.Set(fMembers.GetSize());
563
564 return kTRUE;
565}
566
567// --------------------------------------------------------------------------
568//
569// Builds a rule from all the list members. This is a rule which could
570// be used to rebuild the list using the constructor of a MDataChain
571//
572TString MDataPhrase::GetRule() const
573{
574 if (!fFormula)
575 return "<empty>";
576
577 TString rule = fFormula->GetTitle(); //fFormula->GetExpFormula();
578
579 MData *member = NULL;
580
581 Int_t i=0;
582 TIter Next(&fMembers);
583 while ((member=(MData*)Next()))
584 {
585 TString r = member->GetRule();
586 r.ReplaceAll("]", "\\]");
587 rule.ReplaceAll(Form("[%d]", i++), r);
588 }
589 rule.ReplaceAll("\\]", "]");
590
591 return rule;
592}
593
594// --------------------------------------------------------------------------
595//
596// This returns the rule as seen/interpreted by the TFormula. Mainly
597// for debugging purposes
598//
599TString MDataPhrase::GetRuleRaw() const
600{
601 if (!fFormula)
602 return "<empty>";
603
604 return fFormula->GetExpFormula();
605}
606
607/*
608// --------------------------------------------------------------------------
609//
610// Return a comma seperated list of all data members used in the chain.
611// This is mainly used in MTask::AddToBranchList
612//
613TString MDataPhrase::GetDataMember() const
614{
615 TString str;
616
617 TIter Next(&fMembers);
618
619 MData *member=(MData*)Next();
620
621 if (!member->GetDataMember().IsNull())
622 str += member->GetDataMember();
623
624 while ((member=(MData*)Next()))
625 {
626 if (!member->GetDataMember().IsNull())
627 {
628 str += ",";
629 str += member->GetDataMember();
630 }
631 }
632
633 return str;
634}
635*/
636void MDataPhrase::SetVariables(const TArrayD &arr)
637{
638 fMembers.R__FOR_EACH(MData, SetVariables)(arr);
639}
640
641// --------------------------------------------------------------------------
642//
643// Check for corresponding entries in resource file and setup data phrase.
644//
645// Assuming your MDataChain is called (Set/GetName): MyData
646//
647// Now setup the condition, eg:
648// MyData.Rule: log10(MHillas.fSize)
649// or
650// MyData.Rule: log10(MHillas.fSize) - 4.1
651//
652// If you want to use more difficult rules you can split the
653// condition into subrules. Subrules are identified
654// by {}-brackets. Avoid trailing 0's! For example:
655//
656// MyData.Rule: log10(MHillas.fSize) + {0} - {1}
657// MyData.0: 5.5*MHillas.fSize
658// MyData.1: 2.3*log10(MHillas.fSize)
659//
660// The numbering must be continous and start with 0. You can use
661// a subrules more than once. All {}-brackets are simply replaced
662// by the corresponding conditions. The rules how conditions can
663// be written can be found in the class description of MDataChain.
664//
665Int_t MDataPhrase::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
666{
667 Bool_t rc = kFALSE;
668 if (!IsEnvDefined(env, prefix, "Rule", print))
669 return rc;
670
671 TString rule = GetEnvValue(env, prefix, "Rule", "");
672
673 Int_t idx=0;
674 while (1)
675 {
676 TString cond;
677 if (IsEnvDefined(env, prefix, Form("%d", idx), print))
678 {
679 cond += "(";
680 cond += GetEnvValue(env, prefix, Form("%d", idx), "");
681 cond += ")";
682 }
683
684 if (cond.IsNull())
685 break;
686
687 rule.ReplaceAll(Form("{%d}", idx), cond);
688 idx++;
689 }
690
691 if (rule.IsNull())
692 {
693 *fLog << warn << "MDataPhrase::ReadEnv - ERROR: Empty rule found." << endl;
694 return kERROR;
695 }
696
697 if (!SetRule(rule))
698 return kERROR;
699
700 if (print)
701 *fLog << inf << "found: " << GetRule() << endl;
702
703 return kTRUE;
704}
Note: See TracBrowser for help on using the repository browser.