I have a big problem that I couldn't solve and I really do not find the error.
I want to use this function :
The sample Test code from Apache is that :
public void testSinMin() {
UnivariateFunction f = new Sin();
UnivariateOptimizer optimizer = new BrentOptimizer(1e-10, 1e-14);
Assert.assertEquals(3 * Math.PI / 2, optimizer.optimize(new MaxEval(200),
new UnivariateObjectiveFunction(f),
GoalType.MINIMIZE,
new SearchInterval(4, 5)).getPoint(), 1e-8);
So I tried to reproduce the code with Clojure and another function :
(defn atomic-apache-peak-value [x]
(let [lamb ((x :parameters) 0)
x0 ((x :parameters) 1)
f (D2Sigmoid. lamb x0)
optimizer (BrentOptimizer. 0.0001 0.0001)
maxeval (MaxEval. 1000)
of (UnivariateObjectiveFunction. f)
goal (GoalType/MINIMIZE)
interval (SearchInterval. x0 (* 2 x0))]
(-> (.optimize optimizer maxeval of goal interval)
(.getPoint))))
Clojure tells me "No matching method found : optimize for class ....BrentOptimizer"
I tried to compute the lines of code one by one in the let and it works, so the problem is optimize.
The method is implemented on superclassses sor I imported them
[org.apache.commons.math3.optim.univariate UnivariateOptimizer BrentOptimizer UnivariateObjectiveFunction SearchInterval]
[org.apache.commons.math3.optim BaseOptimizer MaxEval]
It does not change anything.
Do you think I have a syntax problem or a bug or just a wrong way to do ?
Thanks
EDIT :
Forgot to mention that the
(.optimize optimizer)
version throws an exception from Apache but is found. So I do not think Clojure cannot find source code. Maybe there a problem of syntax ?
Also tried Goaltype/MINIMIZE without parenthesis
EDIT 2 :
Final working code
(defn atomic-apache-peak-value [x]
(let [lamb ((x :parameters) 0)
x0 ((x :parameters) 1)
f (D2Sigmoid. lamb x0)
optimizer (BrentOptimizer. 0.0001 0.0001)
maxeval (MaxEval. 1000)
of (UnivariateObjectiveFunction. f)
goal GoalType/MINIMIZE
interval (SearchInterval. x0 (* 2 x0))]
(-> (.optimize optimizer (into-array OptimizationData [maxeval of goal interval]))
(.getPoint))))
OK, let's create a proper answer. When calling Java methods from Clojure, it is important to look up the actual signature for the method. Just copying from Java sample code may not always work. That is mostly due vargars. Java interop in Clojure requires the programmer to do a bit of additional work when varargs are involved.
The signature of the optimize
method you are trying to call is:
public UnivariatePointValuePair optimize(OptimizationData... optData)
throws TooManyEvaluationsException
Notice the ...
, which means that the method takes 0 or more arguments of type OptimizationData
. In Java, this method can be invoked as optimize(foo, bar, baz)
as long as the classes of foo, bar, and baz implement the OptimizationData
interface.
However, such handling of the vararg is mostly due to Java compiler. Under the covers, the method actually expects a single argument of type OptimizationData []
- array of OptimizationData
. The Java compiler generates code that packs the arguments into an array, the programmer does not have to worry about it.
But when calling such methods from Clojure, the programmer has to create the array. It is as if the signature of that method appears to the Clojure compiler as optimize(OptimizationData[] optData)
.
It does not take a lot to create an array in Clojure. One way to do it is to use the into-array
function. The following shows the necessary bits and pieces:
(import '(org.apache.commons.math3.optim OptimizationData))
(.optimize (into-array OptimizationData [optimizer maxeval of goal interval]))
Also, there is not need to the parenthesis around GoalType/MINIMIZE
. The parenthesis mean a list, and lists are evaluated in Clojure as function invocation. Here we do not need to invoke GoalType/MINIMIZE
function, we just need that value.