@@ -2671,18 +2671,36 @@ class _FuncInfo(object):
2671
2671
* The direct function (direct)
2672
2672
* The inverse function (inverse)
2673
2673
* A boolean indicating whether the function
2674
- is bounded in the interval 0-1 (bounded_0_1)
2674
+ is bounded in the interval 0-1 (bounded_0_1), or
2675
+ a method that returns the information depending
2676
+ on this
2677
+ * A callable (check_params) that returns a bool specifying if a
2678
+ certain combination of parameters is valid.
2675
2679
2676
2680
"""
2677
- def __init__ (self , direct , inverse , bounded_0_1 ):
2681
+ def __init__ (self , direct , inverse , bounded_0_1 = True , check_params = None ):
2678
2682
self .direct = direct
2679
2683
self .inverse = inverse
2680
- self .bounded_0_1 = bounded_0_1
2681
2684
2682
- def copy (self ):
2683
- return _FuncInfo (self .direct ,
2684
- self .inverse ,
2685
- self .bounded_0_1 )
2685
+ if (hasattr (bounded_0_1 , '__call__' )):
2686
+ self ._bounded_0_1 = bounded_0_1
2687
+ else :
2688
+ self ._bounded_0_1 = lambda x : bounded_0_1
2689
+
2690
+ if check_params is None :
2691
+ self ._check_params = lambda x : True
2692
+ elif (hasattr (check_params , '__call__' )):
2693
+ self ._check_params = check_params
2694
+ else :
2695
+ raise ValueError ("Check params must be a callable, returning "
2696
+ "a boolean with the validity of the passed "
2697
+ "parameters or None." )
2698
+
2699
+ def is_bounded_0_1 (self , params = None ):
2700
+ return self ._bounded_0_1 (params )
2701
+
2702
+ def check_params (self , params = None ):
2703
+ return self ._check_params (params )
2686
2704
2687
2705
2688
2706
class _StringFuncParser (object ):
@@ -2697,40 +2715,49 @@ class _StringFuncParser(object):
2697
2715
_funcs ['linear' ] = _FuncInfo (lambda x : x ,
2698
2716
lambda x : x ,
2699
2717
True )
2700
- _funcs ['quadratic' ] = _FuncInfo (lambda x : x ** 2 ,
2701
- lambda x : x ** ( 1. / 2 ) ,
2718
+ _funcs ['quadratic' ] = _FuncInfo (np . square ,
2719
+ np . sqrt ,
2702
2720
True )
2703
2721
_funcs ['cubic' ] = _FuncInfo (lambda x : x ** 3 ,
2704
- lambda x : x ** ( 1. / 3 ) ,
2722
+ np . cbrt ,
2705
2723
True )
2706
- _funcs ['sqrt' ] = _FuncInfo (lambda x : x ** ( 1. / 2 ) ,
2707
- lambda x : x ** 2 ,
2724
+ _funcs ['sqrt' ] = _FuncInfo (np . sqrt ,
2725
+ np . square ,
2708
2726
True )
2709
- _funcs ['cbrt' ] = _FuncInfo (lambda x : x ** ( 1. / 3 ) ,
2727
+ _funcs ['cbrt' ] = _FuncInfo (np . cbrt ,
2710
2728
lambda x : x ** 3 ,
2711
2729
True )
2712
- _funcs ['log10' ] = _FuncInfo (lambda x : np .log10 ( x ) ,
2730
+ _funcs ['log10' ] = _FuncInfo (np .log10 ,
2713
2731
lambda x : (10 ** (x )),
2714
2732
False )
2715
- _funcs ['log' ] = _FuncInfo (lambda x : np .log ( x ) ,
2716
- lambda x : ( np .exp ( x )) ,
2733
+ _funcs ['log' ] = _FuncInfo (np .log ,
2734
+ np .exp ,
2717
2735
False )
2736
+ _funcs ['log2' ] = _FuncInfo (np .log2 ,
2737
+ lambda x : (2 ** x ),
2738
+ False )
2718
2739
_funcs ['x**{p}' ] = _FuncInfo (lambda x , p : x ** p [0 ],
2719
2740
lambda x , p : x ** (1. / p [0 ]),
2720
2741
True )
2721
2742
_funcs ['root{p}(x)' ] = _FuncInfo (lambda x , p : x ** (1. / p [0 ]),
2722
2743
lambda x , p : x ** p ,
2723
2744
True )
2745
+ _funcs ['log{p}(x)' ] = _FuncInfo (lambda x , p : (np .log (x ) /
2746
+ np .log (p [0 ])),
2747
+ lambda x , p : p [0 ]** (x ),
2748
+ False ,
2749
+ lambda p : p [0 ] > 0 )
2724
2750
_funcs ['log10(x+{p})' ] = _FuncInfo (lambda x , p : np .log10 (x + p [0 ]),
2725
2751
lambda x , p : 10 ** x - p [0 ],
2726
- True )
2752
+ lambda p : p [ 0 ] > 0 )
2727
2753
_funcs ['log(x+{p})' ] = _FuncInfo (lambda x , p : np .log (x + p [0 ]),
2728
2754
lambda x , p : np .exp (x ) - p [0 ],
2729
- True )
2755
+ lambda p : p [ 0 ] > 0 )
2730
2756
_funcs ['log{p}(x+{p})' ] = _FuncInfo (lambda x , p : (np .log (x + p [1 ]) /
2731
2757
np .log (p [0 ])),
2732
2758
lambda x , p : p [0 ]** (x ) - p [1 ],
2733
- True )
2759
+ lambda p : p [1 ] > 0 ,
2760
+ lambda p : p [0 ] > 0 )
2734
2761
2735
2762
def __init__ (self , str_func ):
2736
2763
"""
@@ -2749,82 +2776,88 @@ def __init__(self, str_func):
2749
2776
raise ValueError ("The argument passed is not a string." )
2750
2777
self ._str_func = str_func
2751
2778
self ._key , self ._params = self ._get_key_params ()
2752
- self ._func = self .get_func ()
2779
+ self ._func = self .func
2753
2780
2754
- def get_func (self ):
2781
+ @property
2782
+ def func (self ):
2755
2783
"""
2756
2784
Returns the _FuncInfo object, replacing the relevant parameters if
2757
2785
necessary in the lambda functions.
2758
2786
2759
2787
"""
2760
2788
2761
- func = self ._funcs [self ._key ]. copy ()
2762
- if len ( self ._params ) > 0 :
2789
+ func = self ._funcs [self ._key ]
2790
+ if self ._params :
2763
2791
m = func .direct
2764
- func .direct = (lambda x , m = m : m (x , self ._params ))
2792
+ direct = (lambda x , m = m : m (x , self ._params ))
2793
+
2765
2794
m = func .inverse
2766
- func .inverse = (lambda x , m = m : m (x , self ._params ))
2795
+ inverse = (lambda x , m = m : m (x , self ._params ))
2796
+
2797
+ is_bounded_0_1 = func .is_bounded_0_1 (self ._params )
2798
+
2799
+ func = _FuncInfo (direct , inverse ,
2800
+ is_bounded_0_1 )
2801
+ else :
2802
+ func = _FuncInfo (func .direct , func .inverse ,
2803
+ func .is_bounded_0_1 ())
2767
2804
return func
2768
2805
2769
- def get_directfunc (self ):
2806
+ @property
2807
+ def directfunc (self ):
2770
2808
"""
2771
2809
Returns the callable for the direct function.
2772
2810
2773
2811
"""
2774
2812
return self ._func .direct
2775
2813
2776
- def get_invfunc (self ):
2814
+ @property
2815
+ def invfunc (self ):
2777
2816
"""
2778
2817
Returns the callable for the inverse function.
2779
2818
2780
2819
"""
2781
2820
return self ._func .inverse
2782
2821
2822
+ @property
2783
2823
def is_bounded_0_1 (self ):
2784
2824
"""
2785
2825
Returns a boolean indicating if the function is bounded
2786
2826
in the [0-1 interval].
2787
2827
2788
2828
"""
2789
- return self ._func .bounded_0_1
2829
+ return self ._func .is_bounded_0_1 ()
2790
2830
2791
2831
def _get_key_params (self ):
2792
2832
str_func = six .text_type (self ._str_func )
2793
2833
# Checking if it comes with parameters
2794
2834
regex = '\{(.*?)\}'
2795
2835
params = re .findall (regex , str_func )
2796
2836
2797
- if len ( params ) > 0 :
2837
+ if params :
2798
2838
for i in range (len (params )):
2799
2839
try :
2800
2840
params [i ] = float (params [i ])
2801
2841
except :
2802
- raise ValueError ("'p' in parametric function strings must"
2842
+ raise ValueError ("Error with parameter number %i: '%s'. "
2843
+ "'p' in parametric function strings must "
2803
2844
" be replaced by a number that is not "
2804
- "zero, e.g. 'log10(x+{0.1})'." )
2845
+ "zero, e.g. 'log10(x+{0.1})'." %
2846
+ (i , params [i ]))
2805
2847
2806
- if params [i ] == 0 :
2807
- raise ValueError ("'p' in parametric function strings must"
2808
- " be replaced by a number that is not "
2809
- "zero." )
2810
2848
str_func = re .sub (regex , '{p}' , str_func )
2811
2849
2812
2850
try :
2813
2851
func = self ._funcs [str_func ]
2814
- except KeyError :
2815
- raise ValueError ("%s: invalid function. The only strings "
2816
- "recognized as functions are %s." %
2817
- (str_func , self .funcs .keys ()))
2818
2852
except :
2819
- raise ValueError ("Invalid function. The only strings recognized "
2820
- "as functions are %s." %
2821
- (self .funcs .keys ()))
2822
- if len (params ) > 0 :
2823
- func .direct (0.5 , params )
2824
- try :
2825
- func .direct (0.5 , params )
2826
- except :
2827
- raise ValueError ("Invalid parameters set for '%s'." %
2828
- (str_func ))
2853
+ raise ValueError ("%s: invalid string. The only strings "
2854
+ "recognized as functions are %s." %
2855
+ (str_func , self ._funcs .keys ()))
2856
+
2857
+ # Checking that the parameters are valid
2858
+ if not func .check_params (params ):
2859
+ raise ValueError ("%s: are invalid values for the parameters "
2860
+ "in %s." %
2861
+ (params , str_func ))
2829
2862
2830
2863
return str_func , params
0 commit comments