Before I do something, because i dont understand: "keep the returning of the data structure for the branch!".
I will: Kill the looping in the library function of B14, and CR72, and TSMFK01. Return one large array with 1e100, if any violation of the math domain. I will make a return in the library function, and then updating the class object: Best troels 2014-05-09 16:07 GMT+02:00 Edward d'Auvergne <[email protected]>: > That might work - but it will require testing. I really don't know > what will happen. The current test suite should be sufficient for the > testing. Also, keep the returning of the data structure for the > branch! > > Regards, > > Edward > > On 9 May 2014 16:04, Troels Emtekær Linnet <[email protected]> wrote: >> Would you agree on discarding the whole loop, return one array with >> all values = 1e100, >> rather than some elements have 1e100, but other rather high values.? >> >> Best >> Troels >> >> 2014-05-09 15:58 GMT+02:00 Edward d'Auvergne <[email protected]>: >>> Hi, >>> >>> To set up a branch, just read the 3 small sections of the 'Branches' >>> section of the user manual >>> (http://www.nmr-relax.com/manual/Branches.html). Using git here is >>> fatal. But all the commands you need are listed there. Try the CR72 >>> optimisations first though. Those will then make the API changes much >>> easier. >>> >>> Regards, >>> >>> Edward >>> >>> >>> >>> On 9 May 2014 15:34, Troels Emtekær Linnet <[email protected]> wrote: >>>> How do I setup a branch? :-) >>>> >>>> Best >>>> Troels >>>> >>>> 2014-05-09 15:31 GMT+02:00 Troels Emtekær Linnet <[email protected]>: >>>>> Hi Edward. >>>>> >>>>> I really think for my case, that 25 speed up is a deal breaker ! >>>>> I have so much data to crunch, that 25 speed is absolutely perfect. >>>>> >>>>> I would only optimise this for CR72, and TSMFK01, since these are the >>>>> ones I need now. >>>>> And the change of code is only 3-5 lines? >>>>> >>>>> And i was thinking of one thing more. >>>>> >>>>> CR72 always go over loop. >>>>> >>>>> ----------- >>>>> # Loop over the time points, back calculating the R2eff values. >>>>> for i in range(num_points): >>>>> # The full eta+/- values. >>>>> etapos = etapos_part / cpmg_frqs[i] >>>>> etaneg = etaneg_part / cpmg_frqs[i] >>>>> >>>>> # Catch large values of etapos going into the cosh function. >>>>> if etapos > 100: >>>>> back_calc[i] = 1e100 >>>>> continue >>>>> >>>>> # The arccosh argument - catch invalid values. >>>>> fact = Dpos * cosh(etapos) - Dneg * cos(etaneg) >>>>> if fact < 1.0: >>>>> back_calc[i] = r20_kex >>>>> continue >>>>> >>>>> # The full formula. >>>>> back_calc[i] = r20_kex - cpmg_frqs[i] * arccosh(fact) >>>>> ------------ >>>>> I would rather do: >>>>> etapos = etapos_part / cpmg_frqs >>>>> >>>>> And then check for nan values. >>>>> If any of these are there, just return the whole array with 1e100, >>>>> instead of single values. >>>>> That would replace a loop with a check. >>>>> >>>>> Best >>>>> Troels >>>>> >>>>> >>>>> 2014-05-09 14:58 GMT+02:00 Edward d'Auvergne <[email protected]>: >>>>>> Hi, >>>>>> >>>>>> This approach can add a little speed. You really need to stress test >>>>>> and have profile timings to understand. You should also try different >>>>>> Python versions (2 and 3) because each implementation is different. >>>>>> You can sometimes have a speed up in Python 2 which does nothing in >>>>>> Python 3 (due to Python 3 being more optimised). There can also be >>>>>> huge differences between numpy versions. Anyway, here is a powerful >>>>>> test which shows 3 different implementation ideas for the >>>>>> back-calculated R2eff data in the dispersion functions: >>>>>> >>>>>> """ >>>>>> import cProfile as profile >>>>>> from numpy import array, cos, float64, sin, zeros >>>>>> import pstats >>>>>> >>>>>> def in_place(values, bc): >>>>>> x = cos(values) * sin(values) >>>>>> for i in range(len(bc)): >>>>>> bc[i] = x[i] >>>>>> >>>>>> def really_slow(values, bc): >>>>>> for i in range(len(bc)): >>>>>> x = cos(values[i]) * sin(values[i]) >>>>>> bc[i] = x >>>>>> >>>>>> def return_bc(values): >>>>>> return cos(values) * sin(values) >>>>>> >>>>>> def test_in_place(inc=None, values=None, values2=None, bc=None): >>>>>> for i in range(inc): >>>>>> in_place(values, bc[0]) >>>>>> in_place(values2, bc[1]) >>>>>> print(bc) >>>>>> >>>>>> def test_really_slow(inc=None, values=None, values2=None, bc=None): >>>>>> for i in range(inc): >>>>>> really_slow(values, bc[0]) >>>>>> really_slow(values2, bc[1]) >>>>>> print(bc) >>>>>> >>>>>> def test_return_bc(inc=None, values=None, values2=None, bc=None): >>>>>> for i in range(inc): >>>>>> bc[0] = return_bc(values) >>>>>> bc[1] = return_bc(values2) >>>>>> print(bc) >>>>>> >>>>>> def test(): >>>>>> values = array([1, 3, 0.1], float64) >>>>>> values2 = array([0.1, 0.2, 0.3], float64) >>>>>> bc = zeros((2, 3), float64) >>>>>> inc = 1000000 >>>>>> test_in_place(inc=inc, values=values, values2=values2, bc=bc) >>>>>> test_really_slow(inc=inc, values=values, values2=values2, bc=bc) >>>>>> test_return_bc(inc=inc, values=values, values2=values2, bc=bc) >>>>>> >>>>>> def print_stats(stats, status=0): >>>>>> pstats.Stats(stats).sort_stats('time', 'name').print_stats() >>>>>> profile.Profile.print_stats = print_stats >>>>>> profile.runctx('test()', globals(), locals()) >>>>>> """" >>>>>> >>>>>> >>>>>> Try running this in Python 2 and 3. If the cProfile import does not >>>>>> work on one version, try simply "import profile". You should create >>>>>> such scripts for testing out code optimisation ideas. Knowing how to >>>>>> profile is essential. For Python 3, I see: >>>>>> >>>>>> """ >>>>>> $ python3.4 edward.py >>>>>> [[ 0.45464871 -0.13970775 0.09933467] >>>>>> [ 0.09933467 0.19470917 0.28232124]] >>>>>> [[ 0.45464871 -0.13970775 0.09933467] >>>>>> [ 0.09933467 0.19470917 0.28232124]] >>>>>> [[ 0.45464871 -0.13970775 0.09933467] >>>>>> [ 0.09933467 0.19470917 0.28232124]] >>>>>> 10001042 function calls (10001036 primitive calls) in 39.303 >>>>>> seconds >>>>>> >>>>>> Ordered by: internal time, function name >>>>>> >>>>>> ncalls tottime percall cumtime percall filename:lineno(function) >>>>>> 2000000 19.744 0.000 19.867 0.000 edward.py:10(really_slow) >>>>>> 2000000 9.128 0.000 9.286 0.000 edward.py:5(in_place) >>>>>> 2000000 5.966 0.000 5.966 0.000 edward.py:15(return_bc) >>>>>> 1 1.964 1.964 7.931 7.931 >>>>>> edward.py:30(test_return_bc) >>>>>> 1 1.119 1.119 20.987 20.987 >>>>>> edward.py:24(test_really_slow) >>>>>> 1 1.099 1.099 10.385 10.385 edward.py:18(test_in_place) >>>>>> 4000198 0.281 0.000 0.281 0.000 {built-in method len} >>>>>> 27 0.001 0.000 0.001 0.000 {method 'reduce' of >>>>>> 'numpy.ufunc' objects} >>>>>> """ >>>>>> >>>>>> Here you can see that test_return_bc() is 80% the speed of >>>>>> test_in_place(). The 'cumtime' is the important number, this is the >>>>>> total amount of time spent in that function. So the speed up is not >>>>>> huge. For Python 2: >>>>>> >>>>>> """ >>>>>> $ python2.7 edward.py >>>>>> [[ 0.45464871 -0.13970775 0.09933467] >>>>>> [ 0.09933467 0.19470917 0.28232124]] >>>>>> [[ 0.45464871 -0.13970775 0.09933467] >>>>>> [ 0.09933467 0.19470917 0.28232124]] >>>>>> [[ 0.45464871 -0.13970775 0.09933467] >>>>>> [ 0.09933467 0.19470917 0.28232124]] >>>>>> 14000972 function calls (14000966 primitive calls) in 38.625 >>>>>> seconds >>>>>> >>>>>> Ordered by: internal time, function name >>>>>> >>>>>> ncalls tottime percall cumtime percall filename:lineno(function) >>>>>> 2000000 18.373 0.000 19.086 0.000 edward.py:10(really_slow) >>>>>> 2000000 8.798 0.000 9.576 0.000 edward.py:5(in_place) >>>>>> 2000000 5.937 0.000 5.937 0.000 edward.py:15(return_bc) >>>>>> 1 1.839 1.839 7.785 7.785 >>>>>> edward.py:30(test_return_bc) >>>>>> 4000021 1.141 0.000 1.141 0.000 {range} >>>>>> 1 1.086 1.086 10.675 10.675 edward.py:18(test_in_place) >>>>>> 1 1.070 1.070 20.165 20.165 >>>>>> edward.py:24(test_really_slow) >>>>>> 4000198 0.379 0.000 0.379 0.000 {len} >>>>>> """ >>>>>> >>>>>> Hmmm, Python 2 is faster than Python 3 for this example! See for >>>>>> yourself. If you really think that making the code 1.25 times faster, >>>>>> as shown in these tests, is worth your time, then this must be done in >>>>>> a subversion branch (http://svn.gna.org/viewcvs/relax/branches/). >>>>>> That way we can have timing tests between the trunk and the branch. >>>>>> As this affects all dispersion models, the changes will be quite >>>>>> disruptive. And if the implementation is not faster or if it breaks >>>>>> everything, then the branch can be deleted. What ever you do, please >>>>>> don't use a git-svn branch. >>>>>> >>>>>> Regards, >>>>>> >>>>>> Edward >>>>>> >>>>>> >>>>>> >>>>>> >>>>>> On 9 May 2014 14:07, Troels Emtekær Linnet <[email protected]> wrote: >>>>>>> Hi Edward. >>>>>>> >>>>>>> How about this script? >>>>>>> Here I try to pass the back the r2eff values, and then set them in the >>>>>>> back_calculated class object. >>>>>>> Will this work ?? >>>>>>> >>>>>>> Or else I found this post about updating values. >>>>>>> http://stackoverflow.com/questions/14916284/in-python-class-object-how-to-auto-update-attributes >>>>>>> They talk about >>>>>>> @property >>>>>>> and setter, which I dont get yet. :-) >>>>>>> >>>>>>> Best >>>>>>> Troels >>>>>>> >>>>>>> >>>>>>> --------------- >>>>>>> >>>>>>> >>>>>>> def loop_rep(x, nr): >>>>>>> y = [98, 99] >>>>>>> for i in range(nr): >>>>>>> x[i] = y[i] >>>>>>> >>>>>>> def not_loop_rep(x, nr): >>>>>>> y = [98, 99] >>>>>>> x = y >>>>>>> >>>>>>> def not_loop_rep_new(x, nr): >>>>>>> y = [98, 99] >>>>>>> x = y >>>>>>> return x >>>>>>> >>>>>>> >>>>>>> class MyClass: >>>>>>> def __init__(self, x): >>>>>>> self.x = x >>>>>>> self.nr = len(x) >>>>>>> >>>>>>> def printc(self): >>>>>>> print self.x, self.nr >>>>>>> >>>>>>> def calc_loop_rep(self, x=None, nr=None): >>>>>>> loop_rep(x=self.x, nr=self.nr) >>>>>>> >>>>>>> def calc_not_loop_rep(self, x=None, nr=None): >>>>>>> not_loop_rep(x=self.x, nr=self.nr) >>>>>>> >>>>>>> def calc_not_loop_rep_new(self, x=None, nr=None): >>>>>>> self.x = not_loop_rep_new(x=self.x, nr=self.nr) >>>>>>> >>>>>>> print("For class where we loop replace ") >>>>>>> "Create object of class" >>>>>>> t_rep = MyClass([0, 1]) >>>>>>> "Print object of class" >>>>>>> t_rep.printc() >>>>>>> "Calc object of class" >>>>>>> t_rep.calc_loop_rep() >>>>>>> " Then print" >>>>>>> t_rep.printc() >>>>>>> >>>>>>> print("\nFor class where we not loop replace ") >>>>>>> " Now try with replace " >>>>>>> t = MyClass([3, 4]) >>>>>>> t.printc() >>>>>>> t.calc_not_loop_rep() >>>>>>> t.printc() >>>>>>> >>>>>>> print("\nFor class where we not loop replace ") >>>>>>> t_new = MyClass([5, 6]) >>>>>>> t_new.printc() >>>>>>> t_new.calc_not_loop_rep_new() >>>>>>> t_new.printc() >>>>>>> >>>>>>> 2014-05-05 19:07 GMT+02:00 Edward d'Auvergne <[email protected]>: >>>>>>>> :) It does slow it down a little, but that's unavoidable. It's also >>>>>>>> unavoidable in C, Fortran, Perl, etc. As long as the number of >>>>>>>> operations in that loop is minimal, then it's the best you can do. If >>>>>>>> it worries you, you could run a test where you call the target >>>>>>>> function say 1e6 times, with and without the loop to see the timing >>>>>>>> difference. Or simply running in Python 2: >>>>>>>> >>>>>>>> for i in xrange(1000000): >>>>>>>> x = 1 >>>>>>>> >>>>>>>> Then try: >>>>>>>> >>>>>>>> for i in xrange(100000000): >>>>>>>> x = 2 >>>>>>>> >>>>>>>> These two demonstrate the slowness of the Python loop. But the second >>>>>>>> case is extreme and you won't encounter that much looping in these >>>>>>>> functions. So while it is theoretically slower than C and Fortran >>>>>>>> looping, you can probably see that no one would care :) Here is >>>>>>>> another test, with Python 2 code: >>>>>>>> >>>>>>>> """ >>>>>>>> import cProfile as profile >>>>>>>> >>>>>>>> def loop_1e6(): >>>>>>>> for i in xrange(int(1e6)): >>>>>>>> x = 1 >>>>>>>> >>>>>>>> def loop_1e8(): >>>>>>>> for i in xrange(int(1e8)): >>>>>>>> x = 1 >>>>>>>> >>>>>>>> def sum_conv(): >>>>>>>> for i in xrange(100000000): >>>>>>>> x = 2 + 2. >>>>>>>> >>>>>>>> def sum_normal(): >>>>>>>> for i in xrange(100000000): >>>>>>>> x = 2. + 2. >>>>>>>> >>>>>>>> def test(): >>>>>>>> loop_1e6() >>>>>>>> loop_1e8() >>>>>>>> sum_normal() >>>>>>>> sum_conv() >>>>>>>> >>>>>>>> profile.runctx('test()', globals(), locals()) >>>>>>>> """ >>>>>>>> >>>>>>>> Running this on my system shows: >>>>>>>> >>>>>>>> """ >>>>>>>> 7 function calls in 6.707 seconds >>>>>>>> >>>>>>>> Ordered by: standard name >>>>>>>> >>>>>>>> ncalls tottime percall cumtime percall filename:lineno(function) >>>>>>>> 1 0.000 0.000 6.707 6.707 <string>:1(<module>) >>>>>>>> 1 2.228 2.228 2.228 2.228 aaa.py:11(sum_conv) >>>>>>>> 1 2.228 2.228 2.228 2.228 aaa.py:15(sum_normal) >>>>>>>> 1 0.000 0.000 6.707 6.707 aaa.py:19(test) >>>>>>>> 1 0.022 0.022 0.022 0.022 aaa.py:3(loop_1e6) >>>>>>>> 1 2.228 2.228 2.228 2.228 aaa.py:7(loop_1e8) >>>>>>>> 1 0.000 0.000 0.000 0.000 {method 'disable' of >>>>>>>> '_lsprof.Profiler' objects} >>>>>>>> """ >>>>>>>> >>>>>>>> That should be self explanatory. The better optimisation targets are >>>>>>>> the repeated maths operations and the maths operations that can be >>>>>>>> shifted into the target function or the target function >>>>>>>> initialisation. Despite the numbers above which prove my int to float >>>>>>>> speed argument as utter nonsense, it might still good to remove the >>>>>>>> int to float conversions, if only to match the other functions. >>>>>>>> >>>>>>>> Regards, >>>>>>>> >>>>>>>> Edward >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> >>>>>>>> On 5 May 2014 18:45, Troels Emtekær Linnet <[email protected]> >>>>>>>> wrote: >>>>>>>>> The reason why I ask, is that I am afraid that this for loop slows >>>>>>>>> everything down. >>>>>>>>> >>>>>>>>> What do you think? >>>>>>>>> >>>>>>>>> 2014-05-05 18:41 GMT+02:00 Edward d'Auvergne <[email protected]>: >>>>>>>>>> This is not Python specific though :) As far as I know, C uses >>>>>>>>>> pass-by-value for arguments, unless they are arrays or other funky >>>>>>>>>> objects/functions/etc.. This is the same behaviour as Python. >>>>>>>>>> Pass-by-reference and pass-by-value is something that needs to be >>>>>>>>>> mastered in all languages, whether or not you have pointers to play >>>>>>>>>> with. >>>>>>>>>> >>>>>>>>>> Regards, >>>>>>>>>> >>>>>>>>>> Edward >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> >>>>>>>>>> On 5 May 2014 18:30, Troels Emtekær Linnet <[email protected]> >>>>>>>>>> wrote: >>>>>>>>>>> This reminds me: >>>>>>>>>>> >>>>>>>>>>> http://combichem.blogspot.dk/2013/08/you-know-what-really-grinds-my-gears-in.html >>>>>>>>>>> >>>>>>>>>>> 2014-05-05 17:52 GMT+02:00 Edward d'Auvergne <[email protected]>: >>>>>>>>>>>> Hi, >>>>>>>>>>>> >>>>>>>>>>>> This is an important difference. In the first case (back_calc[i] = >>>>>>>>>>>> Minty[i]), what is happening is that your are copying the data >>>>>>>>>>>> into a >>>>>>>>>>>> pre-existing structure. In the second case (back_calc = Minty), >>>>>>>>>>>> the >>>>>>>>>>>> existing back_calc structure will be overwritten. Therefore the >>>>>>>>>>>> back_calc structure in the calling code will be modified in the >>>>>>>>>>>> first >>>>>>>>>>>> case but not the second. Here is some demo code: >>>>>>>>>>>> >>>>>>>>>>>> def mod1(x): >>>>>>>>>>>> x[0] = 1 >>>>>>>>>>>> >>>>>>>>>>>> def mod2(x): >>>>>>>>>>>> x = [3, 2] >>>>>>>>>>>> >>>>>>>>>>>> x = [0, 2] >>>>>>>>>>>> print(x) >>>>>>>>>>>> mod1(x) >>>>>>>>>>>> print(x) >>>>>>>>>>>> mod2(x) >>>>>>>>>>>> print(x) >>>>>>>>>>>> >>>>>>>>>>>> I don't know of a way around this. >>>>>>>>>>>> >>>>>>>>>>>> Regards, >>>>>>>>>>>> >>>>>>>>>>>> Edward >>>>>>>>>>>> >>>>>>>>>>>> >>>>>>>>>>>> On 5 May 2014 17:42, Troels Emtekær Linnet <[email protected]> >>>>>>>>>>>> wrote: >>>>>>>>>>>>> Hi Edward. >>>>>>>>>>>>> >>>>>>>>>>>>> In the library function of b14.py, i am looping over >>>>>>>>>>>>> the dispersion points to put in the data. >>>>>>>>>>>>> >>>>>>>>>>>>> for i in range(num_points): >>>>>>>>>>>>> >>>>>>>>>>>>> # The full formula. >>>>>>>>>>>>> back_calc[i] = Minty[i] >>>>>>>>>>>>> >>>>>>>>>>>>> Why can I not just set: >>>>>>>>>>>>> back_calc = Minty >>>>>>>>>>>>> >>>>>>>>>>>>> _______________________________________________ >>>>>>>>>>>>> relax (http://www.nmr-relax.com) >>>>>>>>>>>>> >>>>>>>>>>>>> This is the relax-devel mailing list >>>>>>>>>>>>> [email protected] >>>>>>>>>>>>> >>>>>>>>>>>>> To unsubscribe from this list, get a password >>>>>>>>>>>>> reminder, or change your subscription options, >>>>>>>>>>>>> visit the list information page at >>>>>>>>>>>>> https://mail.gna.org/listinfo/relax-devel _______________________________________________ relax (http://www.nmr-relax.com) This is the relax-devel mailing list [email protected] To unsubscribe from this list, get a password reminder, or change your subscription options, visit the list information page at https://mail.gna.org/listinfo/relax-devel

