-
Notifications
You must be signed in to change notification settings - Fork 43
/
solver.h
1456 lines (1205 loc) · 46.6 KB
/
solver.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
A mathematical optimization solver.
Copyright (C) 2012 AMPL Optimization Inc
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that the copyright notice and this permission notice and warranty
disclaimer appear in supporting documentation.
The author and AMPL Optimization Inc disclaim all warranties with
regard to this software, including all implied warranties of
merchantability and fitness. In no event shall the author be liable
for any special, indirect or consequential damages or any damages
whatsoever resulting from loss of use, data or profits, whether in an
action of contract, negligence or other tortious action, arising out
of or in connection with the use or performance of this software.
Author: Victor Zverovich
*/
#ifndef MP_SOLVER_H_
#define MP_SOLVER_H_
#include <cctype>
#include <csignal>
#include <cstring>
#include <stdint.h>
#include <limits>
#include <memory>
#include <set>
#include <string>
#include <vector>
#if MP_USE_ATOMIC
# include <atomic>
#endif
#include "mp/arrayref.h"
#include "mp/clock.h"
#include "mp/error.h"
#include "mp/format.h"
#include "mp/nl-reader.h"
#include "mp/option.h"
#include "mp/os.h"
#include "mp/problem-builder.h"
#include "mp/sol.h"
#include "mp/suffix.h"
namespace mp {
class ASLProblem;
class Solver;
// Information about a possible option value.
struct OptionValueInfo {
const char *value;
const char *description;
intptr_t data; // Solver-specific data associated with this value.
};
// A reference to an array of OptionValueInfo objects.
class ValueArrayRef {
private:
const OptionValueInfo *values_;
int size_;
public:
ValueArrayRef() : values_(), size_() {}
template <int SIZE>
ValueArrayRef(const OptionValueInfo (&values)[SIZE], int offset = 0)
: values_(values + offset), size_(SIZE - offset) {
assert(offset >= 0 && offset < SIZE);
}
int size() const { return size_; }
typedef const OptionValueInfo *iterator;
iterator begin() const { return values_; }
iterator end() const { return values_ + size_; }
};
namespace internal {
// Formats the string s containing the reStructuredText (RST) markup
// and writes it to w.
// w: stream writer used for output
// indent: indentation to use for the formatted text
// s: string containing reStructuredText to format
// values: information about possible option values to be formatted by the
// value-table directive
void FormatRST(fmt::Writer &w, fmt::CStringRef s,
int indent = 0, ValueArrayRef values = ValueArrayRef());
// A helper class for implementing an option of type T.
template <typename T>
struct OptionHelper;
template <>
struct OptionHelper<int> {
typedef int Arg;
static void Write(fmt::Writer &w, Arg value) { w << value; }
static int Parse(const char *&s);
static int CastArg(fmt::LongLong value) { return static_cast<int>(value); }
};
template <>
struct OptionHelper<fmt::LongLong> {
typedef fmt::LongLong Arg;
static void Write(fmt::Writer &w, Arg value) { w << value; }
static fmt::LongLong Parse(const char *&s) {
return OptionHelper<int>::Parse(s); // TODO
}
static fmt::LongLong CastArg(fmt::LongLong value) { return value; }
};
template <>
struct OptionHelper<double> {
typedef double Arg;
static void Write(fmt::Writer &w, double value) { w << value; }
static double Parse(const char *&s);
static double CastArg(double value) { return value; }
};
template <>
struct OptionHelper<std::string> {
typedef fmt::StringRef Arg;
static void Write(fmt::Writer &w, const std::string &s) { w << s; }
static std::string Parse(const char *&s);
static fmt::StringRef CastArg(fmt::StringRef s) { return s; }
};
inline OptionError OptionTypeError(fmt::StringRef name, fmt::StringRef type) {
return OptionError(
fmt::format("Option \"{}\" is not of type \"{}\"", name, type));
}
// Perform demo version checks if necessary.
void CheckDemoVersion(const NLHeader &h);
} // namespace internal
// An interface for receiving errors reported via Solver::ReportError.
class ErrorHandler {
protected:
~ErrorHandler() {}
public:
virtual void HandleError(fmt::CStringRef message) = 0;
};
// An interface for receiving solver output.
class OutputHandler {
protected:
~OutputHandler() {}
public:
virtual void HandleOutput(fmt::CStringRef output) = 0;
};
// An interface for receiving solutions.
class SolutionHandler {
public:
virtual ~SolutionHandler() {}
// Receives a feasible solution.
virtual void HandleFeasibleSolution(fmt::CStringRef message,
const double *values, const double *dual_values, double obj_value) = 0;
// Receives the final solution or a notification that the problem is
// infeasible or unbounded.
virtual void HandleSolution(int status, fmt::CStringRef message,
const double *values, const double *dual_values, double obj_value) = 0;
};
class BasicSolutionHandler : public SolutionHandler {
public:
virtual void HandleFeasibleSolution(fmt::CStringRef,
const double *, const double *, double) {}
virtual void HandleSolution(int, fmt::CStringRef,
const double *, const double *, double) {}
};
// Interrupt handler.
// Returns true if the solver was interrupted, false if it is not running.
typedef bool (*InterruptHandler)(void *);
// An interface for interrupting solution process.
// When a solver is run in a terminal it should respond to SIGINT (Ctrl-C)
// by interrupting its execution and returning the best solution found.
// This can be done in two ways. The first one is to check the return value
// of Interrupter::Stop() periodically. The second is to register an object
// that implements the Interruptible interface.
class Interrupter {
public:
virtual ~Interrupter() {}
// Returns true if the solution process should be stopped.
virtual bool Stop() const = 0;
// Sets a handler function.
// The handler function must be safe to call from a signal handler.
// In particular, it must only call async-signal-safe library functions.
virtual void SetHandler(InterruptHandler handler, void *data) = 0;
};
// A solver option.
class SolverOption {
private:
const char *name_;
const char *description_;
ValueArrayRef values_;
bool is_flag_;
public:
// Constructs a SolverOption object.
//
// The solver option stores pointers to the passed name and description and
// doesn't copy the strings. Normally both the name and the description are
// string literals and have static storage duration but if this is not the
// case make sure that these strings' lifetimes are longer than that of the
// option object.
//
// The description should be written in a subset of reStructuredText (RST).
// Currently the following RST constructs are supported:
//
// * paragraphs
// * bullet lists
// * literal blocks
// * line blocks
// * the value-table directive (.. value-table::) which is replaced by a
// table of option values as given by the values array
//
// name: option name
// description: option description
// values: information about possible option values
SolverOption(const char *name, const char *description,
ValueArrayRef values = ValueArrayRef(), bool is_flag = false)
: name_(name), description_(description),
values_(values), is_flag_(is_flag) {}
virtual ~SolverOption() {}
// Returns the option name.
const char *name() const { return name_; }
// Returns the option description.
const char *description() const { return description_; }
// Returns the information about possible values.
ValueArrayRef values() const { return values_; }
// Returns true if this option is a flag, i.e. it doesn't take a value.
bool is_flag() const { return is_flag_; }
// Returns the option value.
virtual void GetValue(fmt::LongLong &) const {
throw internal::OptionTypeError(name_, "int");
}
virtual void GetValue(double &) const {
throw internal::OptionTypeError(name_, "double");
}
virtual void GetValue(std::string &) const {
throw internal::OptionTypeError(name_, "string");
}
virtual void GetValue(int &int_value) const {
fmt::LongLong value = 0;
GetValue(value);
if (value < std::numeric_limits<int>::min() ||
value > std::numeric_limits<int>::max()) {
throw Error("Value {} doesn't fit in int", value);
}
int_value = static_cast<int>(value);
}
template <typename T>
T GetValue() const {
T value = T();
GetValue(value);
return value;
}
// Sets the option value or throws InvalidOptionValue if the value is invalid.
virtual void SetValue(fmt::LongLong) {
throw internal::OptionTypeError(name_, "int");
}
virtual void SetValue(double) {
throw internal::OptionTypeError(name_, "double");
}
virtual void SetValue(fmt::StringRef) {
throw internal::OptionTypeError(name_, "string");
}
virtual void SetValue(int value) {
fmt::LongLong long_value = value;
SetValue(long_value);
}
// Formats the option value. Throws OptionError in case of error.
virtual void Write(fmt::Writer &w) = 0;
// Parses a string and sets the option value. Throws InvalidOptionValue
// if the value is invalid or OptionError in case of another error.
virtual void Parse(const char *&s) = 0;
};
// An exception thrown when an invalid value is provided for an option.
class InvalidOptionValue : public OptionError {
private:
template <typename T>
static std::string Format(fmt::StringRef name, T value) {
return fmt::format("Invalid value \"{}\" for option \"{}\"", value, name);
}
public:
template <typename T>
InvalidOptionValue(fmt::StringRef name, T value)
: OptionError(Format(name, value)) {}
template <typename T>
InvalidOptionValue(const SolverOption &opt, T value)
: OptionError(Format(opt.name(), value)) {}
};
template <typename T>
class TypedSolverOption : public SolverOption {
public:
TypedSolverOption(const char *name, const char *description,
ValueArrayRef values = ValueArrayRef())
: SolverOption(name, description, values) {}
void Write(fmt::Writer &w) { w << GetValue<T>(); }
void Parse(const char *&s) {
const char *start = s;
T value = internal::OptionHelper<T>::Parse(s);
if (*s && !std::isspace(*s)) {
do ++s;
while (*s && !std::isspace(*s));
throw InvalidOptionValue(name(), std::string(start, s - start));
}
SetValue(value);
}
};
// A mathematical optimization solver.
//
// Example:
//
// class MySolver : public Solver {
// public:
// void GetTestOption(const char *name, int value, int info) {
// // Returns the option value; info is an arbitrary value passed as
// // the last argument to AddIntOption. It can be useful if the same
// // function handles multiple options.
// ...
// }
// void SetTestOption(const char *name, int value, int info) {
// // Set the option value; info is the same as in GetTestOption.
// ...
// }
//
// MySolver() {
// AddIntOption("test", "This is a test option",
// &MySolver::GetTestOption, &MySolver::SetTestOption, 42);
// }
// };
class Solver : private ErrorHandler,
private OutputHandler, private Interrupter {
private:
std::string name_;
std::string long_name_;
std::string version_;
std::string license_info_;
long date_;
int wantsol_;
int obj_precision_;
// Index of the objective to optimize starting from 1, 0 to ignore
// objective, or -1 to use the first objective if there is one.
int objno_;
enum {SHOW_VERSION = 1, AMPL_FLAG = 2};
int bool_options_;
// The filename stub for returning multiple solutions.
std::string solution_stub_;
// Specifies whether to return the number of solutions in the .nsol suffix.
bool count_solutions_;
unsigned read_flags_; // flags passed to Problem::Read
struct OptionNameLess {
bool operator()(const SolverOption *lhs, const SolverOption *rhs) const;
};
std::string option_header_;
typedef std::set<SolverOption*, OptionNameLess> OptionSet;
OptionSet options_;
bool timing_;
bool multiobj_;
bool has_errors_;
OutputHandler *output_handler_;
ErrorHandler *error_handler_;
Interrupter *interrupter_;
int GetWantSol(const SolverOption &) const { return wantsol_; }
void SetWantSol(const SolverOption &, int value) {
if ((value & ~0xf) != 0)
throw InvalidOptionValue("wantsol", value);
wantsol_ = value;
}
std::string GetSolutionStub(const SolverOption &) const {
return solution_stub_;
}
void SetSolutionStub(const SolverOption &, fmt::StringRef value) {
solution_stub_ = value.to_string();
}
public:
class SuffixInfo {
private:
const char *name_;
const char *table_;
int kind_;
int nextra_;
public:
SuffixInfo(const char *name, const char *table, int kind, int nextra)
: name_(name), table_(table), kind_(kind), nextra_(nextra) {}
const char *name() const { return name_; }
const char *table() const { return table_; }
int kind() const { return kind_; }
int nextra() const { return nextra_; }
};
typedef std::vector<SuffixInfo> SuffixList;
private:
SuffixList suffixes_;
friend class ASLSolver;
void HandleOutput(fmt::CStringRef output) {
std::fputs(output.c_str(), stdout);
}
void HandleError(fmt::CStringRef message) {
std::fputs(message.c_str(), stderr);
std::fputc('\n', stderr);
}
// The default implementation of Interrupter does nothing.
bool Stop() const { return false; }
void SetHandler(InterruptHandler, void *) {}
// Returns the option with specified name.
SolverOption *GetOption(const char *name) const {
SolverOption *opt = FindOption(name);
if (!opt)
throw OptionError(fmt::format("Unknown option \"{}\"", name));
return opt;
}
// Parses an option string.
void ParseOptionString(const char *s, unsigned flags);
// Handler should be a class derived from Solver that will receive
// notifications about parsed options.
template <typename Handler, typename T, typename AccessorT = T>
class ConcreteOption : public TypedSolverOption<T> {
private:
typedef AccessorT (Handler::*Get)(const SolverOption &) const;
typedef void (Handler::*Set)(
const SolverOption &, typename internal::OptionHelper<AccessorT>::Arg);
Handler &handler_;
Get get_;
Set set_;
public:
ConcreteOption(const char *name, const char *description,
Solver *s, Get get, Set set, ValueArrayRef values = ValueArrayRef())
: TypedSolverOption<T>(name, description, values),
handler_(static_cast<Handler&>(*s)), get_(get), set_(set) {}
void GetValue(T &value) const { value = (handler_.*get_)(*this); }
void SetValue(typename internal::OptionHelper<T>::Arg value) {
(handler_.*set_)(*this,
internal::OptionHelper<AccessorT>::CastArg(value));
}
};
public:
template <typename Handler, typename T,
typename Info, typename InfoArg = Info, typename AccessorT = T>
class ConcreteOptionWithInfo : public TypedSolverOption<T> {
private:
typedef AccessorT (Handler::*Get)(const SolverOption &, InfoArg) const;
typedef void (Handler::*Set)(
const SolverOption &,
typename internal::OptionHelper<AccessorT>::Arg, InfoArg);
Handler &handler_;
Get get_;
Set set_;
Info info_;
public:
ConcreteOptionWithInfo(const char *name, const char *description, Solver *s,
Get get, Set set, InfoArg info, ValueArrayRef values = ValueArrayRef())
: TypedSolverOption<T>(name, description, values),
handler_(static_cast<Handler&>(*s)), get_(get), set_(set), info_(info) {}
ConcreteOptionWithInfo(const char *name, const char *description, Handler *s,
Get get, Set set, InfoArg info, ValueArrayRef values = ValueArrayRef())
: TypedSolverOption<T>(name, description, values),
handler_(*s), get_(get), set_(set), info_(info) {}
void GetValue(T &value) const { value = (handler_.*get_)(*this, info_); }
void SetValue(typename internal::OptionHelper<T>::Arg value) {
(handler_.*set_)(*this, internal::OptionHelper<AccessorT>::CastArg(value),
info_);
}
};
int GetObjNo(const SolverOption &) const { return std::abs(objno_); }
void SetObjNo(const SolverOption &opt, int value) {
if (value < 0)
throw InvalidOptionValue(opt, value);
objno_ = value;
}
#ifdef MP_USE_UNIQUE_PTR
typedef std::unique_ptr<SolverOption> OptionPtr;
#else
typedef std::auto_ptr<SolverOption> OptionPtr;
static OptionPtr move(OptionPtr p) { return p; }
#endif
struct DoubleFormatter {
double value;
int precision;
friend void format(fmt::BasicFormatter<char> &f,
const char *, DoubleFormatter df) {
f.writer().write("{:.{}}", df.value, df.precision);
}
};
// Solver flags
enum {
// Multiple solutions support.
// Makes Solver register "countsolutions" and "solutionstub" options
// and write every solution passed to HandleFeastibleSolution to a file
// solutionstub & i & ".sol" where i is a solution number.
MULTIPLE_SOL = 1,
// Multiple objectives support.
MULTIPLE_OBJ = 2
};
protected:
// Constructs a Solver object.
// date: The solver date in YYYYMMDD format.
// flags: Bitwise OR of zero or more of the following values
// MULTIPLE_SOL
// MULTIPLE_OBJ
Solver(fmt::CStringRef name, fmt::CStringRef long_name, long date, int flags);
void set_long_name(fmt::StringRef name) { long_name_ = name.to_string(); }
void add_to_long_name(fmt::StringRef name) { long_name_ += name.to_string(); }
void set_version(fmt::StringRef version) { version_ = version.to_string(); }
void add_to_version(fmt::StringRef version) { version_ += version.to_string(); }
// Sets the flags for Problem::Read.
void set_read_flags(unsigned flags) { read_flags_ = flags; }
// Sets a text to be displayed before option descriptions.
void set_option_header(const char *header) { option_header_ = header; }
// Add more text to be displayed before option descriptions.
void add_to_option_header(const char *header_more) { option_header_ += header_more; }
void AddOption(OptionPtr opt) {
// First insert the option, then release a pointer to it. Doing the other
// way around may lead to a memory leak if insertion throws an exception.
options_.insert(opt.get());
opt.release();
}
// Adds an integer option.
// The option stores pointers to the name and the description so make
// sure that these strings have sufficient lifetimes (normally these are
// string literals).
// The arguments get and set should be pointers to member functions in the
// solver class. They are used to get and set an option value respectively.
template <typename Handler, typename Int>
void AddIntOption(const char *name, const char *description,
Int (Handler::*get)(const SolverOption &) const,
void (Handler::*set)(const SolverOption &, Int)) {
AddOption(OptionPtr(new ConcreteOption<Handler, fmt::LongLong, Int>(
name, description, this, get, set)));
}
// Adds an integer option with additional information.
// The option stores pointers to the name and the description so make
// sure that these strings have sufficient lifetimes (normally these are
// string literals).
// The arguments get and set should be pointers to member functions in the
// solver class. They are used to get and set an option value respectively.
template <typename Handler, typename Info>
void AddIntOption(const char *name, const char *description,
int (Handler::*get)(const SolverOption &, const Info &) const,
void (Handler::*set)(const SolverOption &, int, const Info &),
const Info &info) {
AddOption(OptionPtr(new ConcreteOptionWithInfo<
Handler, fmt::LongLong, Info, const Info &, int>(
name, description, this, get, set, info)));
}
// The same as above but with Info argument passed by value.
template <typename Handler, typename Info>
void AddIntOption(const char *name, const char *description,
int (Handler::*get)(const SolverOption &, Info) const,
void (Handler::*set)(const SolverOption &, int, Info), Info info) {
AddOption(OptionPtr(new ConcreteOptionWithInfo<
Handler, fmt::LongLong, Info, Info, int>(
name, description, this, get, set, info)));
}
// Adds a double option.
// The option stores pointers to the name and the description so make
// sure that these strings have sufficient lifetimes (normally these are
// string literals).
// The arguments get and set should be pointers to member functions in the
// solver class. They are used to get and set an option value respectively.
template <typename Handler>
void AddDblOption(const char *name, const char *description,
double (Handler::*get)(const SolverOption &) const,
void (Handler::*set)(const SolverOption &, double)) {
AddOption(OptionPtr(new ConcreteOption<Handler, double>(
name, description, this, get, set)));
}
// Adds a double option with additional information.
// The option stores pointers to the name and the description so make
// sure that these strings have sufficient lifetimes (normally these are
// string literals).
// The arguments get and set should be pointers to member functions in the
// solver class. They are used to get and set an option value respectively.
template <typename Handler, typename Info>
void AddDblOption(const char *name, const char *description,
double (Handler::*get)(const SolverOption &, const Info &) const,
void (Handler::*set)(const SolverOption &, double, const Info &),
const Info &info) {
AddOption(OptionPtr(
new ConcreteOptionWithInfo<Handler, double, Info, const Info &>(
name, description, this, get, set, info)));
}
// The same as above but with Info argument passed by value.
template <typename Handler, typename Info>
void AddDblOption(const char *name, const char *description,
double (Handler::*get)(const SolverOption &, Info) const,
void (Handler::*set)(const SolverOption &, double, Info), Info info) {
AddOption(OptionPtr(new ConcreteOptionWithInfo<Handler, double, Info>(
name, description, this, get, set, info)));
}
// Adds a string option.
// The option stores pointers to the name and the description so make
// sure that these strings have sufficient lifetimes (normally these are
// string literals).
// The arguments get and set should be pointers to member functions in the
// solver class. They are used to get and set an option value respectively.
template <typename Handler>
void AddStrOption(const char *name, const char *description,
std::string (Handler::*get)(const SolverOption &) const,
void (Handler::*set)(const SolverOption &, fmt::StringRef),
ValueArrayRef values = ValueArrayRef()) {
AddOption(OptionPtr(new ConcreteOption<Handler, std::string>(
name, description, this, get, set, values)));
}
// Adds a string option with additional information.
// The option stores pointers to the name and the description so make
// sure that these strings have sufficient lifetimes (normally these are
// string literals).
// The arguments get and set should be pointers to member functions in the
// solver class. They are used to get and set an option value respectively.
template <typename Handler, typename Info>
void AddStrOption(const char *name, const char *description,
std::string (Handler::*get)(const SolverOption &, const Info &) const,
void (Handler::*set)(const SolverOption &, fmt::StringRef, const Info &),
const Info &info, ValueArrayRef values = ValueArrayRef()) {
AddOption(OptionPtr(
new ConcreteOptionWithInfo<Handler, std::string, Info, const Info &>(
name, description, this, get, set, info, values)));
}
// The same as above but with Info argument passed by value.
template <typename Handler, typename Info>
void AddStrOption(const char *name, const char *description,
std::string (Handler::*get)(const SolverOption &, Info) const,
void (Handler::*set)(const SolverOption &, fmt::StringRef, Info),
Info info, ValueArrayRef values = ValueArrayRef()) {
AddOption(OptionPtr(new ConcreteOptionWithInfo<Handler, std::string, Info>(
name, description, this, get, set, info, values)));
}
virtual void HandleUnknownOption(const char *name) {
ReportError("Unknown option \"{}\"", name);
}
// Adds a suffix.
void AddSuffix(const char *name, const char *table,
int kind, int nextra = 0) {
suffixes_.push_back(SuffixInfo(name, table, kind, nextra));
}
public:
virtual ~Solver();
// Returns the solver name.
const char *name() const { return name_.c_str(); }
// Returns the long solver name.
// This name is used in startup "banner".
const char *long_name() const { return long_name_.c_str(); }
// Returns the solver version.
const char *version() const { return version_.c_str(); }
// Returns the solver date in YYYYMMDD format.
long date() const { return date_; }
// Possible values for the wantsol option (can be combined with bitwise OR).
enum {
WRITE_SOL_FILE = 1, // write .sol file
PRINT_SOLUTION = 2, // print primal variables to stdout
PRINT_DUAL_SOLUTION = 4, // print dual variables to stdout
SUPPRESS_SOLVER_MSG = 8 // suppress solver message
};
// Returns the value of the wantsol option which specifies what solution
// information to write in a stand-alone invocation (no -AMPL on the
// command line).
int wantsol() const { return wantsol_; }
void set_wantsol(int value) { wantsol_ = value; }
// Returns true if -AMPL is specified.
bool ampl_flag() { return (bool_options_ & AMPL_FLAG) != 0; }
void set_ampl_flag(bool value = true) {
if (value)
bool_options_ |= AMPL_FLAG;
else
bool_options_ &= ~AMPL_FLAG;
}
// Returns the index of the objective to optimize starting from 1,
// 0 to not use objective, or -1 to use the first objective is there is one.
// It is used in SolverApp to select the objective, solvers should not
// use this option.
int objno() const { return objno_; }
// Returns true if the timing is enabled.
bool timing() const { return timing_; }
// Returns true if multiobjective optimization is enabled.
bool multiobj() const { return multiobj_; }
// Returns the error handler.
ErrorHandler *error_handler() { return error_handler_; }
// Sets the error handler.
void set_error_handler(ErrorHandler *eh) { error_handler_ = eh; }
// Returns the output handler.
OutputHandler *output_handler() { return output_handler_; }
// Sets the output handler.
void set_output_handler(OutputHandler *oh) { output_handler_ = oh; }
Interrupter *interrupter() { return interrupter_; }
void set_interrupter(Interrupter *interrupter) {
interrupter_ = interrupter ? interrupter : this;
}
const char *solution_stub() const { return solution_stub_.c_str(); }
bool need_multiple_solutions() const {
return count_solutions_ || !solution_stub_.empty();
}
// Returns the number of options.
int num_options() const { return static_cast<int>(options_.size()); }
// Finds an option and returns a pointer to it if found or null otherwise.
SolverOption *FindOption(const char *name) const;
// Option iterator.
class option_iterator :
public std::iterator<std::forward_iterator_tag, SolverOption> {
private:
OptionSet::const_iterator it_;
friend class Solver;
explicit option_iterator(OptionSet::const_iterator it) : it_(it) {}
public:
option_iterator() {}
const SolverOption &operator*() const { return **it_; }
const SolverOption *operator->() const { return *it_; }
option_iterator &operator++() {
++it_;
return *this;
}
option_iterator operator++(int ) {
option_iterator it(*this);
++it_;
return it;
}
bool operator==(option_iterator other) const { return it_ == other.it_; }
bool operator!=(option_iterator other) const { return it_ != other.it_; }
};
option_iterator option_begin() const {
return option_iterator(options_.begin());
}
option_iterator option_end() const {
return option_iterator(options_.end());
}
// Returns the option header.
const char *option_header() const { return option_header_.c_str(); }
// Flags for ParseOptions.
enum {
// Don't echo options during parsing.
NO_OPTION_ECHO = 1
};
// Parses solver options and returns true if there were no errors and
// false otherwise. It accepts a pointer to the problem because some
// options may depend on problem features.
virtual bool ParseOptions(
char **argv, unsigned flags = 0, const ASLProblem *p = 0);
// Returns the value of an integer option.
// Throws OptionError if there is no such option or it has a different type.
fmt::LongLong GetIntOption(const char *name) const {
return GetOption(name)->GetValue<fmt::LongLong>();
}
// Sets the value of an integer option.
// Throws OptionError if there is no such option or it has a different type.
void SetIntOption(const char *name, fmt::LongLong value) {
GetOption(name)->SetValue(value);
}
// Returns the value of a double option.
// Throws OptionError if there is no such option or it has a different type.
double GetDblOption(const char *name) const {
return GetOption(name)->GetValue<double>();
}
// Sets the value of a double option.
// Throws OptionError if there is no such option or it has a different type.
void SetDblOption(const char *name, double value) {
GetOption(name)->SetValue(value);
}
// Returns the value of a string option.
// Throws OptionError if there is no such option or it has a different type.
std::string GetStrOption(const char *name) const {
return GetOption(name)->GetValue<std::string>();
}
// Sets the value of a string option.
// Throws OptionError if there is no such option or it has a different type.
void SetStrOption(const char *name, fmt::StringRef value) {
GetOption(name)->SetValue(value);
}
const SuffixList &suffixes() const { return suffixes_; }
// Reports an error printing the formatted error message to stderr.
// Usage: ReportError("File not found: {}") << filename;
void ReportError(fmt::CStringRef format, const fmt::ArgList &args) {
has_errors_ = true;
fmt::MemoryWriter w;
w.write(format, args);
error_handler_->HandleError(w.c_str());
}
FMT_VARIADIC(void, ReportError, fmt::CStringRef)
// Formats a string and prints it to stdout or, if an output handler
// is registered, sends it to the output handler.
void Print(fmt::CStringRef format, const fmt::ArgList &args) {
fmt::MemoryWriter w;
w.write(format, args);
output_handler_->HandleOutput(w.c_str());
}
FMT_VARIADIC(void, Print, fmt::CStringRef)
// Prints version information.
bool ShowVersion();
// The default precision used in FormatObjValue if the objective_precision
// option is not specified or 0.
enum { DEFAULT_PRECISION = 15 };
// Returns a formatter that writes value using objective precision.
// Usage:
// Print("objective {}", FormatObjValue(obj_value));
DoubleFormatter FormatObjValue(double value);
};
template <typename ProblemBuilderT>
class SolverImpl : public Solver {
public:
typedef ProblemBuilderT ProblemBuilder;
typedef internal::NLProblemBuilder<ProblemBuilder> NLProblemBuilder;
SolverImpl(fmt::CStringRef name, fmt::CStringRef long_name = 0,
long date = 0, int flags = 0)
: Solver(name, long_name, date, flags) {}
};
// Adapts a solution for WriteSol.
template <typename ProblemBuilder>
class SolutionAdapter {
private:
int status_;
ProblemBuilder *builder_;
const char *message_;
mp::ArrayRef<int> options_;
mp::ArrayRef<double> values_;
mp::ArrayRef<double> dual_values_;
public:
SolutionAdapter(int status, ProblemBuilder *pb, const char *message,
mp::ArrayRef<int> options, mp::ArrayRef<double> values,
mp::ArrayRef<double> dual_values)
: status_(status), builder_(pb), message_(message), options_(options),
values_(values), dual_values_(dual_values) {}
int status() const { return status_; }
const char *message() const { return message_; }
int num_options() const { return static_cast<int>(options_.size()); }
int option(int index) const { return options_[index]; }
int num_values() const { return static_cast<int>(values_.size()); }
double value(int index) const { return values_[index]; }
int num_dual_values() const { return static_cast<int>(dual_values_.size()); }
double dual_value(int index) const { return dual_values_[index]; }
const typename ProblemBuilder::SuffixSet *suffixes(suf::Kind kind) const {
return builder_ ? &builder_->suffixes(kind) : 0;
}
};
class NullSolutionHandler : public SolutionHandler {
public:
void HandleFeasibleSolution(
fmt::CStringRef, const double *, const double *, double) {}
void HandleSolution(
int, fmt::CStringRef, const double *, const double *, double) {}
};
// The default .sol file writer.
class SolFileWriter {
public:
template <typename Solution>
void Write(fmt::CStringRef filename, const Solution &sol) {
WriteSolFile(filename, sol);
}
};
// A solution writer.
// Solver: optimization solver class
// Writer: .sol writer
template <typename Solver, typename Writer = SolFileWriter>
class SolutionWriter : private Writer, public SolutionHandler {