2
$\begingroup$

I have a multivariable function, from which I'd like to define a marginalization:

f(a,b,c,d,...,z) ---> g(a,1,c,d,...,z)

Generally, g will have fewer argument than f, possibly none (full evaluation). Is it possible to take the output of a function and transform it into a second function?

This is part of a looped Block[] segment in a larger code, where the number of variables, slot positions, and their values are specified anew on each iteration.

Here is my best failed attempt:

PartialEvaluate[func_, totargs_, args_, targets_] :=

Block[{tmp, g, r},

tmp = Table[Slot[i], {i, 1, totargs}];

Do[tmp[[targets[[slot]]]] = args[[slot]];, {slot, 1, Length[targets]}];

g = Function[func @@ tmp]

Return[g];
];

Thanks

$\endgroup$
  • 2
    $\begingroup$ g[a_,c_]:= f[a, 1, c]? $\endgroup$ – Dr. belisarius Jun 16 '15 at 0:58
  • $\begingroup$ How are the variables specified? $\endgroup$ – C. E. Jun 16 '15 at 1:02
  • $\begingroup$ I've updated the post with an example. $\endgroup$ – sampson Jun 16 '15 at 1:09
  • 1
    $\begingroup$ You mean partial application? $\endgroup$ – ciao Jun 16 '15 at 1:24
  • $\begingroup$ Somewhat related: mathematica.stackexchange.com/q/48137/7936 $\endgroup$ – evanb Jan 14 '17 at 9:11
2
$\begingroup$

Updated version

To be more in line with what the OP wanted, we have the following updated code. Given inputs (in order) g, totargs = 5, args = {x[1],x[4]}, and targets = {1,4}, if we call the function

partialEvaluate[func_, totargs_, args_, targets_] := Block[{argsEvaluated = 1, newArgs = 1}
 , Evaluate[func @@ Table[
    If[MemberQ[targets, i], args[[argsEvaluated++]], Slot[newArgs++]]
    , {i, 1, totargs}]
   ] &
]

the result is

g[x[1], #1, #2, x[4], #3] &

The main reason the OP's original code didn't work is that Function has the Attribute HoldAll:

Attributes[Function]
(* {HoldAll, Protected} *)

If you want the insides of Function to be evaluated, you have to explicitly Evaluate it, as I've done.

Original post

I'm not sure of exactly what the inputs and outputs should be, but here's my best guess as to what you want, with minimal changes to your code. For the purpose of concreteness, let's suppose that totargs = 5, args = {x[1],x[2],x[3],x[4],x[5]}, and the target variables to which the function is going to be applied to are targets = {1,4}. Then if we call the function

partialEvaluate[func_, totargs_, args_, targets_] := Block[{tmp}
  , tmp = Table[Slot[i], {i, 1, totargs}]
  ; tmp[[targets]] = args[[targets]]
  ; Evaluate[func @@ tmp] &
]

with the input

partialEvaluate[g, 5, {x[1], x[2], x[3], x[4], x[5]}, {1, 4}]

we get the pure function

g[x[1], #2, #3, x[4], #5] &
$\endgroup$
  • $\begingroup$ This is what I was looking for, thank you! $\endgroup$ – sampson Jun 16 '15 at 4:25
  • $\begingroup$ Thank you for the accept! However, are you sure? Is it possible that what you actually want is g[x[1], #1, #2, x[4], #3] &? Because I imagine you will want to apply this new function to a three-element list, rather than still a five element list. If so, I can update with a new version that returns this alternative version $\endgroup$ – march Jun 16 '15 at 5:09
  • $\begingroup$ Ah, I forgot to share. I added a simple j=1;Do[If[tmp[[i]]==Slot[i],tmp[[i]]=Slot[j++],{i,Range[totargs]}] fix. before the evaluation step. Share your solution anyway, it's probably more clever! $\endgroup$ – sampson Jun 16 '15 at 5:43
  • $\begingroup$ That's essentially what I did, actually, but I'll post anyway, since it's slightly cleaner. $\endgroup$ – march Jun 16 '15 at 5:47
  • $\begingroup$ A pedantic difference, but my ugly do loop was to account for a Length[args]=Length[targets] constraint which I didn't mention earlier. Thanks a lot for your help. $\endgroup$ – sampson Jun 16 '15 at 5:51
1
$\begingroup$

Since mathematica does not use currying evaluation like language like haskell, you should do the currying for partial evaluation by yourself.

Here is some simple example:

In[1]:= addx[x_] := Function[y, x + y]
In[2]:= addx[3][4]
Out[2]:= 7
In[3]:= add3 = addx[3]
Out[3]:= Function[y$, 3 + y$]
In[4]:= add3[4]
Out[4]:= 7

In general, you should make the function you want to partial evaluate take arguments and return another function which take the remaining arguments. And then you can pass few arguments to the function and you will get another function which can be applied to the remaining arguments.

$\endgroup$
  • $\begingroup$ Thanks for your suggestion, didn't know about 'currying'. Unfortunately the structure of func is fixed by other elements of the larger code. I was hoping to find a solution to the problem as I posted it above. it is sufficiently general and presumably will be simple to solve. but I'm not sure why the application of the Function fails to recruit the slots in the output of Apply [func]. $\endgroup$ – sampson Jun 16 '15 at 3:16
1
$\begingroup$

March's version is problematic: in case the original function uses itself pure functions in its definition then this can lead to unintended results and errors. Consider the following example:

foo[x_,y_] := (x #)& /@ {x,y}

Partial evaluation of foo with the second argument set to three should result in a function that is equivalent to:

fooPartial[x_] := (x #)& /@ {x,3}

If we now use march's method for partial evaluation we get the following pure function

{#1^2, 9}&

which is equivalent to

fooPartialWrong[x_] := (# #)& /@ {x,3}

which is not equivalent to fooPartial! The problem is that the #'s in the argument of foo and in the definition get mixed up.

A further problem (probably even more problematic in this case) is that Evaluate "breaks" scoping

In[1]:= x = 3                                                                   

Out[1]= 3

In[2]:= foo2[x_,y_]:= Evaluate[x y]                                              

In[3]:= ?foo2                                                                    
Global`foo2

foo2[x_, y_] := 3*y

A better method is to exploit the fact that List does not have the attribute HoldAll (see Enforcing correct variable bindings and avoiding renamings for conflicting variables in nested scoping constructs):

Module[{x},SetDelayed @@ {fooPartial[x_],foo[x,3]}]

In rewriting partialEvaluate one cannot use Module as above since the number of variables scoped by Module is fixed, so I use Unique instead to avoid side-effects.

partialEvaluate2[func_, funcPartial_, totargs_, args_, targets_] :=
 Module[{num1 = 1,num2 = 1,variablesNames,variablesNamesUnderscore},
  variablesNames = Unique /@ ConstantArray["x",totargs - Length[args]];
  variablesNamesUnderscore = Pattern[#,Blank[]]& /@ variablesNames;
  SetDelayed @@ {funcPartial@@variablesNamesUnderscore, func @@ Table[
    If[MemberQ[targets, i], args[[num1++]], variablesNames[[num2++]]],
    {i, 1, totargs}]}
 ]

And we now get as desired:

In[1]:= partialEvaluate2[foo,fooPartial,2,{3},{2}] 
In[2]:= ?fooPartial                                                                                                                           
Global`fooPartial                                                                                                                              

fooPartial[x31_] := {x31^2, 3*x31}
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.