1 /* 2 .nl file support. 3 4 Copyright (C) 2013 AMPL Optimization Inc 5 6 Permission to use, copy, modify, and distribute this software and its 7 documentation for any purpose and without fee is hereby granted, 8 provided that the above copyright notice appear in all copies and that 9 both that the copyright notice and this permission notice and warranty 10 disclaimer appear in supporting documentation. 11 12 The author and AMPL Optimization Inc disclaim all warranties with 13 regard to this software, including all implied warranties of 14 merchantability and fitness. In no event shall the author be liable 15 for any special, indirect or consequential damages or any damages 16 whatsoever resulting from loss of use, data or profits, whether in an 17 action of contract, negligence or other tortious action, arising out 18 of or in connection with the use or performance of this software. 19 20 Author: Victor Zverovich 21 */ 22 23 #include "mp/nl-reader.h" 24 25 namespace { 26 enum { 27 USE_VBTOL_OPTION = 1, 28 READ_VBTOL = 3 29 }; 30 } 31 32 void mp::ReadError::init(fmt::CStringRef filename, int line, int column, 33 fmt::CStringRef format_str, fmt::ArgList args) { 34 filename_ = filename.c_str(); 35 line_ = line; 36 column_ = column; 37 fmt::MemoryWriter w; 38 w.write("{}:{}:{}: ", filename, line, column); 39 w.write(format_str, args); 40 SetMessage(w.c_str()); 41 } 42 43 mp::arith::Kind mp::arith::GetKind() { 44 // Unlike ASL, we don't try detecting floating-point arithmetic at 45 // configuration time because it doesn't work with cross-compiling. 46 if (sizeof(double) != 2 * sizeof(uint32_t)) 47 return UNKNOWN; 48 union { 49 double d; 50 uint32_t i[2]; 51 } u; 52 u.i[0] = u.i[1] = 0; 53 u.d = 1e13; 54 if (u.i[1] == 0x42A2309C && u.i[0] == 0xE5400000) 55 return IEEE_LITTLE_ENDIAN; 56 if (u.i[0] == 0x42A2309C && u.i[1] == 0xE5400000) 57 return IEEE_BIG_ENDIAN; 58 return UNKNOWN; 59 } 60 61 fmt::Writer &mp::operator<<(fmt::Writer &w, const NLHeader &h) { 62 w << (h.format == NLHeader::TEXT ? 'g' : 'b') << h.num_ampl_options; 63 for (int i = 0; i < h.num_ampl_options; ++i) 64 w << ' ' << h.ampl_options[i]; 65 if (h.ampl_options[USE_VBTOL_OPTION] == READ_VBTOL) 66 w << ' ' << h.ampl_vbtol; 67 w << '\n'; 68 w.write(" {} {} {} {} {} {}\n", 69 h.num_vars, h.num_algebraic_cons, h.num_objs, 70 h.num_ranges, h.num_eqns, h.num_logical_cons); 71 w.write(" {} {} {} {} {} {}\n", 72 h.num_nl_cons, h.num_nl_objs, 73 h.num_compl_conds - h.num_nl_compl_conds, 74 h.num_nl_compl_conds, h.num_compl_dbl_ineqs, 75 h.num_compl_vars_with_nz_lb); 76 w.write(" {} {}\n", h.num_nl_net_cons, h.num_linear_net_cons); 77 w.write(" {} {} {}\n", 78 h.num_nl_vars_in_cons, h.num_nl_vars_in_objs, h.num_nl_vars_in_both); 79 w.write(" {} {} {} {}\n", 80 h.num_linear_net_vars, h.num_funcs, 81 h.format == NLHeader::TEXT ? 0 : h.arith_kind, h.flags); 82 w.write(" {} {} {} {} {}\n", 83 h.num_linear_binary_vars, h.num_linear_integer_vars, 84 h.num_nl_integer_vars_in_both, h.num_nl_integer_vars_in_cons, 85 h.num_nl_integer_vars_in_objs); 86 w.write(" {} {}\n", h.num_con_nonzeros, h.num_obj_nonzeros); 87 w.write(" {} {}\n", h.max_con_name_len, h.max_var_name_len); 88 w.write(" {} {} {} {} {}\n", 89 h.num_common_exprs_in_both, h.num_common_exprs_in_cons, 90 h.num_common_exprs_in_objs, h.num_common_exprs_in_single_cons, 91 h.num_common_exprs_in_single_objs); 92 return w; 93 } 94 95 mp::internal::ReaderBase::ReaderBase(NLStringRef data, fmt::CStringRef name) 96 : ptr_(data.c_str()), start_(ptr_), end_(ptr_ + data.size()), 97 token_(ptr_), name_(name.c_str()) {} 98 99 template <typename Locale> 100 mp::internal::TextReader<Locale>::TextReader( 101 NLStringRef data, fmt::CStringRef name) 102 : ReaderBase(data, name), line_start_(ptr_), line_(1) {} 103 104 template <typename Locale> 105 void mp::internal::TextReader<Locale>::DoReportError( 106 const char *loc, fmt::CStringRef format_str, const fmt::ArgList &args) { 107 int line = line_; 108 const char *line_start = line_start_; 109 if (loc < line_start) { 110 --line; 111 // Find the beginning of the previous line. 112 line_start = loc; 113 if (*line_start == '\n') 114 --line_start; 115 while (*line_start != '\n' && line_start != start_) 116 --line_start; 117 if (*line_start == '\n') 118 ++line_start; 119 } 120 int column = static_cast<int>(loc - line_start + 1); 121 throw ReadError(name_, line, column, format_str, args); 122 } 123 124 template <typename Locale> 125 bool mp::internal::TextReader<Locale>::ReadOptionalDouble(double &value) { 126 SkipSpace(); 127 if (*ptr_ == '\n') 128 return false; 129 char *end = 0; 130 value = std::strtod(ptr_, &end); 131 bool has_value = ptr_ != end; 132 ptr_ = end; 133 return has_value; 134 } 135 136 template <typename Locale> 137 fmt::StringRef mp::internal::TextReader<Locale>::ReadString() { 138 int length = ReadUInt(); 139 if (*ptr_ != ':') 140 DoReportError(ptr_, "expected ':'"); 141 ++ptr_; 142 const char *start = ptr_; 143 for (int i = 0; i < length; ++i, ++ptr_) { 144 char c = *ptr_; 145 if (c == '\n') { 146 line_start_ = ptr_ + 1; 147 ++line_; 148 } else if (!c && ptr_ == end_) { 149 DoReportError(ptr_, "unexpected end of file in string"); 150 } 151 } 152 if (*ptr_ != '\n') 153 DoReportError(ptr_, "expected newline"); 154 ++line_; 155 line_start_ = ++ptr_; 156 return fmt::StringRef(length != 0 ? start : 0, length); 157 } 158 159 template <typename Locale> 160 fmt::StringRef mp::internal::TextReader<Locale>::ReadName() { 161 SkipSpace(); 162 const char *start = ptr_; 163 if (*ptr_ == '\n' || !*ptr_) 164 ReportError("expected name"); 165 do ++ptr_; 166 while (!std::isspace(*ptr_) && *ptr_); 167 return fmt::StringRef(start, ptr_ - start); 168 } 169 170 template <typename Locale> 171 void mp::internal::TextReader<Locale>::ReadHeader(NLHeader &header) { 172 // Read the format (text or binary). 173 switch (ReadChar()) { 174 case 'g': 175 break; 176 case 'b': 177 header.format = NLHeader::BINARY; 178 break; 179 default: 180 ReportError("expected format specifier"); 181 break; 182 } 183 184 // Read options. 185 ReadOptionalUInt(header.num_ampl_options); 186 if (header.num_ampl_options > MAX_AMPL_OPTIONS) 187 ReportError("too many options"); 188 for (int i = 0; i < header.num_ampl_options; ++i) { 189 if (!ReadOptionalInt(header.ampl_options[i])) 190 break; 191 } 192 if (header.ampl_options[USE_VBTOL_OPTION] == READ_VBTOL) 193 ReadOptionalDouble(header.ampl_vbtol); 194 ReadTillEndOfLine(); 195 196 // Read problem dimensions. 197 header.num_vars = ReadUInt(); 198 header.num_algebraic_cons = ReadUInt(); 199 header.num_objs = ReadUInt(); 200 header.num_eqns = -1; 201 if (ReadOptionalUInt(header.num_ranges) && 202 ReadOptionalUInt(header.num_eqns)) { 203 ReadOptionalUInt(header.num_logical_cons); 204 } 205 ReadTillEndOfLine(); 206 207 // Read the nonlinear and complementarity information. 208 header.num_nl_cons = ReadUInt(); 209 header.num_nl_objs = ReadUInt(); 210 bool all_compl = 211 ReadOptionalUInt(header.num_compl_conds) && 212 ReadOptionalUInt(header.num_nl_compl_conds) && 213 ReadOptionalUInt(header.num_compl_dbl_ineqs) && 214 ReadOptionalUInt(header.num_compl_vars_with_nz_lb); 215 header.num_compl_conds += header.num_nl_compl_conds; 216 if (header.num_compl_conds > 0 && !all_compl) 217 header.num_compl_dbl_ineqs = -1; 218 ReadTillEndOfLine(); 219 220 // Read the information about network constraints. 221 header.num_nl_net_cons = ReadUInt(); 222 header.num_linear_net_cons = ReadUInt(); 223 ReadTillEndOfLine(); 224 225 // Read the information about nonlinear variables. 226 header.num_nl_vars_in_cons = ReadUInt(); 227 header.num_nl_vars_in_objs = ReadUInt(); 228 header.num_nl_vars_in_both = -1; 229 ReadOptionalUInt(header.num_nl_vars_in_both); 230 ReadTillEndOfLine(); 231 232 header.num_linear_net_vars = ReadUInt(); 233 header.num_funcs = ReadUInt(); 234 int arith_kind = 0; 235 if (ReadOptionalUInt(arith_kind)) { 236 if (arith_kind > arith::LAST) 237 ReportError("unknown floating-point arithmetic kind"); 238 header.arith_kind = static_cast<arith::Kind>(arith_kind); 239 ReadOptionalUInt(header.flags); 240 } 241 ReadTillEndOfLine(); 242 243 // Read the information about discrete variables. 244 header.num_linear_binary_vars = ReadUInt(); 245 header.num_linear_integer_vars = ReadUInt(); 246 if (header.num_nl_vars_in_both >= 0) { // ampl versions >= 19930630 247 header.num_nl_integer_vars_in_both = ReadUInt(); 248 header.num_nl_integer_vars_in_cons = ReadUInt(); 249 header.num_nl_integer_vars_in_objs = ReadUInt(); 250 } 251 ReadTillEndOfLine(); 252 253 // Read the information about nonzeros. 254 header.num_con_nonzeros = ReadUInt<std::size_t>(); 255 header.num_obj_nonzeros = ReadUInt<std::size_t>(); 256 ReadTillEndOfLine(); 257 258 // Read the information about names. 259 header.max_con_name_len = ReadUInt(); 260 header.max_var_name_len = ReadUInt(); 261 ReadTillEndOfLine(); 262 263 // Read the information about common expressions checking for overflow 264 // as the variable indices go from 0 to num_vars + num_common_exprs. 265 int max_vars = header.num_vars; 266 header.num_common_exprs_in_both = ReadUInt(max_vars); 267 header.num_common_exprs_in_cons = ReadUInt(max_vars); 268 header.num_common_exprs_in_objs = ReadUInt(max_vars); 269 header.num_common_exprs_in_single_cons = ReadUInt(max_vars); 270 header.num_common_exprs_in_single_objs = ReadUInt(max_vars); 271 ReadTillEndOfLine(); 272 } 273 274 void mp::internal::BinaryReaderBase::ReportError( 275 fmt::CStringRef format_str, const fmt::ArgList &args) { 276 fmt::MemoryWriter w; 277 std::size_t offset = token_ - start_; 278 w.write("{}:offset {}: ", name_, offset); 279 w.write(format_str, args); 280 throw BinaryReadError(name_, offset, w.c_str()); 281 } 282 283 template <typename File> 284 void mp::internal::NLFileReader<File>::Open(fmt::CStringRef filename) { 285 file_ = File(filename, fmt::File::RDONLY); 286 size_ = ConvertFileToMmapSize(file_.size(), filename); 287 // Round size up to a multiple of page_size. The remainded of the last 288 // partial page is zero-filled both on POSIX and Windows so the resulting 289 // memory buffer is zero terminated. 290 std::size_t page_size = fmt::getpagesize(); 291 std::size_t remainder = size_ % page_size; 292 rounded_size_ = remainder != 0 ? (size_ + page_size - remainder) : size_; 293 } 294 295 template <typename File> 296 void mp::internal::NLFileReader<File>::Read( 297 fmt::internal::MemoryBuffer<char, 1> &array) { 298 array.resize(size_ + 1); 299 std::size_t offset = 0; 300 while (offset < size_) 301 offset += file_.read(&array[offset], size_ - offset); 302 array[size_] = 0; 303 } 304 305 template class mp::internal::TextReader<>; 306 template class mp::internal::NLFileReader<>; 307