I've been very confused about your question, but I think I've finally figured it out. Here's what I think you're trying to achieve:
- You want to define a predicate that depends on some arguments that in turn you want to depend on some functions.
- Those other functions can be arbitrary, and they don't all have the same signature.
- While they don't have the same signature, they do all depend on the same overall set of arguments, so you somehow need to pass in all of these lower level arguments and feed them to your functions in some pre-specified manner
If all of that's correct, then here's a possible answer. I'll start by defining a function that does all the work of putting things together correctly. It'll be a bit confusing, but I'll demonstrate how to use it.
SpecialFunction[combiner_, lower_, upper_, count_][fns__][ranges : {_, _} ...] :=
With[
{randomVals = Table[RandomReal /@ {ranges}, count],
applicators = MapApply /@ {fns},
checker = Function[Null, lower <= combiner[##] <= upper]},
With[
{appliedVals = Through[applicators[randomVals]]},
Transpose[{randomVals, Transpose@appliedVals, checker @@@ Transpose[appliedVals]}]]]
The first set of arguments correspond to how you want to do your checking (basically your hF1 and the less-equal tests). The second set of arguments are the functions you want to apply to the arguments (your fA1, fB2, and fB3). We'll need to put them in a special form, but we'll get to that later. The last set of arguments are the ranges in which you want to pick random values for each argument that will be fed to the functions. The way this is set up, you can pass any number of fns
and any number of ranges
.
Let's try it out with some dummy arguments so you can see what it does:
SpecialFunction[someCombiner, 0, 100, 2][func1, func2, func3][{1, 5}, {10, 20}]
{{{4.62279, 16.5337}, {func1[4.62279, 16.5337],
func2[4.62279, 16.5337], func3[4.62279, 16.5337]},
0 <= someCombiner[func1[4.62279, 16.5337], func2[4.62279, 16.5337],
func3[4.62279, 16.5337]] <= 100}, {{1.44301,
17.4801}, {func1[1.44301, 17.4801], func2[1.44301, 17.4801],
func3[1.44301, 17.4801]},
0 <= someCombiner[func1[1.44301, 17.4801], func2[1.44301, 17.4801],
func3[1.44301, 17.4801]] <= 100}}
Let's focus on just the first item in this output (note that the random numbers will be different when you try this yourself).
{{4.62279, 16.5337}, {func1[4.62279, 16.5337], func2[4.62279, 16.5337], func3[4.62279, 16.5337]}, 0 <= someCombiner[func1[4.62279, 16.5337], func2[4.62279, 16.5337], func3[4.62279, 16.5337]] <= 100}
We have our random values, the values we get when we apply our intermediate functions, and then the result of applying our combiner and the limits. We get that kind of output for as many trials as we do (which is determined by count
).
Now, the challenge we have is that you've got several low-level arguments that get fed to the intermediate functions in different ways, in terms of count, order, and even more intermediate computations (like adding the first and second or doubling the first). So, we need to define our intermediate functions by wrapping our actual functions. You can do this with OperatorApplied or CurryApplied, but rather than introduce yet another complication, let's just use plain Function.
wrappedFA1 = Function[Null, fA1[#1 + #2, #3, #1, #2]];
wrappedFB2 = Function[Null, fB2[#1 + #2, #3, #1, #2]];
wrappedFC3 = Function[Null, fC3[2*#1]];
Now we can pass these to SpecialFunction
. I'll also change it to use the parameters you were using.
SpecialFunction[hF1, 1, 10000000, 2][wrappedFA1, wrappedFB2, wrappedFC3][{1, 2}, {1, 2}, {1, 2}, {1, 2}]
{{{1.13805, 1.77789, 1.77946,
1.09484}, {fA1[2.91594, 1.77946, 1.13805, 1.77789],
fB2[2.91594, 1.77946, 1.13805, 1.77789], fC3[2.2761]},
1 <= hF1[fA1[2.91594, 1.77946, 1.13805, 1.77789],
fB2[2.91594, 1.77946, 1.13805, 1.77789], fC3[2.2761]] <=
10000000}, {{1.2858, 1.52344, 1.41616,
1.93163}, {fA1[2.80924, 1.41616, 1.2858, 1.52344],
fB2[2.80924, 1.41616, 1.2858, 1.52344], fC3[2.5716]},
1 <= hF1[fA1[2.80924, 1.41616, 1.2858, 1.52344],
fB2[2.80924, 1.41616, 1.2858, 1.52344], fC3[2.5716]] <= 10000000}}
Now we're ready to use the definitions you provided for the fA1 and so forth.
hF1[fA1_, fB2_, fC3_] := (2*fA1)*(3*fB2)*(4*fC3);
fA1[w1_, w2_, w3_, w4_] := 10 + w1 + w2 + w3 + w4;
fB2[r1_, r2_, r3_, r4_] := 1 + r1 + r2 + r3 + r4;
fC3[t1_] := 5*t1;
Finally, let's evaluate our SpecialFunction again:
SpecialFunction[hF1, 1, 10000000, 2][wrappedFA1, wrappedFB2, wrappedFC3][{1, 2}, {1, 2}, {1, 2}, {1, 2}]
{{{1.23086, 1.13019, 1.20986, 1.62693}, {15.932, 6.93197, 12.3086}, True},
{{1.96329, 1.14836, 1.4373, 1.46791}, {17.6606, 8.66061, 19.6329}, True}}
At this point, you can select the ones with True
.
Comments/suggestions:
- Looking at it now, I realize that it might have been useful to add the value of the
combiner
to the output. I don't want to worry about copy-paste errors at this point, so I'll let you decide how you want your results to be displayed.
- This currently assumes that hF1 takes its arguments in the order of the
fns
. If you want to play with that, then you'd want to wrap hF1 like we did for fA1 et al.
- Obviously, you need to provide as many argument range specs as you'll need for the
fns
. And you'll need to provide as many fns
as you'll need for the combiner
.
- You could work the final selection into
SpecialFunction
, but I thought it was easier to demonstrate without that step, so that's an exercise I leave to you.
- This could all be simplified if there were known constraints on your functions. Given that you wanted a general solution that works for any arbitrary set of functions, you'll need something at about this level of complexity.