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

Last change on this file since 8300 was 8274, checked in by tbretz, 18 years ago
*** empty log message ***
File size: 19.9 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 p = 0;
285
286 // Now check for matrix elemets as M[5]
287 reg = TPRegexp("\\w+\\[\\d+\\]");
288 while (1)
289 {
290 // If some indices are already existing
291 // initialize them by a MDataValue
292 idx = CheckForVariable(phrase, idx);
293
294 // Check whether expression is found
295 if (reg.Match(phrase, mods, p, 130, &pos)==0)
296 break;
297
298 // Get expression from phrase
299 TString expr = phrase(pos[0], pos[1]-pos[0]);
300
301 // Add a corresponding MDataMember to our list
302 fMembers.AddLast(new MDataElement(expr));
303
304 // Make the expression "Regular expression proofed"
305 expr.ReplaceAll("[", "\\[");
306 expr.ReplaceAll("]", "\\]");
307
308 // Find other occurances of arg by this regexp
309 // and start next search behind first match
310 const TString regexp = Form("\\b%s", expr.Data());
311 p = pos[0] + Substitute(phrase, regexp, idx);
312
313 // Step forward to the next argument
314 idx++;
315 }
316
317 return phrase;
318}
319
320// --------------------------------------------------------------------------
321//
322// Clear Formula and corresponding data members
323//
324void MDataPhrase::Clear(Option_t *)
325{
326 fMembers.Delete();
327 if (!fFormula)
328 return;
329
330 delete fFormula;
331 fFormula = NULL;
332}
333
334// --------------------------------------------------------------------------
335//
336// Set a new phrase/rule. Returns kTRUE on sucess, kFALSE otherwise
337//
338Bool_t MDataPhrase::SetRule(const TString &rule)
339{
340 Clear();
341
342 const TString txt=Parse(rule);
343 if (txt.IsNull())
344 {
345 Clear();
346 return kFALSE;
347 }
348
349 fFormula = new TFormula;
350
351 // Must have a name otherwise all axis labels disappear like a miracle
352 fFormula->SetName(fName.IsNull()?"TFormula":fName.Data());
353 if (fFormula->Compile(txt))
354 {
355 *fLog << err << dbginf << "Syntax Error: TFormula::Compile failed..."<< endl;
356 *fLog << " Full Rule: " << rule << endl;
357 *fLog << " Parsed Rule: " << txt << endl;
358 Clear();
359 return kFALSE;
360 }
361
362 gROOT->GetListOfFunctions()->Remove(fFormula);
363
364 return kTRUE;
365}
366
367namespace MFastFun {
368 //
369 // Namespace with basic primitive functions registered by TFormulaPrimitive
370 // all function registerd by TFormulaPrimitive can be used in TFormula
371 //
372 Double_t Nint(Double_t x){return TMath::Nint(x);}
373 Double_t Sign(Double_t x){return x<0?-1:+1;}
374 Double_t IsNaN(Double_t x){return TMath::IsNaN(x);}
375 Double_t Finite(Double_t x){return TMath::Finite(x);}
376}
377
378// --------------------------------------------------------------------------
379//
380// Default constructor. Set a rule (phrase), see class description for more
381// details. Set a name and title. If no title is given it is set to the rule.
382//
383MDataPhrase::MDataPhrase(const char *rule, const char *name, const char *title) : fFormula(0)
384{
385 // More in TFormulaPrimitive.cxx
386 // More in TFormulaMathInterface
387 if (!TFormulaPrimitive::FindFormula("isnan"))
388 {
389 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("log2", "log2", (TFormulaPrimitive::GenFunc10)TMath::Log2));
390 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("fabs", "fabs", (TFormulaPrimitive::GenFunc10)TMath::Abs));
391 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("floor", "floor", (TFormulaPrimitive::GenFunc10)TMath::Floor));
392 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("ceil", "ceil", (TFormulaPrimitive::GenFunc10)TMath::Ceil));
393
394 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("nint", "nint", (TFormulaPrimitive::GenFunc10)MFastFun::Nint));
395 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("round", "round", (TFormulaPrimitive::GenFunc10)MFastFun::Nint));
396 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("sgn", "sgn", (TFormulaPrimitive::GenFunc10)MFastFun::Sign));
397
398 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("isnan", "isnan", (TFormulaPrimitive::GenFunc10)MFastFun::IsNaN));
399 TFormulaPrimitive::AddFormula(new TFormulaPrimitive("finite", "finite", (TFormulaPrimitive::GenFunc10)MFastFun::Finite));
400 }
401
402 // TFormulaPrimitive is used to get direct acces to the function pointers
403 // GenFunc - pointers to the static function
404 // TFunc - pointers to the data member functions
405 //
406 // The following sufixes are currently used, to describe function arguments:
407 // ------------------------------------------------------------------------
408 // G - generic layout - pointer to double (arguments), pointer to double (parameters)
409 // 10 - double
410 // 110 - double, double
411 // 1110 - double, double, double
412
413 fName = name ? name : "MDataPhrase";
414 fTitle = title ? title : rule;
415
416 fMembers.SetOwner();
417
418 if (rule)
419 SetRule(rule);
420}
421
422// --------------------------------------------------------------------------
423//
424// Destructor
425//
426MDataPhrase::~MDataPhrase()
427{
428 if (fFormula)
429 delete fFormula;
430}
431
432// --------------------------------------------------------------------------
433//
434// CopyConstructor
435//
436MDataPhrase::MDataPhrase(MDataPhrase &ts)
437{
438 TFormula *f = ts.fFormula;
439
440 fName = "MDataPhrase";
441 fTitle = f ? f->GetExpFormula() : (TString)"";
442
443 fFormula = f ? (TFormula*)f->Clone() : 0;
444 gROOT->GetListOfFunctions()->Remove(fFormula);
445
446 fMembers.SetOwner();
447
448 TObject *o = NULL;
449 TIter Next(&ts.fMembers);
450 while ((o=Next()))
451 fMembers.AddLast(o->Clone());
452}
453
454// --------------------------------------------------------------------------
455//
456// Evaluates and returns the result of the member list.
457//
458Double_t MDataPhrase::GetValue() const
459{
460 const Int_t n = fMembers.GetEntriesFast();
461
462 if (fMembers.GetSize()<n)
463 {
464 *fLog << err << "ERROR - MDataPhrase::GetValue: Size mismatch!" << endl;
465 return 0;
466 }
467
468 // This is to get rid of the cost-qualifier for this->fStorage
469 Double_t *arr = fStorage.GetArray();
470
471 // Evaluate parameters (the access with TObjArray::UncheckedAt is
472 // roughly two times faster than with a TIter and rougly three
473 // times than accessing a TOrdCollection. However this might still
474 // be quite harmless compared to the time needed by GetValue)
475 for (Int_t i=0; i<n; i++)
476 arr[i] = static_cast<MData*>(fMembers.UncheckedAt(i))->GetValue();
477
478 // Evaluate function
479 return fFormula->EvalPar(0, arr);
480}
481
482// --------------------------------------------------------------------------
483//
484// Returns kTRUE if all members of fMemebers are valid and fFormula!=NULL
485//
486Bool_t MDataPhrase::IsValid() const
487{
488 TIter Next(&fMembers);
489
490 MData *data = NULL;
491 while ((data=(MData*)Next()))
492 if (!data->IsValid())
493 return kFALSE;
494
495 return fFormula ? kTRUE : kFALSE;
496}
497
498// --------------------------------------------------------------------------
499//
500// Checks whether at least one member has the ready-to-save flag.
501//
502Bool_t MDataPhrase::IsReadyToSave() const
503{
504 TIter Next(&fMembers);
505
506 MData *data = NULL;
507
508 while ((data=(MData*)Next()))
509 if (data->IsReadyToSave())
510 return kTRUE;
511
512 return kFALSE;
513}
514
515// --------------------------------------------------------------------------
516//
517// PreProcesses all members in the list
518//
519Bool_t MDataPhrase::PreProcess(const MParList *plist)
520{
521 if (!fFormula)
522 {
523 *fLog << err << "Error - So far no valid phrase was setup." << endl;
524 return kFALSE;
525 }
526
527 TIter Next(&fMembers);
528
529 MData *member=NULL;
530
531 //
532 // loop over all members
533 //
534 while ((member=(MData*)Next()))
535 if (!member->PreProcess(plist))
536 {
537 *fLog << err << "Error - Preprocessing Data Member ";
538 *fLog << member->GetName() << " in " << fName << endl;
539 return kFALSE;
540 }
541
542 // For speed reasons MArrayD instead of TArrayD is used
543 // (no range check is done when accessing). The storage is
544 // allocated (Set) only when its size is changing. If GetValue
545 // is called many times this should improve the speed significantly
546 // because the necessary memory is already allocated and doesn't need
547 // to be freed. (Just a static variable is not enough, because there
548 // may be several independant objects of this class)
549 fStorage.Set(fMembers.GetSize());
550
551 return kTRUE;
552}
553
554// --------------------------------------------------------------------------
555//
556// Builds a rule from all the list members. This is a rule which could
557// be used to rebuild the list using the constructor of a MDataChain
558//
559TString MDataPhrase::GetRule() const
560{
561 if (!fFormula)
562 return "<empty>";
563
564 TString rule = fFormula->GetTitle(); //fFormula->GetExpFormula();
565
566 MData *member = NULL;
567
568 Int_t i=0;
569 TIter Next(&fMembers);
570 while ((member=(MData*)Next()))
571 {
572 TString r = member->GetRule();
573 r.ReplaceAll("]", "\\]");
574 rule.ReplaceAll(Form("[%d]", i++), r);
575 }
576 rule.ReplaceAll("\\]", "]");
577
578 return rule;
579}
580/*
581// --------------------------------------------------------------------------
582//
583// Return a comma seperated list of all data members used in the chain.
584// This is mainly used in MTask::AddToBranchList
585//
586TString MDataPhrase::GetDataMember() const
587{
588 TString str;
589
590 TIter Next(&fMembers);
591
592 MData *member=(MData*)Next();
593
594 if (!member->GetDataMember().IsNull())
595 str += member->GetDataMember();
596
597 while ((member=(MData*)Next()))
598 {
599 if (!member->GetDataMember().IsNull())
600 {
601 str += ",";
602 str += member->GetDataMember();
603 }
604 }
605
606 return str;
607}
608*/
609void MDataPhrase::SetVariables(const TArrayD &arr)
610{
611 fMembers.R__FOR_EACH(MData, SetVariables)(arr);
612}
613
614// --------------------------------------------------------------------------
615//
616// Check for corresponding entries in resource file and setup data phrase.
617//
618// Assuming your MDataChain is called (Set/GetName): MyData
619//
620// Now setup the condition, eg:
621// MyData.Rule: log10(MHillas.fSize)
622// or
623// MyData.Rule: log10(MHillas.fSize) - 4.1
624//
625// If you want to use more difficult rules you can split the
626// condition into subrules. Subrules are identified
627// by {}-brackets. Avoid trailing 0's! For example:
628//
629// MyData.Rule: log10(MHillas.fSize) + {0} - {1}
630// MyData.0: 5.5*MHillas.fSize
631// MyData.1: 2.3*log10(MHillas.fSize)
632//
633// The numbering must be continous and start with 0. You can use
634// a subrules more than once. All {}-brackets are simply replaced
635// by the corresponding conditions. The rules how conditions can
636// be written can be found in the class description of MDataChain.
637//
638Int_t MDataPhrase::ReadEnv(const TEnv &env, TString prefix, Bool_t print)
639{
640 Bool_t rc = kFALSE;
641 if (!IsEnvDefined(env, prefix, "Rule", print))
642 return rc;
643
644 TString rule = GetEnvValue(env, prefix, "Rule", "");
645
646 Int_t idx=0;
647 while (1)
648 {
649 TString cond;
650 if (IsEnvDefined(env, prefix, Form("%d", idx), print))
651 {
652 cond += "(";
653 cond += GetEnvValue(env, prefix, Form("%d", idx), "");
654 cond += ")";
655 }
656
657 if (cond.IsNull())
658 break;
659
660 rule.ReplaceAll(Form("{%d}", idx), cond);
661 idx++;
662 }
663
664 if (rule.IsNull())
665 {
666 *fLog << warn << "MDataPhrase::ReadEnv - ERROR: Empty rule found." << endl;
667 return kERROR;
668 }
669
670 if (!SetRule(rule))
671 return kERROR;
672
673 if (print)
674 *fLog << inf << "found: " << GetRule() << endl;
675
676 return kTRUE;
677}
Note: See TracBrowser for help on using the repository browser.