3
$\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$
5
  • 2
    $\begingroup$ g[a_,c_]:= f[a, 1, c]? $\endgroup$ Commented Jun 16, 2015 at 0:58
  • $\begingroup$ How are the variables specified? $\endgroup$
    – C. E.
    Commented Jun 16, 2015 at 1:02
  • $\begingroup$ I've updated the post with an example. $\endgroup$
    – user30193
    Commented Jun 16, 2015 at 1:09
  • 1
    $\begingroup$ You mean partial application? $\endgroup$
    – ciao
    Commented Jun 16, 2015 at 1:24
  • $\begingroup$ Somewhat related: mathematica.stackexchange.com/q/48137/7936 $\endgroup$
    – evanb
    Commented Jan 14, 2017 at 9:11

3 Answers 3

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$
6
  • $\begingroup$ This is what I was looking for, thank you! $\endgroup$
    – user30193
    Commented Jun 16, 2015 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
    Commented Jun 16, 2015 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$
    – user30193
    Commented Jun 16, 2015 at 5:43
  • $\begingroup$ That's essentially what I did, actually, but I'll post anyway, since it's slightly cleaner. $\endgroup$
    – march
    Commented Jun 16, 2015 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$
    – user30193
    Commented Jun 16, 2015 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$
1
  • $\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$
    – user30193
    Commented Jun 16, 2015 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 and acknowledge you have read our privacy policy.