Poplar and PopLibs
OptionParsing.hpp
Go to the documentation of this file.
1// Copyright (c) 2018 Graphcore Ltd. All rights reserved.
10#ifndef poputil_OptionParsing_hpp
11#define poputil_OptionParsing_hpp
12
13#include <functional>
14#include <initializer_list>
15#include <limits>
16#include <map>
17#include <poplar/StringRef.hpp>
18#include <poplar/exceptions.hpp>
19#include <sstream>
20#include <string>
21
23namespace poplibs {
24
25namespace parse {
26template <typename T> T asInteger(const poplar::StringRef &str) {
27 std::string stdStr = str.cloneAsString();
28 std::istringstream iss(stdStr);
29 T result;
30 if (stdStr.find("0x") != std::string::npos) {
31 iss >> std::hex >> result;
32 } else {
33 iss >> std::dec >> result;
34 }
35 if (iss.fail() || !iss.eof()) {
36 throw poplar::invalid_option("Not a valid integer");
37 }
38 return result;
39}
40
41template <typename T> T asFloatingPoint(const poplar::StringRef &str) {
42 std::stringstream s(str);
43 T result;
44 s >> result;
45 if (s.fail()) {
46 throw poplar::invalid_option("Not a floating point number");
47 }
48 return result;
49}
50
51template <typename T>
52static inline std::string
53describeEnumValues(const std::map<std::string, T> &valueMap) {
54 std::stringstream s;
55 s << "[";
56 if (!valueMap.empty()) {
57 auto it = valueMap.begin();
58 s << "'" << it->first << "'";
59 while (++it != valueMap.end()) {
60 s << ", '" << it->first << "'";
61 }
62 }
63 s << "]";
64 return s.str();
65}
66
67template <typename T>
68T asEnum(const poplar::StringRef &value, const std::map<std::string, T> &map) {
69 auto it = map.find(value);
70 if (it == map.end()) {
71 throw poplar::invalid_option("Not one of the values: " +
72 describeEnumValues(map));
73 }
74
75 return it->second;
76}
77
78inline bool asBool(const poplar::StringRef &value) {
79 static const std::map<std::string, bool> enumMap = {{"true", true},
80 {"false", false}};
81 return asEnum<bool>(value, enumMap);
82}
83
84} // namespace parse
85
90 std::function<void(poplar::StringRef)> valueHandler;
91
92public:
93 template <typename T>
94 OptionHandler(T &&valueHandler)
95 : valueHandler(std::forward<T>(valueHandler)) {}
96 void parseValue(poplar::StringRef value) const { valueHandler(value); }
97
98 // Utility functions to help build a spec
99 template <typename T, typename ValueMapT = std::map<std::string, T>>
100 static inline OptionHandler createWithEnum(T &output, ValueMapT &&valueMap) {
101
102 // Allow forwarding of rvalues for valueMap but we always want
103 // to hold a copy for safety.
104 return OptionHandler{[map = std::forward<ValueMapT>(valueMap),
105 &output](poplar::StringRef value) {
106 output = parse::asEnum<T>(value, map);
107 }};
108 }
109
110 template <typename T>
111 static inline OptionHandler createWithInteger(T &output) {
112 return OptionHandler{[&output](poplar::StringRef value) {
113 output = parse::asInteger<int>(value);
114 }};
115 }
116
117 template <typename T> static inline OptionHandler createWithBool(T &output) {
118 static std::map<std::string, T> boolMap{{"true", static_cast<T>(true)},
119 {"false", static_cast<T>(false)}};
120 return createWithEnum(output, boolMap);
121 }
122
123 template <typename T>
124 static inline void ensureValueIsWithinBounds(T value, const T &lowerBound,
125 bool lowerBoundIsInclusive,
126 const T &upperBound,
127 bool upperBoundIsInclusive) {
128 if (value < lowerBound || (!lowerBoundIsInclusive && value == lowerBound)) {
130 "Value must be greater than " +
131 std::string(lowerBoundIsInclusive ? "or equal to " : "") +
132 std::to_string(lowerBound));
133 }
134
135 if (value > upperBound || (!upperBoundIsInclusive && value == upperBound)) {
137 "Value must be less than " +
138 std::string(upperBoundIsInclusive ? "or equal to " : "") +
139 std::to_string(upperBound));
140 }
141 }
142
143 template <typename T>
144 static inline OptionHandler
145 createWithDouble(T &output,
146 double lowerBound = std::numeric_limits<double>::min(),
147 bool lowerBoundIsInclusive = true,
148 double upperBound = std::numeric_limits<double>::max(),
149 bool upperBoundIsInclusive = true) {
150 return OptionHandler{
151 [&output, lowerBound, lowerBoundIsInclusive, upperBound,
152 upperBoundIsInclusive](poplar::StringRef value) {
153 double output_ = parse::asFloatingPoint<double>(value);
154 ensureValueIsWithinBounds(output_, lowerBound, lowerBoundIsInclusive,
155 upperBound, upperBoundIsInclusive);
156 output = output_;
157 },
158 };
159 }
160
161 static inline OptionHandler createWithString(std::string &output) {
162 return OptionHandler{
163 [&output](poplar::StringRef value) { output = value; }};
164 }
165
166 template <typename T>
167 static inline OptionHandler createWithList(std::vector<T> &output) {
168 return OptionHandler{[&output](poplar::StringRef values) {
169 std::istringstream iss(values);
170 for (std::string element; std::getline(iss, element, ',');) {
171 T value;
172 std::istringstream elementStream(element);
173 if (element.find("0x") != std::string::npos) {
174 elementStream >> std::hex >> value;
175 } else {
176 elementStream >> std::dec >> value;
177 }
178 if (elementStream.fail() || !elementStream.eof()) {
179 throw poplar::invalid_option("Not a comma-separated list of "
180 "integers");
181 }
182 output.emplace_back(value);
183 }
184 }};
185 }
186};
187
191 using value_type = std::pair<const std::string, OptionHandler>;
192 using map_type = std::map<const std::string, OptionHandler>;
193 using initializer_list_t = std::initializer_list<value_type>;
194 map_type handlers;
195
196public:
197 OptionSpec(initializer_list_t &&handlers) : handlers(std::move(handlers)) {}
198
199 // Validate and handle options based on the spec
200 void parse(poplar::StringRef option, poplar::StringRef value,
201 bool ignoreUnknown = false) const {
202 auto it = handlers.find(option);
203 if (it == handlers.end()) {
204 if (ignoreUnknown) {
205 return;
206 } else {
207 std::stringstream s;
208 s << "Unrecognised option '" << option << "'";
209 throw poplar::invalid_option(s.str());
210 }
211 }
212 try {
213 const auto &handler = it->second;
214 handler.parseValue(value);
215 } catch (const poplar::invalid_option &e) {
216 std::stringstream s;
217 s << "Invalid value '" << value << "'"
218 << " for option '" << option << "': " << e.what();
219 throw poplar::invalid_option(s.str());
220 }
221 }
222};
223
224template <typename T>
225inline OptionHandler createOptionalDoubleHandler(
226 std::optional<T> &output,
227 double lowerBound = std::numeric_limits<double>::min(),
228 bool lowerBoundIsInclusive = true,
229 double upperBound = std::numeric_limits<double>::max(),
230 bool upperBoundIsInclusive = true) {
231 return OptionHandler{[&output, lowerBound, lowerBoundIsInclusive, upperBound,
232 upperBoundIsInclusive](poplar::StringRef value) {
233 T output_ = parse::asFloatingPoint<double>(value);
234 OptionHandler::ensureValueIsWithinBounds(output_, lowerBound,
235 lowerBoundIsInclusive, upperBound,
236 upperBoundIsInclusive);
237 output = output_;
238 }};
239}
240
241template <typename T, typename ValueMapT = std::map<std::string, T>>
242inline OptionHandler createOptionalEnumHandler(std::optional<T> &output,
243 ValueMapT &&valueMap) {
244 return OptionHandler{[&output, map = std::forward<ValueMapT>(valueMap)](
245 poplar::StringRef value) {
246 if (value == "none") {
247 output = std::nullopt;
248 return;
249 }
250 output = parse::asEnum(value, map);
251 }};
252}
253
254} // end namespace poplibs
255
256#endif // poputil_OptionParsing_hpp
Represents the various options types.
Definition: OptionParsing.hpp:89
Represents a set of options and their values.
Definition: OptionParsing.hpp:190
half2 max(half2 src0, half2 src1)
Targets the f16v2max instruction.
Definition: ipu_intrinsics:333
half2 min(half2 src0, half2 src1)
Targets the f16v2min instruction.
Definition: ipu_intrinsics:384
PopLibs classes and functions.
Definition: OptionParsing.hpp:23
poplar::Tensor map(poplar::Graph &graph, const expr::Expr &expr, const std::vector< poplar::Tensor > &ts, poplar::program::Sequence &prog, const poplar::DebugContext &debugContext={}, const poplar::OptionFlags &options={})
Map an expression across tensors.
This exception is thrown when an unrecognised or invalid option is passed to a Poplar API.
Definition: exceptions.hpp:292