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

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

Added Lou's annotations to Cbc_ampl.cpp, CbcSolver?.cpp, CbcSolver?.hpp, CbcStrategy?.cpp, CbcTreeLocal?.cpp, ClpAmplStuff?.cpp, CoinSolve?.cpp, and unitTestClp.cpp

File size: 44.0 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(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(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(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(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.