/* $Id$ @author Nikolai van Kempen Provides the nonlinear algorithm to optimize a formula that represents a cost function that is aware of the memory dependent cost. See \$SECONDO\_HOME/Optimizer/MemoryAllocation/ for more information. */ #include #include #include "stdlib.h" #include "SWI-Prolog.h" #include #include #include #include #include using namespace std; struct OpMemoryConstraint { int dimension; double sufficientMemory; }; /* Forwards the predicate references to the objective function. */ struct FData { predicate_t pF; predicate_t pD; }; struct OptimizeResult { int rc; std::vector x; }; /* An exception is needed to pass-through an error during predicate evaluation. */ class EvalException: public exception { virtual const char* what() const throw() { return "Predicate evaluation error."; } } evalex; /* Generalized function to evaluate our objective and derivate predicates. */ double evalPredicate(predicate_t p, int i, unsigned n, const double *x) { int arity=i>=0?3:2; term_t tt = PL_new_term_refs(arity); term_t ct = tt; // The current term if (i>=0) { PL_unify_integer(ct, i); ct++; } PL_put_nil(ct); for(int i=0;ipF, -1, n, x); if (grad) { // Only needed if choosen algorithm demands it. for (int i=0; i < n; i++) { grad[i] = evalPredicate(fd->pD, i, n, x); } } return dresult; } /* The constraint function form within nlopt is always c(x)<=0 Max memory is limited by the maximum memory value within a shelf. A shelf decripton contains within the data vector: - The first element is the amount of memory which can be assigned to a shelf. - The next n elements contain either a 0 or a 1. - 0 means: This operator is not of any interest here. But the operator MUST exist with a 1 in another data vector. - 1 means: This operator is within this shelf and needs memory. */ double maxMemoryConstraint(unsigned n, const double *x, double *grad, void *dataptr) { std::vector data = *(std::vector*) dataptr; int i=0; if (grad) { for(i=0; i 0) grad[i] = 1; else grad[i] = 0; } } //count << "Shelf-Memory: " << data[0] << endl; int rc=-1 * data[0]; // Contains the max memory for this shelf for(i=0; i 0) rc=rc+x[i]; } return rc; } /* This could be done in the same way as with the objective and derivate functions, with prolog predicates, but these functions stay quite simple, even if the other two metioned functions are more complex. Limits the memory assignments for one operator to it's sufficientMemory value. */ double sufficientMemoryConstraint(unsigned n, const double *x, double *grad, void *data) { OpMemoryConstraint* d = (OpMemoryConstraint*) data; double suffMem = d->sufficientMemory; int dim=d->dimension; if (grad) { for(int i=0; i > vConstraints, int dimension, std::vector vSufficientMemoryInMiB) { /* Check out http://ab-initio.mit.edu/wiki/index.php/NLopt_Algorithms for information about the different implemented algorithm. You can switch to a local minimum alogorithm if we can assure that a local minimum is a global minimum. For the current used cost functions, this is the case. Depending on the choosen algorithm, the fuctions must be differentiable, and even more, the derivate must be provided. More precicely, for the current choosen LD_MMA algorithm, only continuously, differentiable and convex functions are currently allowed to be memory dependent cost functions. // Use COBYLA if you think there is something wrong with // the derivates. nlopt::opt opt(nlopt::LN_COBYLA, dimension); */ nlopt::opt opt(nlopt::LD_MMA, dimension); /* The lower and upper bounds are the search area. The minimum is the minimum amount of memory that should be assigned to an operator. */ int lowerBound = minOpMemory; std::vector lb(dimension); for(int i=0;i ub(dimension); for(int i=0;i opcdata(dimension); for(int i=0;i x(dimension); for(int i=0;i > vConstraints(constraints, vector(dimension+1)); l = PL_copy_term_ref(t_constraints); h = PL_new_term_ref(); int i=0; while(PL_get_list(l, h, l)) { int c=0; term_t hi = PL_new_term_ref(); term_t li = PL_copy_term_ref(h); while(PL_get_list(li, hi, li)) { int intValue; PL_get_integer(hi, &intValue); vConstraints[i][c] = intValue; c++; } if (c!=dimension+1) { cerr << "The parameter 'constraints' does not contain " << dimension+1 << " values. Abort." << endl; PL_fail; } i++; } if (i==0) { // otherwiese the amount of memory is unristricted. cerr << "Error: No constraint list provided. "; cerr << "At least one constraint list is needed. " << endl; PL_fail; } if (!PL_is_list(sufficientMemory)) { cerr << "sufficientMemory parameter is not a list. Abort." << endl; PL_fail; } std::vector vSufficientMemoryInMiB(dimension); l = PL_copy_term_ref(sufficientMemory); h = PL_new_term_ref(); i=0; while(PL_get_list(l, h, l)) { int intValue; PL_get_integer(h, &intValue); vSufficientMemoryInMiB[i] = intValue; i++; } if (i!=dimension) { cerr << "sufficientMemory parameter don't contain " << dimension << " values. Abort." << endl; PL_fail; } OptimizeResult oResult; try { oResult=minimize(minOpMemoryMiB, memoryMiB, vConstraints, dimension, vSufficientMemoryInMiB); } catch(std::exception &e) { cerr << "Optimization failed due to the exception: " << e.what() << endl; PL_fail; } if (oResult.rc!=0) { cerr << "Optimization failed." << endl; PL_fail; } term_t result = PL_new_term_ref(); PL_put_nil(result); for(int i=0;i The list is built tail-to-head! PL_put_float(elem, oResult.x[dimension-(i+1)]); PL_cons_list(result, elem, result); } if (!PL_unify(memoryAssignments, result)) { cerr << "Result unification failed." << endl; PL_fail; } PL_succeed; } // eof