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

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