source: branches/sandbox/Cbc/src/ClpAmplStuff.cpp @ 1373

Last change on this file since 1373 was 1373, checked in by bjarni, 10 years ago

Renamed parameter constants in CbcParam?, CbcSolver?, and ClpAmplStuff? with same names as CbcOrClpParam? in CLP to make them more readable/search-able

File size: 44.1 KB
Line 
1// Copyright (C) 2007, International Business Machines
2// Corporation and others.  All Rights Reserved.
3/* $Id: ClpAmplStuff.cpp 1200 2009-07-25 08:44:13Z forrest $ */
4
5/*! \file ClpAmplStuff.cpp
6    \brief Hooks to Ampl (for the new-style solver?)
7*/
8
9#include "ClpConfig.h"
10#include "CbcConfig.h"
11#ifdef COIN_HAS_ASL
12#include "CoinPragma.hpp"
13#include "CoinHelperFunctions.hpp"
14#include "CoinIndexedVector.hpp"
15#include "ClpFactorization.hpp"
16#include "ClpSimplex.hpp"
17#include "ClpAmplObjective.hpp"
18#include "ClpConstraintAmpl.hpp"
19#include "ClpMessage.hpp"
20#include "CoinUtilsConfig.h"
21#include "CoinHelperFunctions.hpp"
22#include "CoinWarmStartBasis.hpp"
23#include "OsiSolverInterface.hpp"
24#include "CbcSolver.hpp"
25#include "Cbc_ampl.h"
26#include "CoinTime.hpp"
27#include "CglStored.hpp"
28#include "CoinModel.hpp"
29#include "CbcLinked.hpp"
30
31/*! \brief Extension of CbcUser for Ampl.
32
33  Beyond that, can't say yet what this does. A CbcAmpl object can be installed
34  in a CbcSolver object using CbcSolver::addUserFunction.
35*/
36
37class CbcAmpl  : public CbcUser {
38
39public:
40    ///@name usage methods
41    //@{
42
43    /// Solve (whatever that means)
44    virtual void solve(CbcSolver * model, const char * options);
45
46    /*! \brief Returns true if function knows about option
47
48      Currently knows about
49        - cbc_load
50        - cbc_quit
51    */
52    virtual bool canDo(const char * option) ;
53
54    /*! \brief Import - gets full command arguments
55
56      \return
57      - -1 - no action
58      -  0 - data read in without error
59      -  1 - errors
60    */
61    virtual int importData(CbcSolver * model, int & argc, char ** & argv);
62
63    /*! \brief Export
64
65      \param mode
66      - 1 OsiClpSolver
67      - 2 CbcModel
68      - add 10 if infeasible from odd situation
69    */
70    virtual void exportSolution(CbcSolver * model, int mode, const char * message = NULL) ;
71    /// Export Data (i.e. at very end)
72    virtual void exportData(CbcSolver * model);
73    /// Get useful stuff
74    virtual void fillInformation(CbcSolver * model,
75                                 CbcSolverUsefulData & info);
76    //@}
77    ///@name Constructors and destructors etc
78    //@{
79    /// Default Constructor
80    CbcAmpl();
81
82    /** Copy constructor .
83     */
84    CbcAmpl(const CbcAmpl & rhs);
85
86    /// Assignment operator
87    CbcAmpl & operator=(const CbcAmpl& rhs);
88
89    /// Clone
90    virtual CbcUser * clone() const;
91
92    /// Destructor
93    virtual ~CbcAmpl ();
94    //@}
95private:
96    ///@name Private member data
97    //@{
98    /// AMPL info
99    ampl_info info_;
100    //@}
101};
102// Mechanicsburg stuff
103CbcAmpl::CbcAmpl()
104        : CbcUser()
105{
106    userName_ = "mech";
107    memset(&info_, 0, sizeof(info_));
108}
109CbcAmpl::~CbcAmpl()
110{
111}
112// Copy constructor
113CbcAmpl::CbcAmpl ( const CbcAmpl & rhs)
114        : CbcUser(rhs)
115{
116    info_ = rhs.info_;
117}
118// Assignment operator
119CbcAmpl &
120CbcAmpl::operator=(const CbcAmpl & rhs)
121{
122    if (this != &rhs) {
123        CbcUser::operator=(rhs);
124        info_ = rhs.info_;
125    }
126    return *this;
127}
128// Clone
129CbcUser *
130CbcAmpl::clone() const
131{
132    return new CbcAmpl(*this);
133}
134// Solve (whatever that means)
135void
136CbcAmpl::solve(CbcSolver * controlModel, const char * options)
137{
138    assert (controlModel->model());
139    //OsiClpSolverInterface * clpSolver = dynamic_cast< OsiClpSolverInterface*> (model->solver());
140    //ClpSimplex * lpSolver = clpSolver->getModelPtr();
141    if (!strcmp(options, "cbc_load")) {
142    } else if (!strcmp(options, "cbc_quit")) {
143    } else {
144        printf("unknown option for CbcAmpl is %s\n", options);
145        abort();
146    }
147}
148// Returns true if function knows about option
149bool
150CbcAmpl::canDo(const char * option)
151{
152    return (!strcmp(option, "cbc_load") || !strcmp(option, "cbc_quit"));
153}
154/* Import - gets full command arguments
155   Returns -1 - no action
156            0 - data read in without error
157            1 - errors
158*/
159int
160CbcAmpl::importData(CbcSolver * control, int &argc, char ** & argv)
161{
162    CbcModel * babModel = control->model();
163    assert (babModel);
164    CoinMessageHandler * generalMessageHandler = babModel->messageHandler();
165    OsiClpSolverInterface * solver =
166        dynamic_cast< OsiClpSolverInterface*> (control->model()->solver());
167    assert (solver);
168    CoinMessages generalMessages = solver->getModelPtr()->messages();
169    char generalPrint[10000];
170    OsiSolverLink * si = NULL;
171    /*
172      Poke through the arguments looking for a log level. If we find it, write it
173      into the info block we'll use to load from AMPL, and set a magic number to
174      indicate the log level is valid.
175
176      This looks brittle, in several different directions.
177    */
178    ClpSimplex * lpSolver = solver->getModelPtr();
179    if (argc > 2 && !strcmp(argv[2], "-AMPL")) {
180        // see if log in list
181        bool printing = false;
182        for (int i = 1; i < argc; i++) {
183            if (!strncmp(argv[i], "log", 3)) {
184                const char * equals = strchr(argv[i], '=');
185                if (equals && atoi(equals + 1) > 0) {
186                    printing = true;
187                    info_.logLevel = atoi(equals + 1);
188                    control->setIntValue(CLP_PARAM_INT_LOGLEVEL, info_.logLevel);
189                    // mark so won't be overWritten
190                    info_.numberRows = -1234567;
191                    break;
192                }
193            }
194        }
195        union {
196            void * voidModel;
197            CoinModel * model;
198        } coinModelStart;
199        coinModelStart.model = NULL;
200        int returnCode = readAmpl(&info_, argc, argv, & coinModelStart.voidModel);
201        CoinModel * coinModel = coinModelStart.model;
202        if (returnCode)
203            return returnCode;
204        control->setReadMode(3); // so will start with parameters
205        // see if log in list (including environment)
206        for (int i = 1; i < info_.numberArguments; i++) {
207            if (!strcmp(info_.arguments[i], "log")) {
208                if (i < info_.numberArguments - 1 && atoi(info_.arguments[i+1]) > 0)
209                    printing = true;
210                break;
211            }
212        }
213        control->setPrinting(printing);
214        if (printing)
215            printf("%d rows, %d columns and %d elements\n",
216                   info_.numberRows, info_.numberColumns, info_.numberElements);
217        if (!coinModel) {
218            solver->loadProblem(info_.numberColumns, info_.numberRows, info_.starts,
219                                info_.rows, info_.elements,
220                                info_.columnLower, info_.columnUpper, info_.objective,
221                                info_.rowLower, info_.rowUpper);
222            if (info_.numberSos) {
223                // SOS
224                solver->setSOSData(info_.numberSos, info_.sosType, info_.sosStart,
225                                   info_.sosIndices, info_.sosReference);
226            }
227        } else {
228            // save
229            control->setOriginalCoinModel(coinModel);
230            // load from coin model
231            OsiSolverLink solver1;
232            OsiSolverInterface * solver2 = solver1.clone();
233            babModel->assignSolver(solver2, false);
234            si = dynamic_cast<OsiSolverLink *>(babModel->solver()) ;
235            assert (si != NULL);
236            si->setDefaultMeshSize(0.001);
237            // need some relative granularity
238            si->setDefaultBound(100.0);
239            double dextra3 = control->doubleValue(CBC_PARAM_DBL_DEXTRA3);
240            if (dextra3)
241                si->setDefaultMeshSize(dextra3);
242            si->setDefaultBound(100000.0);
243            si->setIntegerPriority(1000);
244            si->setBiLinearPriority(10000);
245            CoinModel * model2 = (CoinModel *) coinModel;
246            int logLevel = control->intValue(CLP_PARAM_INT_LOGLEVEL);
247            si->load(*model2, true, logLevel);
248            // redo
249            solver = dynamic_cast< OsiClpSolverInterface*> (control->model()->solver());
250            lpSolver = solver->getModelPtr();
251            solver->messageHandler()->setLogLevel(0) ;
252            control->setIntValue(CBC_PARAM_INT_TESTOSI, 0);
253            if (info_.cut) {
254                printf("Sorry - can't do cuts with LOS as ruins delicate row order\n");
255                abort();
256            }
257        }
258        if (info_.cut) {
259            int numberRows = info_.numberRows;
260            int * whichRow = new int [numberRows];
261            // Row copy
262            const CoinPackedMatrix * matrixByRow = solver->getMatrixByRow();
263            const double * elementByRow = matrixByRow->getElements();
264            const int * column = matrixByRow->getIndices();
265            const CoinBigIndex * rowStart = matrixByRow->getVectorStarts();
266            const int * rowLength = matrixByRow->getVectorLengths();
267
268            const double * rowLower = solver->getRowLower();
269            const double * rowUpper = solver->getRowUpper();
270            int nDelete = 0;
271            CglStored storedAmpl;
272            for (int iRow = 0; iRow < numberRows; iRow++) {
273                if (info_.cut[iRow]) {
274                    whichRow[nDelete++] = iRow;
275                    int start = rowStart[iRow];
276                    storedAmpl.addCut(rowLower[iRow], rowUpper[iRow],
277                                      rowLength[iRow], column + start, elementByRow + start);
278                }
279            }
280            control->addCutGenerator(&storedAmpl);
281            solver->deleteRows(nDelete, whichRow);
282            // and special matrix
283            si->cleanMatrix()->deleteRows(nDelete, whichRow);
284            delete [] whichRow;
285        }
286        // If we had a solution use it
287        if (info_.primalSolution) {
288            solver->setColSolution(info_.primalSolution);
289        }
290        // status
291        if (info_.rowStatus) {
292            unsigned char * statusArray = lpSolver->statusArray();
293            memset(statusArray, 0, lpSolver->numberColumns() + lpSolver->numberRows());
294            int i;
295            for (i = 0; i < info_.numberColumns; i++)
296                statusArray[i] = (char)info_.columnStatus[i];
297            statusArray += info_.numberColumns;
298            for (i = 0; i < info_.numberRows; i++)
299                statusArray[i] = (char)info_.rowStatus[i];
300            CoinWarmStartBasis * basis = lpSolver->getBasis();
301            solver->setWarmStart(basis);
302            delete basis;
303        }
304        freeArrays1(&info_);
305        // modify objective if necessary
306        solver->setObjSense(info_.direction);
307        solver->setDblParam(OsiObjOffset, info_.offset);
308        if (info_.offset) {
309            sprintf(generalPrint, "Ampl objective offset is %g",
310                    info_.offset);
311            generalMessageHandler->message(CLP_GENERAL, generalMessages)
312            << generalPrint
313            << CoinMessageEol;
314        }
315        // Set integer variables (unless nonlinear when set)
316        if (!info_.nonLinear) {
317            for (int i = info_.numberColumns - info_.numberIntegers;
318                    i < info_.numberColumns; i++)
319                solver->setInteger(i);
320        }
321        // change argc etc
322        argc = info_.numberArguments;
323        argv = info_.arguments;
324        return 0;
325    } else {
326        return -1;
327    }
328    abort();
329    return -1;
330}
331// Export 1 OsiClpSolver, 2 CbcModel - add 10 if infeasible from odd situation
332void
333CbcAmpl::exportSolution(CbcSolver * model, int mode, const char * message)
334{
335    OsiClpSolverInterface * solver = model->originalSolver();
336    if (!solver) {
337        solver = dynamic_cast< OsiClpSolverInterface*> (model->model()->solver());
338        assert (solver);
339    }
340    ClpSimplex * lpSolver = solver->getModelPtr();
341    int numberColumns = lpSolver->numberColumns();
342    int numberRows = lpSolver->numberRows();
343    double totalTime = CoinCpuTime() - model->startTime();
344    if (mode == 1) {
345        double value = lpSolver->getObjValue() * lpSolver->getObjSense();
346        char buf[300];
347        int pos = 0;
348        int iStat = lpSolver->status();
349        if (iStat == 0) {
350            pos += sprintf(buf + pos, "optimal," );
351        } else if (iStat == 1) {
352            // infeasible
353            pos += sprintf(buf + pos, "infeasible,");
354        } else if (iStat == 2) {
355            // unbounded
356            pos += sprintf(buf + pos, "unbounded,");
357        } else if (iStat == 3) {
358            pos += sprintf(buf + pos, "stopped on iterations or time,");
359        } else if (iStat == 4) {
360            iStat = 7;
361            pos += sprintf(buf + pos, "stopped on difficulties,");
362        } else if (iStat == 5) {
363            iStat = 3;
364            pos += sprintf(buf + pos, "stopped on ctrl-c,");
365        } else {
366            pos += sprintf(buf + pos, "status unknown,");
367            iStat = 6;
368        }
369        info_.problemStatus = iStat;
370        info_.objValue = value;
371        pos += sprintf(buf + pos, " objective %.*g", ampl_obj_prec(),
372                       value);
373        sprintf(buf + pos, "\n%d iterations",
374                lpSolver->getIterationCount());
375        free(info_.primalSolution);
376        info_.primalSolution = (double *) malloc(numberColumns * sizeof(double));
377        CoinCopyN(lpSolver->primalColumnSolution(), numberColumns, info_.primalSolution);
378        free(info_.dualSolution);
379        info_.dualSolution = (double *) malloc(numberRows * sizeof(double));
380        CoinCopyN(lpSolver->dualRowSolution(), numberRows, info_.dualSolution);
381        CoinWarmStartBasis * basis = lpSolver->getBasis();
382        free(info_.rowStatus);
383        info_.rowStatus = (int *) malloc(numberRows * sizeof(int));
384        free(info_.columnStatus);
385        info_.columnStatus = (int *) malloc(numberColumns * sizeof(int));
386        // Put basis in
387        int i;
388        // free,basic,ub,lb are 0,1,2,3
389        for (i = 0; i < numberRows; i++) {
390            CoinWarmStartBasis::Status status = basis->getArtifStatus(i);
391            info_.rowStatus[i] = status;
392        }
393        for (i = 0; i < numberColumns; i++) {
394            CoinWarmStartBasis::Status status = basis->getStructStatus(i);
395            info_.columnStatus[i] = status;
396        }
397        // put buffer into info_
398        strcpy(info_.buffer, buf);
399        delete basis;
400    } else if (mode == 2) {
401        CbcModel * babModel = model->model();
402        int iStat = babModel->status();
403        int iStat2 = babModel->secondaryStatus();
404        double value = babModel->getObjValue() * lpSolver->getObjSense();
405        char buf[300];
406        int pos = 0;
407        if (iStat == 0) {
408            if (babModel->getObjValue() < 1.0e40) {
409                pos += sprintf(buf + pos, "optimal," );
410            } else {
411                // infeasible
412                iStat = 1;
413                pos += sprintf(buf + pos, "infeasible,");
414            }
415        } else if (iStat == 1) {
416            if (iStat2 != 6)
417                iStat = 3;
418            else
419                iStat = 4;
420            std::string minor[] = {"", "", "gap", "nodes", "time", "", "solutions", "user ctrl-c"};
421            pos += sprintf(buf + pos, "stopped on %s,", minor[iStat2].c_str());
422        } else if (iStat == 2) {
423            iStat = 7;
424            pos += sprintf(buf + pos, "stopped on difficulties,");
425        } else if (iStat == 5) {
426            iStat = 3;
427            pos += sprintf(buf + pos, "stopped on ctrl-c,");
428        } else {
429            pos += sprintf(buf + pos, "status unknown,");
430            iStat = 6;
431        }
432        info_.problemStatus = iStat;
433        info_.objValue = value;
434        if (babModel->getObjValue() < 1.0e40) {
435            int precision = ampl_obj_prec();
436            if (precision > 0)
437                pos += sprintf(buf + pos, " objective %.*g", precision,
438                               value);
439            else
440                pos += sprintf(buf + pos, " objective %g", value);
441        }
442        sprintf(buf + pos, "\n%d nodes, %d iterations, %g seconds",
443                babModel->getNodeCount(),
444                babModel->getIterationCount(),
445                totalTime);
446        if (babModel->bestSolution()) {
447            free(info_.primalSolution);
448            info_.primalSolution = (double *) malloc(numberColumns * sizeof(double));
449            CoinCopyN(lpSolver->primalColumnSolution(), numberColumns, info_.primalSolution);
450            free(info_.dualSolution);
451            info_.dualSolution = (double *) malloc(numberRows * sizeof(double));
452            CoinCopyN(lpSolver->dualRowSolution(), numberRows, info_.dualSolution);
453        } else {
454            info_.primalSolution = NULL;
455            info_.dualSolution = NULL;
456        }
457        // put buffer into info
458        strcpy(info_.buffer, buf);
459    } else if (mode == 11 || mode == 12) {
460        // infeasible
461        info_.problemStatus = 1;
462        info_.objValue = 1.0e100;
463        sprintf(info_.buffer, "%s", message);
464        info_.primalSolution = NULL;
465        info_.dualSolution = NULL;
466    }
467}
468// Export Data (i.e. at very end)
469void
470CbcAmpl::exportData(CbcSolver * model)
471{
472    writeAmpl(&info_);
473    freeArrays2(&info_);
474    freeArgs(&info_);
475}
476// Get useful stuff
477void
478CbcAmpl::fillInformation(CbcSolver * model,
479                         CbcSolverUsefulData & info)
480{
481    memset(&info, 0, sizeof(info));
482    info.priorities_ = info_.priorities;
483    info.sosPriority_ = info_.sosPriority;
484    info.branchDirection_ = info_.branchDirection;
485    info.primalSolution_ = info_.primalSolution;
486    info.pseudoDown_ = info_.pseudoDown;
487    info.pseudoUp_ = info_.pseudoUp;
488}
489void addAmplToCbc(CbcSolver * control)
490{
491    CbcAmpl ampl;
492    control->addUserFunction(&ampl);
493}
494extern "C" {
495    //# include "getstub.h"
496# include "asl_pfgh.h"
497}
498//#############################################################################
499// Constructors / Destructor / Assignment
500//#############################################################################
501
502//-------------------------------------------------------------------
503// Default Constructor
504//-------------------------------------------------------------------
505ClpAmplObjective::ClpAmplObjective ()
506        : ClpObjective()
507{
508    type_ = 12;
509    objective_ = NULL;
510    amplObjective_ = NULL;
511    gradient_ = NULL;
512    offset_ = 0.0;
513}
514// stolen from IPopt with changes
515typedef struct {
516    double obj_sign_;
517    ASL_pfgh * asl_;
518    double * non_const_x_;
519    int * column_; // for jacobian
520    int * rowStart_;
521    double * gradient_;
522    double * constraintValues_;
523    int nz_h_full_; // number of nonzeros in hessian
524    int nerror_;
525    bool objval_called_with_current_x_;
526    bool conval_called_with_current_x_;
527    bool jacval_called_with_current_x_;
528} CbcAmplInfo;
529#if 0
530static bool get_nlp_info(void * amplInfo, int & n, int & m, int & nnz_jac_g,
531                         int & nnz_h_lag)
532{
533    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
534    ASL_pfgh* asl = info->asl_;
535
536    n = n_var; // # of variables
537    m = n_con; // # of constraints
538    nnz_jac_g = nzc; // # of non-zeros in the jacobian
539    nnz_h_lag = info->nz_h_full_; // # of non-zeros in the hessian
540
541    return true;
542}
543
544static bool get_bounds_info(void * amplInfo, int  n, double * x_l,
545                            double * x_u, int  m, double * g_l, double * g_u)
546{
547    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
548    ASL_pfgh* asl = info->asl_;
549    assert(n == n_var);
550    assert(m == n_con);
551    int i;
552    for (i = 0; i < n; i++) {
553        x_l[i] = LUv[2*i];
554        x_u[i] = LUv[2*i+1];
555    }
556
557    for (i = 0; i < m; i++) {
558        g_l[i] = LUrhs[2*i];
559        g_u[i] = LUrhs[2*i+1];
560    }
561    return true;
562}
563
564#endif
565bool get_constraints_linearity(void * amplInfo, int  n,
566                               int * const_types)
567{
568    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
569    ASL_pfgh* asl = info->asl_;
570    //check that n is good
571    assert(n == n_con);
572    // check that there are no network constraints
573    assert(nlnc == 0 && lnc == 0);
574    //the first nlc constraints are non linear the rest is linear
575    int i;
576    for (i = 0; i < nlc; i++) {
577        const_types[i] = 1;
578    }
579    // the rest is linear
580    for (i = nlc; i < n_con; i++)
581        const_types[i] = 0;
582    return true;
583}
584#if 0
585bool get_starting_point(int  n, bool init_x, double * x, bool init_z,
586                        double * z_L, double * z_U, int  m, bool init_lambda, double * lambda)
587{
588    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
589    ASL_pfgh* asl = info->asl_;
590    assert(n == n_var);
591    assert(m == n_con);
592    int i;
593
594    if (init_x) {
595        for (i = 0; i < n; i++) {
596            if (havex0[i]) {
597                x[i] = X0[i];
598            } else {
599                x[i] = 0.0;
600            }
601        }
602    }
603
604    if (init_z) {
605        for (i = 0; i < n; i++) {
606            z_L[i] = z_U[i] = 1.0;
607        }
608    }
609
610    if (init_lambda) {
611        for (i = 0; i < m; i++) {
612            if (havepi0[i]) {
613                lambda[i] = pi0[i];
614            } else {
615                lambda[i] = 0.0;
616            }
617        }
618    }
619
620    return true;
621}
622#endif
623static bool internal_objval(CbcAmplInfo * info , double & obj_val)
624{
625    ASL_pfgh* asl = info->asl_;
626    info->objval_called_with_current_x_ = false; // in case the call below fails
627
628    if (n_obj == 0) {
629        obj_val = 0;
630        info->objval_called_with_current_x_ = true;
631        return true;
632    }  else {
633        double  retval = objval(0, info->non_const_x_, (fint*)info->nerror_);
634        if (!info->nerror_) {
635            obj_val = info->obj_sign_ * retval;
636            info->objval_called_with_current_x_ = true;
637            return true;
638        } else {
639            abort();
640        }
641    }
642
643    return false;
644}
645static bool internal_conval(CbcAmplInfo * info , double * g)
646{
647    ASL_pfgh* asl = info->asl_;
648    info->conval_called_with_current_x_ = false; // in case the call below fails
649    assert (g);
650
651    conval(info->non_const_x_, g, (fint*)info->nerror_);
652
653    if (!info->nerror_) {
654        info->conval_called_with_current_x_ = true;
655        return true;
656    } else {
657        abort();
658    }
659    return false;
660}
661
662static bool apply_new_x(CbcAmplInfo * info  , bool new_x, int  n, const double * x)
663{
664    ASL_pfgh* asl = info->asl_;
665
666    if (new_x) {
667        // update the flags so these methods are called
668        // before evaluating the hessian
669        info->conval_called_with_current_x_ = false;
670        info->objval_called_with_current_x_ = false;
671        info->jacval_called_with_current_x_ = false;
672
673        //copy the data to the non_const_x_
674        if (!info->non_const_x_) {
675            info->non_const_x_ = new double [n];
676        }
677
678        for (int  i = 0; i < n; i++) {
679            info->non_const_x_[i] = x[i];
680        }
681
682        // tell ampl that we have a new x
683        xknowne(info->non_const_x_, (fint*)info->nerror_);
684        return info->nerror_ ? false : true;
685    }
686
687    return true;
688}
689static bool eval_f(void * amplInfo, int  n, const double * x, bool new_x, double & obj_value)
690{
691    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
692    if (!apply_new_x(info, new_x, n, x)) {
693        return false;
694    }
695
696    return internal_objval(info, obj_value);
697}
698
699static bool eval_grad_f(void * amplInfo, int  n, const double * x, bool new_x, double * grad_f)
700{
701    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
702    ASL_pfgh* asl = info->asl_;
703    if (!apply_new_x(info, new_x, n, x)) {
704        return false;
705    }
706    int i;
707
708    if (n_obj == 0) {
709        for (i = 0; i < n; i++) {
710            grad_f[i] = 0.;
711        }
712    } else {
713        objgrd(0, info->non_const_x_, grad_f, (fint*)info->nerror_);
714        if (info->nerror_) {
715            return false;
716        }
717
718        if (info->obj_sign_ == -1) {
719            for (i = 0; i < n; i++) {
720                grad_f[i] = -grad_f[i];
721            }
722        }
723    }
724    return true;
725}
726static bool eval_g(void * amplInfo, int  n, const double * x, bool new_x, double * g)
727{
728    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
729#ifndef NDEBUG
730    ASL_pfgh* asl = info->asl_;
731#endif
732    // warning: n_var is a macro that assumes we have a variable called asl
733    assert(n == n_var);
734
735    if (!apply_new_x(info, new_x, n, x)) {
736        return false;
737    }
738
739    return internal_conval(info, g);
740}
741
742static bool eval_jac_g(void * amplInfo, int  n, const double * x, bool new_x,
743                       double * values)
744{
745    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
746    ASL_pfgh* asl = info->asl_;
747    assert(n == n_var);
748
749    assert (values);
750    if (!apply_new_x(info, new_x, n, x)) {
751        return false;
752    }
753
754    jacval(info->non_const_x_, values, (fint*)info->nerror_);
755    if (!info->nerror_) {
756        return true;
757    } else {
758        abort();
759    }
760    return false;
761}
762#if 0
763
764static bool eval_h(void * amplInfo, int  n, const double * x, bool new_x,
765                   double  obj_factor, int  m, const double * lambda,
766                   bool new_lambda, int  nele_hess, int * iRow,
767                   int * jCol, double * values)
768{
769    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
770    ASL_pfgh* asl = info->asl_;
771    assert(n == n_var);
772    assert(m == n_con);
773    int i;
774
775    if (iRow && jCol && !values) {
776        // setup the structure
777        int k = 0;
778        for (int i = 0; i < n; i++) {
779            for (int j = sputinfo->hcolstarts[i]; j < sputinfo->hcolstarts[i+1]; j++) {
780                //iRow[k] = i + 1;
781                iRow[k] = i;
782                jCol[k] = sputinfo->hrownos[j] + 1;
783                k++;
784            }
785        }
786        assert(k == nele_hess);
787        return true;
788    } else if (!iRow & !jCol && values) {
789        if (!apply_new_x(info, new_x, n, x)) {
790            return false;
791        }
792        if (!info->objval_called_with_current_x_) {
793            double  dummy;
794            internal_objval(info, dummy);
795            internal_conval(info, m, NULL);
796        }
797        if (!info->conval_called_with_current_x_) {
798            internal_conval(info, m, NULL);
799        }
800        // copy lambda to non_const_lambda - note, we do not store a copy like
801        // we do with x since lambda is only used here and not in other calls
802        double * non_const_lambda = new double [m];
803        for (i = 0; i < m; i++) {
804            non_const_lambda[i] = lambda[i];
805        }
806
807        real ow = info->obj_sign_ * obj_factor;
808        sphes(values, -1, &ow, non_const_lambda);
809
810        delete [] non_const_lambda;
811        return true;
812    } else {
813        assert(false && "Invalid combination of iRow, jCol, and values pointers");
814    }
815
816    return false;
817}
818#endif
819//-------------------------------------------------------------------
820// Useful Constructor
821//-------------------------------------------------------------------
822ClpAmplObjective::ClpAmplObjective (void * amplInfo)
823        : ClpObjective()
824{
825    type_ = 12;
826    activated_ = 1;
827    gradient_ = NULL;
828    objective_ = NULL;
829    offset_ = 0.0;
830    amplObjective_ = amplInfo;
831}
832
833//-------------------------------------------------------------------
834// Copy constructor
835//-------------------------------------------------------------------
836ClpAmplObjective::ClpAmplObjective (const ClpAmplObjective & rhs)
837        : ClpObjective(rhs)
838{
839    amplObjective_ = rhs.amplObjective_;
840    offset_ = rhs.offset_;
841    type_ = rhs.type_;
842    if (!amplObjective_) {
843        objective_ = NULL;
844        gradient_ = NULL;
845    } else {
846        CbcAmplInfo * info = (CbcAmplInfo *) amplObjective_;
847        ASL_pfgh* asl = info->asl_;
848
849        int numberColumns = n_var;;
850        if (rhs.objective_) {
851            objective_ = new double [numberColumns];
852            memcpy(objective_, rhs.objective_, numberColumns*sizeof(double));
853        } else {
854            objective_ = NULL;
855        }
856        if (rhs.gradient_) {
857            gradient_ = new double [numberColumns];
858            memcpy(gradient_, rhs.gradient_, numberColumns*sizeof(double));
859        } else {
860            gradient_ = NULL;
861        }
862    }
863}
864
865
866//-------------------------------------------------------------------
867// Destructor
868//-------------------------------------------------------------------
869ClpAmplObjective::~ClpAmplObjective ()
870{
871    delete [] objective_;
872    delete [] gradient_;
873}
874
875//----------------------------------------------------------------
876// Assignment operator
877//-------------------------------------------------------------------
878ClpAmplObjective &
879ClpAmplObjective::operator=(const ClpAmplObjective & rhs)
880{
881    if (this != &rhs) {
882        delete [] objective_;
883        delete [] gradient_;
884        amplObjective_ = rhs.amplObjective_;
885        offset_ = rhs.offset_;
886        type_ = rhs.type_;
887        if (!amplObjective_) {
888            objective_ = NULL;
889            gradient_ = NULL;
890        } else {
891            CbcAmplInfo * info = (CbcAmplInfo *) amplObjective_;
892            ASL_pfgh* asl = info->asl_;
893
894            int numberColumns = n_var;;
895            if (rhs.objective_) {
896                objective_ = new double [numberColumns];
897                memcpy(objective_, rhs.objective_, numberColumns*sizeof(double));
898            } else {
899                objective_ = NULL;
900            }
901            if (rhs.gradient_) {
902                gradient_ = new double [numberColumns];
903                memcpy(gradient_, rhs.gradient_, numberColumns*sizeof(double));
904            } else {
905                gradient_ = NULL;
906            }
907        }
908    }
909    return *this;
910}
911
912// Returns gradient
913double *
914ClpAmplObjective::gradient(const ClpSimplex * model,
915                           const double * solution, double & offset, bool refresh,
916                           int includeLinear)
917{
918    if (model)
919        assert (model->optimizationDirection() == 1.0);
920    bool scaling = false;
921    if (model && (model->rowScale() ||
922                  model->objectiveScale() != 1.0 || model->optimizationDirection() != 1.0))
923        scaling = true;
924    const double * cost = NULL;
925    if (model)
926        cost = model->costRegion();
927    if (!cost) {
928        // not in solve
929        cost = objective_;
930        scaling = false;
931    }
932    assert (!scaling);
933    if (!amplObjective_ || !solution || !activated_) {
934        offset = offset_;
935        return objective_;
936    } else {
937        if (refresh || !gradient_) {
938            CbcAmplInfo * info = (CbcAmplInfo *) amplObjective_;
939            ASL_pfgh* asl = info->asl_;
940            int numberColumns = n_var;;
941
942            if (!gradient_)
943                gradient_ = new double[numberColumns];
944            assert (solution);
945            eval_grad_f(amplObjective_, numberColumns, solution, true, gradient_);
946            // Is this best way?
947            double objValue = 0.0;
948            eval_f(amplObjective_, numberColumns, solution, false, objValue);
949            double objValue2 = 0.0;
950            for (int i = 0; i < numberColumns; i++)
951                objValue2 += gradient_[i] * solution[i];
952            offset_ = objValue2 - objValue; // or other way???
953            if (model && model->optimizationDirection() != 1.0) {
954                offset *= model->optimizationDirection();
955                for (int i = 0; i < numberColumns; i++)
956                    gradient_[i] *= -1.0;
957            }
958        }
959        offset = offset_;
960        return gradient_;
961    }
962}
963
964//-------------------------------------------------------------------
965// Clone
966//-------------------------------------------------------------------
967ClpObjective * ClpAmplObjective::clone() const
968{
969    return new ClpAmplObjective(*this);
970}
971// Resize objective
972void
973ClpAmplObjective::resize(int newNumberColumns)
974{
975    CbcAmplInfo * info = (CbcAmplInfo *) amplObjective_;
976    ASL_pfgh* asl = info->asl_;
977    int numberColumns = n_var;;
978    if (numberColumns != newNumberColumns) {
979        abort();
980    }
981
982}
983// Delete columns in  objective
984void
985ClpAmplObjective::deleteSome(int numberToDelete, const int * which)
986{
987    if (numberToDelete)
988        abort();
989}
990/* Returns reduced gradient.Returns an offset (to be added to current one).
991 */
992double
993ClpAmplObjective::reducedGradient(ClpSimplex * model, double * region,
994                                  bool useFeasibleCosts)
995{
996    int numberRows = model->numberRows();
997    int numberColumns = model->numberColumns();
998
999    //work space
1000    CoinIndexedVector  * workSpace = model->rowArray(0);
1001
1002    CoinIndexedVector arrayVector;
1003    arrayVector.reserve(numberRows + 1);
1004
1005    int iRow;
1006#ifdef CLP_DEBUG
1007    workSpace->checkClear();
1008#endif
1009    double * array = arrayVector.denseVector();
1010    int * index = arrayVector.getIndices();
1011    int number = 0;
1012    const double * costNow = gradient(model, model->solutionRegion(), offset_,
1013                                      true, useFeasibleCosts ? 2 : 1);
1014    double * cost = model->costRegion();
1015    const int * pivotVariable = model->pivotVariable();
1016    for (iRow = 0; iRow < numberRows; iRow++) {
1017        int iPivot = pivotVariable[iRow];
1018        double value;
1019        if (iPivot < numberColumns)
1020            value = costNow[iPivot];
1021        else if (!useFeasibleCosts)
1022            value = cost[iPivot];
1023        else
1024            value = 0.0;
1025        if (value) {
1026            array[iRow] = value;
1027            index[number++] = iRow;
1028        }
1029    }
1030    arrayVector.setNumElements(number);
1031
1032    // Btran basic costs
1033    model->factorization()->updateColumnTranspose(workSpace, &arrayVector);
1034    double * work = workSpace->denseVector();
1035    ClpFillN(work, numberRows, 0.0);
1036    // now look at dual solution
1037    double * rowReducedCost = region + numberColumns;
1038    double * dual = rowReducedCost;
1039    const double * rowCost = cost + numberColumns;
1040    for (iRow = 0; iRow < numberRows; iRow++) {
1041        dual[iRow] = array[iRow];
1042    }
1043    double * dj = region;
1044    ClpDisjointCopyN(costNow, numberColumns, dj);
1045
1046    model->transposeTimes(-1.0, dual, dj);
1047    for (iRow = 0; iRow < numberRows; iRow++) {
1048        // slack
1049        double value = dual[iRow];
1050        value += rowCost[iRow];
1051        rowReducedCost[iRow] = value;
1052    }
1053    return offset_;
1054}
1055/* Returns step length which gives minimum of objective for
1056   solution + theta * change vector up to maximum theta.
1057
1058   arrays are numberColumns+numberRows
1059*/
1060double
1061ClpAmplObjective::stepLength(ClpSimplex * model,
1062                             const double * solution,
1063                             const double * change,
1064                             double maximumTheta,
1065                             double & currentObj,
1066                             double & predictedObj,
1067                             double & thetaObj)
1068{
1069    // Assume convex
1070    CbcAmplInfo * info = (CbcAmplInfo *) amplObjective_;
1071    ASL_pfgh* asl = info->asl_;
1072
1073    int numberColumns = n_var;;
1074    double * tempSolution = new double [numberColumns];
1075    double * tempGradient = new double [numberColumns];
1076    // current
1077    eval_f(amplObjective_, numberColumns, solution, true, currentObj);
1078    double objA = currentObj;
1079    double thetaA = 0.0;
1080    // at maximum
1081    int i;
1082    for (i = 0; i < numberColumns; i++)
1083        tempSolution[i] = solution[i] + maximumTheta * change[i];
1084    eval_f(amplObjective_, numberColumns, tempSolution, true, thetaObj);
1085    double objC = thetaObj;
1086    double thetaC = maximumTheta;
1087    double objB = 0.5 * (objA + objC);
1088    double thetaB = 0.5 * maximumTheta;
1089    double gradientNorm = 1.0e6;
1090    while (gradientNorm > 1.0e-6 && thetaC - thetaA > 1.0e-8) {
1091        for (i = 0; i < numberColumns; i++)
1092            tempSolution[i] = solution[i] + thetaB * change[i];
1093        eval_grad_f(amplObjective_, numberColumns, tempSolution, true, tempGradient);
1094        eval_f(amplObjective_, numberColumns, tempSolution, false, objB);
1095        double changeObj = 0.0;
1096        gradientNorm = 0.0;
1097        for (i = 0; i < numberColumns; i++) {
1098            changeObj += tempGradient[i] * change[i];
1099            gradientNorm += tempGradient[i] * tempGradient[i];
1100        }
1101        gradientNorm = fabs(changeObj) / sqrt(gradientNorm);
1102        // Should try and get quadratic convergence by interpolation
1103        if (changeObj < 0.0) {
1104            // increasing is good
1105            thetaA = thetaB;
1106        } else {
1107            // decreasing is good
1108            thetaC = thetaB;
1109        }
1110        thetaB = 0.5 * (thetaA + thetaC);
1111    }
1112    delete [] tempSolution;
1113    delete [] tempGradient;
1114    predictedObj = objB;
1115    return thetaB;
1116}
1117// Return objective value (without any ClpModel offset) (model may be NULL)
1118double
1119ClpAmplObjective::objectiveValue(const ClpSimplex * model, const double * solution) const
1120{
1121    CbcAmplInfo * info = (CbcAmplInfo *) amplObjective_;
1122    ASL_pfgh* asl = info->asl_;
1123
1124    int numberColumns = n_var;;
1125    // current
1126    double currentObj = 0.0;
1127    eval_f(amplObjective_, numberColumns, solution, true, currentObj);
1128    return currentObj;
1129}
1130// Scale objective
1131void
1132ClpAmplObjective::reallyScale(const double * columnScale)
1133{
1134    abort();
1135}
1136/* Given a zeroed array sets nonlinear columns to 1.
1137   Returns number of nonlinear columns
1138*/
1139int
1140ClpAmplObjective::markNonlinear(char * which)
1141{
1142    int iColumn;
1143    CbcAmplInfo * info = (CbcAmplInfo *) amplObjective_;
1144    ASL_pfgh* asl = info->asl_;
1145    int nonLinear = CoinMax(nlvc, nlvo);
1146    for (iColumn = 0; iColumn < nonLinear; iColumn++) {
1147        which[iColumn] = 1;
1148    }
1149    int numberNonLinearColumns = 0;
1150    int numberColumns = n_var;;
1151    for (iColumn = 0; iColumn < numberColumns; iColumn++) {
1152        if (which[iColumn])
1153            numberNonLinearColumns++;
1154    }
1155    return numberNonLinearColumns;
1156}
1157// Say we have new primal solution - so may need to recompute
1158void
1159ClpAmplObjective::newXValues()
1160{
1161    CbcAmplInfo * info = (CbcAmplInfo *) amplObjective_;
1162    info->conval_called_with_current_x_ = false;
1163    info->objval_called_with_current_x_ = false;
1164    info->jacval_called_with_current_x_ = false;
1165}
1166//#############################################################################
1167// Constructors / Destructor / Assignment
1168//#############################################################################
1169//-------------------------------------------------------------------
1170// Default Constructor
1171//-------------------------------------------------------------------
1172ClpConstraintAmpl::ClpConstraintAmpl ()
1173        : ClpConstraint()
1174{
1175    type_ = 3;
1176    column_ = NULL;
1177    coefficient_ = NULL;
1178    numberCoefficients_ = 0;
1179    amplInfo_ = NULL;
1180}
1181
1182//-------------------------------------------------------------------
1183// Useful Constructor
1184//-------------------------------------------------------------------
1185ClpConstraintAmpl::ClpConstraintAmpl (int row, void * amplInfo)
1186        : ClpConstraint()
1187{
1188    type_ = 3;
1189    rowNumber_ = row;
1190    amplInfo_ = amplInfo;
1191    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo_;
1192#ifndef NDEBUG
1193    ASL_pfgh* asl = info->asl_;
1194#endif
1195    // warning: nlc is a macro that assumes we have a variable called asl
1196    assert (rowNumber_ < nlc);
1197    numberCoefficients_ = info->rowStart_[rowNumber_+1] - info->rowStart_[rowNumber_];
1198    column_ = CoinCopyOfArray(info->column_ + info->rowStart_[rowNumber_], numberCoefficients_);
1199    coefficient_ = new double [numberCoefficients_];;
1200}
1201
1202//-------------------------------------------------------------------
1203// Copy constructor
1204//-------------------------------------------------------------------
1205ClpConstraintAmpl::ClpConstraintAmpl (const ClpConstraintAmpl & rhs)
1206        : ClpConstraint(rhs)
1207{
1208    numberCoefficients_ = rhs.numberCoefficients_;
1209    column_ = CoinCopyOfArray(rhs.column_, numberCoefficients_);
1210    coefficient_ = CoinCopyOfArray(rhs.coefficient_, numberCoefficients_);
1211}
1212
1213
1214//-------------------------------------------------------------------
1215// Destructor
1216//-------------------------------------------------------------------
1217ClpConstraintAmpl::~ClpConstraintAmpl ()
1218{
1219    delete [] column_;
1220    delete [] coefficient_;
1221}
1222
1223//----------------------------------------------------------------
1224// Assignment operator
1225//-------------------------------------------------------------------
1226ClpConstraintAmpl &
1227ClpConstraintAmpl::operator=(const ClpConstraintAmpl & rhs)
1228{
1229    if (this != &rhs) {
1230        delete [] column_;
1231        delete [] coefficient_;
1232        numberCoefficients_ = rhs.numberCoefficients_;
1233        column_ = CoinCopyOfArray(rhs.column_, numberCoefficients_);
1234        coefficient_ = CoinCopyOfArray(rhs.coefficient_, numberCoefficients_);
1235    }
1236    return *this;
1237}
1238//-------------------------------------------------------------------
1239// Clone
1240//-------------------------------------------------------------------
1241ClpConstraint * ClpConstraintAmpl::clone() const
1242{
1243    return new ClpConstraintAmpl(*this);
1244}
1245
1246// Returns gradient
1247int
1248ClpConstraintAmpl::gradient(const ClpSimplex * model,
1249                            const double * solution,
1250                            double * gradient,
1251                            double & functionValue,
1252                            double & offset,
1253                            bool useScaling,
1254                            bool refresh) const
1255{
1256    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo_;
1257    ASL_pfgh* asl = info->asl_;
1258    int numberColumns = n_var;;
1259    // If not done then do all
1260    if (!info->jacval_called_with_current_x_) {
1261        bool getStuff = eval_g(amplInfo_, numberColumns, solution, true, info->constraintValues_);
1262        assert (getStuff);
1263        getStuff = eval_jac_g(amplInfo_, numberColumns, solution, false, info->gradient_);
1264        assert (getStuff);
1265        info->jacval_called_with_current_x_ = getStuff;
1266    }
1267    if (refresh || !lastGradient_) {
1268        functionValue_ = info->constraintValues_[rowNumber_];
1269        offset_ = functionValue_; // sign??
1270        if (!lastGradient_)
1271            lastGradient_ = new double[numberColumns];
1272        CoinZeroN(lastGradient_, numberColumns);
1273        assert (!(model && model->rowScale() && useScaling));
1274        int i;
1275        int start = info->rowStart_[rowNumber_];
1276        assert (numberCoefficients_ == info->rowStart_[rowNumber_+1] - start);
1277        for (i = 0; i < numberCoefficients_; i++) {
1278            int iColumn = column_[i];
1279            double valueS = solution[iColumn];
1280            double valueG = info->gradient_[start+i];
1281            lastGradient_[iColumn] = valueG;
1282            offset_ -= valueS * valueG;
1283        }
1284    }
1285    functionValue = functionValue_;
1286    offset = offset_;
1287    memcpy(gradient, lastGradient_, numberColumns*sizeof(double));
1288    return 0;
1289}
1290// Resize constraint
1291void
1292ClpConstraintAmpl::resize(int newNumberColumns)
1293{
1294    abort();
1295}
1296// Delete columns in  constraint
1297void
1298ClpConstraintAmpl::deleteSome(int numberToDelete, const int * which)
1299{
1300    if (numberToDelete) {
1301        abort();
1302    }
1303}
1304// Scale constraint
1305void
1306ClpConstraintAmpl::reallyScale(const double * columnScale)
1307{
1308    abort();
1309}
1310/* Given a zeroed array sets nonlinear columns to 1.
1311   Returns number of nonlinear columns
1312*/
1313int
1314ClpConstraintAmpl::markNonlinear(char * which) const
1315{
1316    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo_;
1317    ASL_pfgh* asl = info->asl_;
1318    int iColumn;
1319    int numberNon = 0;
1320    int nonLinear = CoinMax(nlvc, nlvo);
1321    for (iColumn = 0; iColumn < numberCoefficients_; iColumn++) {
1322        int jColumn = column_[iColumn];
1323        if (jColumn < nonLinear) {
1324            which[jColumn] = 1;
1325            numberNon++;
1326        }
1327    }
1328    return numberNon;
1329}
1330/* Given a zeroed array sets possible nonzero coefficients to 1.
1331   Returns number of nonzeros
1332*/
1333int
1334ClpConstraintAmpl::markNonzero(char * which) const
1335{
1336    int iColumn;
1337    for (iColumn = 0; iColumn < numberCoefficients_; iColumn++) {
1338        which[column_[iColumn]] = 1;
1339    }
1340    return numberCoefficients_;
1341}
1342// Number of coefficients
1343int
1344ClpConstraintAmpl::numberCoefficients() const
1345{
1346    return numberCoefficients_;
1347}
1348// Say we have new primal solution - so may need to recompute
1349void
1350ClpConstraintAmpl::newXValues()
1351{
1352    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo_;
1353    info->conval_called_with_current_x_ = false;
1354    info->objval_called_with_current_x_ = false;
1355    info->jacval_called_with_current_x_ = false;
1356}
1357/* Load nonlinear part of problem from AMPL info
1358   Returns 0 if linear
1359   1 if quadratic objective
1360   2 if quadratic constraints
1361   3 if nonlinear objective
1362   4 if nonlinear constraints
1363   -1 on failure
1364*/
1365int
1366ClpSimplex::loadNonLinear(void * amplInfo, int & numberConstraints,
1367                          ClpConstraint ** & constraints)
1368{
1369    numberConstraints = 0;
1370    constraints = NULL;
1371    CbcAmplInfo * info = (CbcAmplInfo *) amplInfo;
1372    ASL_pfgh* asl = info->asl_;
1373    // For moment don't say quadratic
1374    int type = 0;
1375    if (nlo + nlc) {
1376        // nonlinear
1377        if (!nlc) {
1378            type = 3;
1379            delete objective_;
1380            objective_ = new ClpAmplObjective(amplInfo);
1381        } else {
1382            type = 4;
1383            numberConstraints = nlc;
1384            constraints = new ClpConstraint * [numberConstraints];
1385            if (nlo) {
1386                delete objective_;
1387                objective_ = new ClpAmplObjective(amplInfo);
1388            }
1389            for (int i = 0; i < numberConstraints; i++) {
1390                constraints[i] = new ClpConstraintAmpl(i, amplInfo);
1391            }
1392        }
1393    }
1394    return type;
1395}
1396#else
1397#include "ClpSimplex.hpp"
1398#include "ClpConstraint.hpp"
1399int
1400ClpSimplex::loadNonLinear(void * , int & ,
1401                          ClpConstraint ** & )
1402{
1403    abort();
1404    return 0;
1405}
1406#endif
Note: See TracBrowser for help on using the repository browser.