From 73cc5c7b7a298a1e00c4ac3ef875908e4444b488 Mon Sep 17 00:00:00 2001 From: Gleb Belov Date: Thu, 29 Feb 2024 16:58:12 +1100 Subject: [PATCH] MiniJSONWriter #232 --- CMakeLists.txt | 4 +- include/mp/flat/constr_general.h | 15 +++ include/mp/flat/constr_keeper.h | 43 +++++++- include/mp/flat/converter.h | 18 +--- include/mp/flat/converter_model.h | 48 ++++++++- include/mp/flat/expr_algebraic.h | 9 ++ include/mp/util-json-write.h | 167 ++++++++++++++++++++++++++++++ include/mp/util-json-write.hpp | 134 ++++++++++++++++++++++++ include/mp/utils-file.h | 21 +++- include/mp/utils-string.h | 29 +++++- include/mp/valcvt.h | 22 ++-- src/std_constr.cc | 56 ++++++---- src/utils_file.cc | 5 + 13 files changed, 504 insertions(+), 67 deletions(-) create mode 100644 include/mp/util-json-write.h create mode 100644 include/mp/util-json-write.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c24e7921..06b54fdcf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -353,7 +353,9 @@ add_prefix(MP_HEADERS include/mp/ rstparser.h safeint.h sol.h solver.h solver-opt.h solver-base.h solver-io.h solver-app-base.h solver-app.h suffix.h - utils-file.h utils-hash.h utils-hash-stream.h + utils-file.h + util-json-write.h util-json-write.hpp + utils-hash.h utils-hash-stream.h utils-string.h utils-math.h) add_prefix(MP_FLAT_HEADERS include/mp/flat/ diff --git a/include/mp/flat/constr_general.h b/include/mp/flat/constr_general.h index 13d3a2c34..4c5a86ff4 100644 --- a/include/mp/flat/constr_general.h +++ b/include/mp/flat/constr_general.h @@ -221,6 +221,21 @@ using SOS1Constraint = SOS_1or2_Constraint<1>; using SOS2Constraint = SOS_1or2_Constraint<2>; +/// Write a SOS1, SOS2 constraint +template +inline void WriteJSON(Writer& wrt, const SOS_1or2_Constraint& sos) { + wrt.write("{} ", '{'); + wrt.write("\"sos_type\": {}, ", sos.get_sos_type()); + wrt.write("\"vars\": "); + WriteJSONVec(wrt, sos.get_vars()); + wrt.write(", \"weights\": "); + WriteJSONVec(wrt, sos.get_weights()); + auto bnds = sos.get_sum_of_vars_range(); + wrt.write(", \"sum_of_vars_range\": [{}, {}]", + bnds.lb_, bnds.ub_); + wrt.write(" {}", '}'); +} + //////////////////////////////////////////////////////////////////////// /// Complementarity constraint. /// \a Expr complements a variable. diff --git a/include/mp/flat/constr_keeper.h b/include/mp/flat/constr_keeper.h index 402eea4dc..36f197360 100644 --- a/include/mp/flat/constr_keeper.h +++ b/include/mp/flat/constr_keeper.h @@ -10,6 +10,8 @@ #include "mp/format.h" #include "mp/env.h" #include "mp/utils-math.h" +#include "mp/utils-file.h" +#include "mp/util-json-write.hpp" #include "mp/flat/model_api_base.h" #include "mp/flat/constr_hash.h" @@ -513,6 +515,13 @@ class BasicConstraintKeeper { /// Compute violations virtual void ComputeViolations(SolCheck& ) = 0; + /// Set logger + void SetLogger(BasicLogger* lg) { exporter_=lg; } + /// Get logger, if provided and open and ok. + BasicLogger* GetLogger() const { + return exporter_ && exporter_->IsOpen() + ? exporter_ : nullptr; + } protected: int& GetAccLevRef() { return acceptance_level_; } @@ -524,6 +533,7 @@ class BasicConstraintKeeper { const char* const solver_opt_nm_; mutable std::string type_name_short_; int acceptance_level_ {-1}; + BasicLogger* exporter_{}; }; const char* @@ -668,6 +678,7 @@ class ConstraintKeeper final int AddConstraint(int d, Args&&... args) { cons_.emplace_back( d, std::move(args)... ); + ExportConstraint(cons_.size()-1, cons_.back()); return cons_.size()-1; } @@ -860,6 +871,20 @@ class ConstraintKeeper final ++n_bridged_or_unused_; } +protected: + /// Export last added constraint + void ExportConstraint(int i_con, const Container& cnt) { + if (GetLogger()) { + fmt::MemoryWriter wrt; + MiniJSONWriter jw(wrt); + jw["con_type"] = cnt.con_.GetTypeName(); + jw["index"] = i_con; + jw["depth"] = cnt.GetDepth(); +// wrt.write("\"data\": "); +// WriteJSON(wrt, cnt.con_); + GetLogger()->Append(wrt); + } + } public: /// Mark cons[\a i] as reformulated. @@ -1120,13 +1145,12 @@ bool operator==(std::reference_wrapper< //////////////////////////////////////////////////////////////////////////////////// /// Manage ConstraintKeepers for different constraint types class ConstraintManager { - std::multimap con_keepers_; - - public: /// Add a new CKeeper with given conversion priority (smaller = sooner) - void AddConstraintKeeper(BasicConstraintKeeper& ck, double priority) - { con_keepers_.insert( { priority, ck } ); } + void AddConstraintKeeper(BasicConstraintKeeper& ck, double priority) { + con_keepers_.insert( { priority, ck } ); + ck.SetLogger(&*graph_exporter_app_); + } /// This should be called after adding all constraint keepers void ConsiderAcceptanceOptions( @@ -1177,6 +1201,15 @@ class ConstraintManager { for (const auto& ck: con_keepers_) ck.second.ComputeViolations(chk); } + + /// Retrieve file logger + BasicFileAppender& GetFileAppender() { return *graph_exporter_app_; } + +private: + std::multimap con_keepers_; + /// Conversion graph exporter file appender + std::unique_ptr + graph_exporter_app_{MakeFileAppender()}; }; } // namespace mp diff --git a/include/mp/flat/converter.h b/include/mp/flat/converter.h index a1b3bc13d..3f249c4dd 100644 --- a/include/mp/flat/converter.h +++ b/include/mp/flat/converter.h @@ -22,7 +22,6 @@ #include "mp/flat/redef/std/range_con.h" #include "mp/flat/redef/conic/cones.h" #include "mp/flat/redef/conic/qcones2qc.h" -#include "mp/utils-file.h" #include "mp/ampls-ccallbacks.h" namespace mp { @@ -235,9 +234,9 @@ class FlatConverter : void OpenGraphExporter() { if (graph_export_file().size()) { - if (!graph_exporter_app_->Open(graph_export_file().c_str(), true)) + if (!GetModel().GetFileAppender().Open( + graph_export_file().c_str(), true)) MP_RAISE("Failed to open the graph export file."); - value_presolver_.SetExport(true); } } @@ -256,7 +255,7 @@ class FlatConverter : void CloseGraphExporter() { value_presolver_.FinishExportingLinkEntries(); - graph_exporter_app_->Close(); + GetModel().GetFileAppender().Close(); } //////////////////////// WHOLE-MODEL PREPROCESSING ///////////////////////// @@ -646,6 +645,7 @@ class FlatConverter : } /// Add several variables once. + /// @note this is only to be called once for the original vars. /// @return value node range for them pre::NodeRange AddVars(const typename BaseFlatModel::VarBndVec& lbs, const typename BaseFlatModel::VarBndVec& ubs, @@ -1215,19 +1215,11 @@ class FlatConverter : /// We store ModelApi in the converter for speed. /// Should be before constraints ModelAPIType modelapi_; - /// Conversion graph exporter file appender - std::unique_ptr graph_exporter_app_{MakeFileAppender()}; - /// Conversion graph exporter functor - pre::ValuePresolver::ExporterFn graph_exporter_fn_{ - [this](const char* s){ - graph_exporter_app_->Append(s); - } - }; /// ValuePresolver: should be init before constraint keepers /// and links pre::ValuePresolver value_presolver_ { - GetModel(), GetEnv(), graph_exporter_fn_, + GetModel(), GetEnv(), (BasicLogger&)GetModel().GetFileAppender(), [this]( ArrayRef x, const pre::ValueMapDbl& y, diff --git a/include/mp/flat/converter_model.h b/include/mp/flat/converter_model.h index 01c1a00c4..ae37f8b03 100644 --- a/include/mp/flat/converter_model.h +++ b/include/mp/flat/converter_model.h @@ -35,6 +35,8 @@ class FlatModel using VarTypeVec = std::vector; using VarNameVec = std::vector; + using ConstraintManager::GetFileAppender; + ///////////////////////////// VARIABLES //////////////////////////////// /// Add variable, return its index Var AddVar__basic(double lb=MinusInf(), double ub=Inf(), @@ -43,6 +45,7 @@ class FlatModel var_lb_.push_back(lb); var_ub_.push_back(ub); var_type_.push_back(type); + ExportVars(var_type_.size()-1, {lb}, {ub}, {type}); return var_type_.size()-1; } @@ -55,9 +58,32 @@ class FlatModel var_ub_.insert(var_ub_.end(), ubs.begin(), ubs.end()); var_type_.insert(var_type_.end(), types.begin(), types.end()); assert(check_vars()); + assert(!num_vars_orig_); num_vars_orig_ = var_lb_.size(); + ExportVars(var_type_.size()-1, lbs, ubs, types); + } + +protected: + /// Export new variables + template , + class TypeVec=std::array > + void ExportVars(int i_start, const BndVec& lbs, const BndVec& ubs, + const TypeVec types) { + for (int i=0; + GetFileAppender().IsOpen() && i<(int)lbs.size(); ++i) { + fmt::MemoryWriter wrt; + wrt.write("{} ", '{'); + wrt.write("\"var_index\": {}, ", i+i_start); + wrt.write("\"bounds\": [{}, {}], ", lbs[i], ubs[i]); + wrt.write("\"type\": {}, ", (int)types[i]); + wrt.write("\"is_from_nl\": {}, ", (int)is_var_original(i)); + wrt.write(" {}\n", '}'); // with EOL + GetFileAppender().Append(wrt); + } } +public: + /// Set var names vector void AddVarNames(const std::vector& names) { var_names_storage_ = names; } @@ -68,6 +94,7 @@ class FlatModel ? var_names_storage_[i].c_str() : nullptr; } + /// N vars int num_vars() const { assert(check_vars()); return (int)var_lb_.size(); } @@ -198,9 +225,26 @@ class FlatModel return false; } /// Add an objective - void AddObjective(QuadraticObjective&& obj) - { get_objectives().push_back(std::move(obj)); } + void AddObjective(QuadraticObjective&& obj) { + get_objectives().push_back(std::move(obj)); + ExportObjective(num_objs()-1, get_objectives().back()); + } +protected: + void ExportObjective(int i_obj, const QuadraticObjective& obj) { + if (GetFileAppender().IsOpen()) { + fmt::MemoryWriter wrt; + wrt.write("{} ", '{'); + wrt.write("\"obj_index\": {}, ", i_obj); + wrt.write("\"sense\": {}, ", obj.obj_sense()); + wrt.write("\"qp_terms\": "); + WriteJSON(wrt, obj.GetQPTerms()); + wrt.write("\"lin_terms\": "); + WriteJSON(wrt, obj.GetLinTerms()); + wrt.write(" {}\n", '}'); // with EOL + GetFileAppender().Append(wrt); + } + } ///////////////////////////// FLAT CONSTRAINTS //////////////////////////// protected: diff --git a/include/mp/flat/expr_algebraic.h b/include/mp/flat/expr_algebraic.h index 466f0d9c9..4ce007984 100644 --- a/include/mp/flat/expr_algebraic.h +++ b/include/mp/flat/expr_algebraic.h @@ -111,6 +111,15 @@ class AlgebraicExpression : public Body { double constant_term_ = 0.0; }; +/// Very general template to write +/// a JSON representation of anything +template +void WriteJSON(Writer& , const Obj& ); + +/// Write JSON string of a vector +template +void WriteJSONVec(Writer& wrt, const Vec& vec); + } // namespace mp #endif // EXPR_ALGEBRAIC_H diff --git a/include/mp/util-json-write.h b/include/mp/util-json-write.h new file mode 100644 index 000000000..a4f385b4a --- /dev/null +++ b/include/mp/util-json-write.h @@ -0,0 +1,167 @@ +/* + JSON write utils. + + Copyright (C) 2024 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: Gleb Belov + */ + +#ifndef UTILJSONWRITE_H +#define UTILJSONWRITE_H + +#include +#include +#include + +namespace mp { + +/// A lightweight JSON writer. +/// +/// Does not require an intermediate representation of the data. +/// Similar to https://github.com/giacomodrago/minijson_writer +/// but the string buffer writer is a template parameter +/// with method write() conformant to fmt::format. +/// +/// @tparam Formatter: consumes provided data (e.g., numbers/strings) +/// to produce a JSON string/file. +template +class MiniJSONWriter { +public: + /// Construct with provided Formatter \a wrt. + MiniJSONWriter(Formatter& wrt) : wrt_(wrt) { } + + /// Declare Node. + /// A node is a JSON tree node (value, array, dict.) + using Node = MiniJSONWriter; + + /// Prefix++: make/ensure *this an array, + /// add and return a new element. + /// Useful when writing complex elements + /// or non-supported types manually. + Node operator++(); + + /// operator[]: make/ensure *this a dictionary, + /// add and return a new element at \a key. + /// Allows the syntax `node[key] = val;`, + /// but also writing a complex subtree manually. + Node operator[](const char* key); + + /// operator<<: make/ensure *this an array + /// and write \a val a new element. + /// + /// @param val: scalar or container. + /// For non-supported types, define global method + /// Serialize(MiniJSONWriter&, const YourType& ). + /// + /// @return *this. + template + Node& operator<<(const Value& val) + { EnsureArray(); Write(val); return *this; } + + /// operator=: write \a val as a whole. + /// + /// @param val: scalar or container. + /// For non-supported types, define global method + /// Serialize(MiniJSONWrt& , const YourType& ). + /// + /// @note Can be called only once on a single node. + template + void operator=(const Value& val) + { EnsureUnset(); Write(val); Close(); } + + /// Write a sequence between two iterators + template + void WriteSequence(It b, It e) { + for ( ; b!=e; ++b) + (*this) << (*b); + } + + /// Close node. + /// Call this before writing to any non-child nodes, + /// if not exiting the scope which calls the destructor. + void Close(); + + /// Destructor. + /// Closes node by RAII. + ~MiniJSONWriter() { Close(); } + + /// Move construct + MiniJSONWriter(MiniJSONWriter&& other) + : wrt_(other.wrt_), + kind_(other.kind_), n_written_(other.n_written_) + { other.kind_ = Kind::Closed; } + +protected: + /// Construct a child. + MiniJSONWriter(MiniJSONWriter& parent) + : wrt_(parent.wrt_) { } + + /// Generic value write. + template + void Write(const Value& val) + { EnsureCanWrite(); DoWrite(val); } + + /// Make sure this node has not been written into. + void EnsureUnset(); + /// Make sure this is a scalar node. + void EnsureScalar(); + /// Make sure this is an array node. + void EnsureArray(); + /// Make sure this is a dictionary node. + void EnsureDictionary(); + /// Make sure we can write another value/element. + void EnsureCanWrite(); + + /// Kind enum + enum class Kind { + Unset, + Scalar, + Array, + Dict, + Closed + }; + + /// Insert element separator if needed + void InsertElementSeparator(); + + void DoWrite(const char* s) { DoWriteScalar(s); } + void DoWrite(const std::string& s) { DoWriteScalar(s); } + + template , + int> = 0> // C++17 + void DoWrite(Arithmetic v) { DoWriteScalar(v); } + + template ()))> > + void DoWrite(const C& c) { WriteSequence(c.begin(), c.end()); } + + template + void DoWriteScalar(const Value& val) + { EnsureScalar(); wrt_.write("{}", val); } + +private: + Formatter& wrt_; + Kind kind_{Kind::Unset}; + int n_written_{}; +}; + +} // namespace mp + +#endif // UTILJSONWRITE_H diff --git a/include/mp/util-json-write.hpp b/include/mp/util-json-write.hpp new file mode 100644 index 000000000..00f8c4698 --- /dev/null +++ b/include/mp/util-json-write.hpp @@ -0,0 +1,134 @@ +/* + JSON write utils. + + Copyright (C) 2024 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: Gleb Belov + */ + +#ifndef UTILJSONWRITE_HPP +#define UTILJSONWRITE_HPP + +#include + +#include "mp/util-json-write.h" + +namespace mp { + +template +typename MiniJSONWriter::Node +MiniJSONWriter::operator++() { + EnsureArray(); + InsertElementSeparator(); + return Node{*this}; +} + +template +typename MiniJSONWriter::Node +MiniJSONWriter::operator[](const char* key) { + EnsureDictionary(); + InsertElementSeparator(); + wrt_.write("{}: ", key); + return Node{*this}; +} + +template +void MiniJSONWriter::EnsureUnset() { + assert(Kind::Unset == kind_); +} + +template +void MiniJSONWriter::EnsureScalar() { + EnsureUnset(); + kind_ = Kind::Scalar; +} + +template +void MiniJSONWriter::EnsureArray() { + if (Kind::Unset == kind_) { + kind_ = Kind::Array; + wrt_.write("["); + } else { + assert(Kind::Array == kind_); // cannot change + } +} + +template +void MiniJSONWriter::EnsureDictionary() { + if (Kind::Unset == kind_) { + kind_ = Kind::Dict; + wrt_.write("{}", '{'); + } else { + assert(Kind::Dict == kind_); // cannot change + } +} + +template +void MiniJSONWriter::EnsureCanWrite() { + switch (kind_) { + case Kind::Unset: + assert(false && "unset MiniJSONWriter node kind for writing"); + break; + case Kind::Scalar: + assert(0==n_written_); + break; + case Kind::Array: + case Kind::Dict: + break; + case Kind::Closed: // done already + assert(false && "writing into closed MiniJSONWriter node"); + break; + default: + assert(false && "unknown MiniJSONWriter node kind"); + break; + } +} + +template +void MiniJSONWriter::InsertElementSeparator() { + if (n_written_) + wrt_.write(", "); +} + +template +void MiniJSONWriter::Close() { + switch (kind_) { + case Kind::Unset: + wrt_.write("[]"); // empty array. Is this ok to default to? + break; + case Kind::Scalar: + assert(1==n_written_); + break; + case Kind::Array: + wrt_.write("]"); + break; + case Kind::Dict: + wrt_.write("{}", '}'); + break; + case Kind::Closed: // done already + break; + default: + assert(false && "unknown MiniJSONWriter node kind"); + break; + } + kind_ = Kind::Closed; +} + + +} // namespace mp + +#endif // UTILJSONWRITE_HPP diff --git a/include/mp/utils-file.h b/include/mp/utils-file.h index e681fb6a9..d3228356d 100644 --- a/include/mp/utils-file.h +++ b/include/mp/utils-file.h @@ -26,13 +26,16 @@ #include #include +#include "mp/utils-string.h" + namespace mp { /// Class appending strings to file with given name -class BasicFileAppender { +class BasicFileAppender + : public BasicLogger { public: - /// Destruct - virtual ~BasicFileAppender() { } + /// Is the log active and ok? + bool IsOpen() const override = 0; /// Open file virtual bool Open(const std::string& fln, bool fErase) = 0; @@ -40,8 +43,16 @@ class BasicFileAppender { /// Close file virtual void Close() = 0; - /// Append string - virtual bool Append(const char* ) = 0; + /// append string. + /// + /// @return whether all ok. + template + bool Append (const Str& s) { return Append(s.c_str()); } + + /// Append string. + /// + /// @return whether all ok. + bool Append(const char* ) override = 0; }; /// FileAppender maker diff --git a/include/mp/utils-string.h b/include/mp/utils-string.h index 471effc5b..4677a9f75 100644 --- a/include/mp/utils-string.h +++ b/include/mp/utils-string.h @@ -1,7 +1,7 @@ /* - String utils + String utils. - Copyright (C) 2021 AMPL Optimization Inc + Copyright (C) 2024 AMPL Optimization Inc. Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, @@ -13,7 +13,7 @@ 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 + 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. @@ -28,13 +28,36 @@ namespace mp { +/// Split string std::vector split_string(const char* ); +/// Split string template inline std::vector split_string(const Str& str) { return split_string(str.c_str()); } +/// Class logging strings to file or screen or memory etc. +class BasicLogger { +public: + /// Destruct + virtual ~BasicLogger() { } + + /// Is the log active and ok? + virtual bool IsOpen() const = 0; + + /// append string. + /// + /// @return whether all ok. + template + bool Append (const Str& s) { return Append(s.c_str()); } + + /// Append string. + /// + /// @return whether all ok. + virtual bool Append(const char* ) = 0; +}; + } // namespace mp #endif // MP_UTILS_STRING_H_ diff --git a/include/mp/valcvt.h b/include/mp/valcvt.h index c72b43d1f..43d1da6e0 100644 --- a/include/mp/valcvt.h +++ b/include/mp/valcvt.h @@ -9,6 +9,8 @@ #include #include +#include "mp/utils-string.h" + #include "valcvt-node.h" #include "valcvt-link.h" #include "mp/flat/converter_model_base.h" @@ -58,11 +60,8 @@ class LinkRangeList : private std::deque { /// between the original model and the presolved one. class ValuePresolverImpl : public BasicValuePresolver { public: - /// Exporter functor type - using ExporterFn = std::function< void (const char*) >; - /// Constructor - ValuePresolverImpl(Env& env, ExporterFn bts={}) : + ValuePresolverImpl(Env& env, BasicLogger& bts) : BasicValuePresolver(env), bts_(bts) { } /// Source nodes of the conversion graph, const @@ -86,11 +85,8 @@ class ValuePresolverImpl : public BasicValuePresolver { } } - /// Switch exporting on/off. - void SetExport(bool onoff) { f_export_=onoff; } - /// Want Export? - bool GetExport() const { return f_export_; } + bool GetExport() const { return bts_.IsOpen(); } /// Finish exporting entries, if exporting=ON. /// This should be called after model conversions are finished, @@ -202,8 +198,7 @@ class ValuePresolverImpl : public BasicValuePresolver { wrt.write(" ]"); // end dest nodes wrt.write(" {}\n", '}'); // with EOL /// Export record - assert(bts_); - bts_(wrt.c_str()); + bts_.Append(wrt); } } @@ -237,11 +232,8 @@ class ValuePresolverImpl : public BasicValuePresolver { /// The link ranges LinkRangeList brl_; - /// Export on/off - bool f_export_ { false }; - /// Exporter functor - ExporterFn const bts_{}; + BasicLogger& bts_; /// 1-after-last exported entry int i_exported_=0; @@ -266,7 +258,7 @@ using SolCheckerType = std::function; class ValuePresolver : public ValuePresolverImpl { public: ValuePresolver(BasicFlatModel& m, Env& env, - ExporterFn bts={}, SolCheckerType sc={}) + BasicLogger& bts, SolCheckerType sc={}) : ValuePresolverImpl(env, bts), model_(m), solchk_(sc) { } /// Override PresolveSolution(). diff --git a/src/std_constr.cc b/src/std_constr.cc index 81d9a5b13..fbf79f987 100644 --- a/src/std_constr.cc +++ b/src/std_constr.cc @@ -1,7 +1,9 @@ #include -#include "mp/flat/expr_quadratic.h" +#include "mp/format.h" +#include "mp/util-json-write.hpp" +#include "mp/flat/expr_quadratic.h" #include "mp/flat/model_info.hpp" namespace mp { @@ -44,29 +46,37 @@ void QuadTerms::sort_terms() { } } -//void print(std::ostream& os) const { -// os << lb_ << " <= "; -// for (int i=0; i +void WriteJSON(fmt::MemoryWriter& wrt, const LinTerms& qt) { + wrt.write("{} ", '{'); + wrt.write("\"coefs\": "); + WriteJSONVec(wrt, qt.coefs()); + wrt.write(", \"vars\": "); + WriteJSONVec(wrt, qt.vars()); + wrt.write(" {}", '}'); +} } // namespace mp diff --git a/src/utils_file.cc b/src/utils_file.cc index fee42c4a0..755d2105c 100644 --- a/src/utils_file.cc +++ b/src/utils_file.cc @@ -32,6 +32,11 @@ namespace mp { /// Class appending strings to file with given name class FileAppender__fstream : public BasicFileAppender { public: + /// Is the log active and ok? + bool IsOpen() const override { + return fs_.is_open() ? fs_.good() : false; + } + /// Open file bool Open(const std::string& fln, bool fErase=false) override { if (fErase) {