Looks good to me!

Cheers,

Colin

On 24 June 2015 at 23:04, Andrew <[email protected]> wrote:

> Yup, I like that idea too. Multiple dispatch is quite useful here. This is
> my implementation.
>
> abstract UtilityFunction
>
> immutable CRRA <: UtilityFunction
>     sigmac::Float64
>     sigmal::Float64
>     psi::Float64
> end
>
> immutable LogUtility <: UtilityFunction
>
> end
>
> function u(UF::CRRA,consump,labor)
>     sigmac = UF.sigmac
>     sigmal = UF.sigmal
>     psi = UF.psi
>     (consump > 0 && labor <1) && return consump.^(1-sigmac)/(1-sigmac) +
> psi*(1-labor).^(1-sigmal)/(1-sigmal)
>     return -Inf
> end
> function u(UF::LogUtility,consump,labor)
>     consump > 0 && return log(consump)
>     return -Inf
> end
> function test1(UF::UtilityFunction)
>     for i = 1:1000000
>         u(UF,-1. + 1/i, .5)
>     end
> end
> function test2(UF::UtilityFunction)
>     for i = 1:1000000
>         u(UF,-1. + 1/i ,.5)
>     end
> end
>
> UF1 = CRRA(4,2,1)
> UF2 = LogUtility()
> @time test1(UF1)
> @time test2(UF2)
>
> elapsed time: 0.005229738 seconds (80 bytes allocated)
> elapsed time: 0.004894504 seconds (80 bytes allocated)
>
>
>
>
> On Tuesday, June 23, 2015 at 8:28:28 PM UTC-4, Colin Bowers wrote:
>
>> Yes, that is pretty much how I would do it, although, as I said in my
>> previous post, I would set `UtilityFunction` to an abstract type, and then
>> define my actual utility function immutable, say `MyCustomUtilityFunc`, as
>> a subtype of `UtilityFunction`. That way you can easily add different types
>> of utility functions later without having to change your existing code. By
>> the way, just for the record, a fair test between the two approaches would
>> be as follows:
>>
>> abstract UtilityFunction
>> immutable MyCustomUtilityFunction <: UtilityFunction
>>     sigmac::Float64
>>     sigmal::Float64
>>     psi::Float64
>> end
>> u4(sigmac, sigmal, psi, consump,labor) = consump.^(1-sigmac)/(1-sigmac) +
>> psi*(1-labor).^(1-sigmal)/(1-sigmal)
>> u(UF::MyCustomUtilityFunctionconsump,labor) =
>> consump.^(1-UF.sigmac)/(1-UF.sigmac) +
>> UF.psi*(1-labor).^(1-UF.sigmal)/(1-UF.sigmal)
>>
>>
>> function test1(sigmac, sigmal, psi)
>>     for i = 1:1000000
>>         u4(sigmac, sigmal, psi, 1.0 + 1/i, 0.5)
>>     end
>> end
>> function test2(UF::UtilityFunction)
>>     for i = 1:1000000
>>         u(UF, 1.0 + 1/i , 0.5)
>>     end
>> end
>>
>> UF = MyCustomUtilityFunction(4,2,1)
>> test1(4.0, 2.0, 1.0)
>> test2(UF)
>>
>>
>> On my machine that returns:
>>
>> elapsed time: 0.090409383 seconds (80 bytes allocated)
>> elapsed time: 0.091065473 seconds (80 bytes allocated)
>>
>> ie, no significant performance difference
>>
>> On 24 June 2015 at 00:56, Andrew <[email protected]> wrote:
>>
>>> Thanks, this is all very useful. I think I am going to back away from
>>> using the @anon functions at the moment, so I'll postpone my idea to
>>> encapsulate the functions into a type. Instead, I will just pass a
>>> parameter type to an externally defined(not nested) function. I had thought
>>> this would be slow (see my question here
>>> https://groups.google.com/forum/#!topic/julia-users/6U-otLSx7B0 ), but
>>> I did a little testing.
>>>
>>> immutable UtilityFunction
>>>     sigmac::Float64
>>>     sigmal::Float64
>>>     psi::Float64
>>> end
>>> function u(UF::UtilityFunction,consump,labor)
>>>     sigmac = UF.sigmac
>>>     sigmal = UF.sigmal
>>>     psi = UF.psi
>>>     consump.^(1-sigmac)/(1-sigmac) + psi*(1-labor).^(1-sigmal)/(1-sigmal
>>> )
>>> end
>>> function u4(consump,labor)
>>>     consump.^(1-4)/(1-4) + 1*(1-labor).^(1-2)/(1-2)
>>> end
>>>
>>> function test1(UF)
>>>     for i = 1:1000000
>>>         u4(1. + 1/i, .5)
>>>     end
>>> end
>>> function test2(UF)
>>>     for i = 1:1000000
>>>         u(UF,1. + 1/i ,.5)
>>>     end
>>> end
>>> UF = UtilityFunction(4,2,1)
>>>
>>> @time test1(UF)
>>> @time test2(UF)
>>>
>>> elapsed time: 0.068562617 seconds (80 bytes allocated)
>>> elapsed time: 0.139422608 seconds (80 bytes allocated)
>>>
>>>
>>>  So, even versus the extreme case where I built the constants into the
>>> function, the slowdown is not huge. I asume @anon would have similar
>>> performance to the constants built in case, which is nice. However, I want
>>> to be able to share my Julia code with others who aren't very experienced
>>> with the language, so I'd be uncomfortable asking them to understand the
>>> workings of FastAnonymous. It's useful to know about in case I need the
>>> speedup in my own personal code though.
>>>
>>>
>>> On Tuesday, June 23, 2015 at 8:51:25 AM UTC-4, [email protected]
>>> wrote:
>>>>
>>>> Yes, this proves to be an issue for me sometimes too. I asked a
>>>> StackOverflow question on this topic a few months ago and got a very
>>>> interesting response, as well as some interesting links. See here:
>>>>
>>>>
>>>> http://stackoverflow.com/questions/28356437/julia-compiler-does-not-appear-to-optimize-when-a-function-is-passed-a-function
>>>>
>>>> As a general rule, if the function you are passing round is very simple
>>>> and gets called a lot, then you will really notice the performance
>>>> overhead. In other cases where the function is more complicated, or is not
>>>> called that often, the overhead will be barely measurable.
>>>>
>>>> If the number of functions that you want to pass around is not that
>>>> large, one way around this is to use types and multiple dispatch instead of
>>>> functions, eg
>>>>
>>>> abstract UtilityFunctions
>>>> type QuadraticUtility <: UtilityFunctions
>>>>     a::Float64
>>>>     b::Float64
>>>>     c::Float64
>>>> end
>>>> evaluate(x::Number, f::QuadraticUtility) = f.a*x^2 + f.b*x + f.c
>>>>
>>>> Now your function would be something like:
>>>>
>>>> function solveModel(f::UtilityFunctions, ...)
>>>>
>>>> and you would call evaluate at the appropriate place in the function
>>>> body and multiple dispatch will take care of the rest. There is no
>>>> performance overhead with this approach.
>>>>
>>>> Of course, if you want to be able to just pass in any arbitrary
>>>> function that a user might think up, then this approach is not tenable.
>>>>
>>>> On Tuesday, 23 June 2015 01:07:25 UTC+10, Andrew wrote:
>>>>>
>>>>>
>>>>>
>>>>> I'm trying to write some abstract Julia code to solve a variety of
>>>>> economics models. Julia provides powerful abstraction tools which I think
>>>>> makes it very well-suited to this; however, I've read in several places
>>>>> that Julia doesn't yet know how to inline functions passed as arguments,
>>>>> hence code like
>>>>>
>>>>> function SolveModel(Utility::Function, ProductionTechnology::Function
>>>>> ,...)
>>>>> ...
>>>>>
>>>>> will be slow. I performed this very simple test.
>>>>>
>>>>> function ftest1()
>>>>>     u(x) = log(x)
>>>>>     function hello(fun::Function)
>>>>>         for i = 1:1000000
>>>>>             fun(i.^(1/2))
>>>>>         end
>>>>>     end
>>>>> end
>>>>>
>>>>> function ftest2()
>>>>>     function hello()
>>>>>         for i = 1:1000000
>>>>>             log(i.^(1/2))
>>>>>         end
>>>>>     end
>>>>> end
>>>>>
>>>>> @time ftest1()
>>>>> @time ftest2()
>>>>>
>>>>> elapsed time: 6.065e-6 seconds (496 bytes allocated)
>>>>> elapsed time: 3.784e-6 seconds (264 bytes allocated)
>>>>>
>>>>>
>>>>>  The inlined version is about twice as fast, which isn't all that bad,
>>>>> although I'm not sure if it would be worse in a more complicated example.
>>>>> Perhaps I shouldn't worry about this, and should code how I want. I was
>>>>> wondering though, if anybody knows when this is going to change. I've read
>>>>> about functors, which I don't really understand, but it sounds like people
>>>>> are working on this problem.
>>>>>
>>>>
>>

Reply via email to