20#include <unordered_map>
24namespace uni20::detail
28inline constexpr std::uint64_t qnum_out_of_line_mask = std::uint64_t{1} << 63;
31inline constexpr std::uint64_t qnum_inline_code_max = qnum_out_of_line_mask - 1;
34struct SymmetryFactorSpec
37 SymmetryFactorBase
const* factor =
nullptr;
51 explicit SymmetryImpl(std::vector<SymmetryFactorSpec> factors) : factors_(
std::move(factors))
53 for (std::size_t i = 0; i < factors_.size(); ++i)
55 auto const [it, inserted] = index_by_name_.emplace(factors_[i].name, i);
58 throw std::invalid_argument(
"duplicate symmetry component name: " + it->first);
61 canonical_string_ = build_canonical_string(factors_);
66 auto factors() const ->
std::
span<SymmetryFactorSpec const> {
return factors_; }
70 auto factor_count() const ->
std::
size_t {
return factors_.size(); }
75 auto find_factor(std::string_view name)
const -> std::optional<std::size_t>
77 auto const it = index_by_name_.find(std::string{name});
78 if (it == index_by_name_.end())
87 auto to_string() const ->
std::
string const& {
return canonical_string_; }
92 static auto intern(std::vector<SymmetryFactorSpec> factors) -> SymmetryImpl
const*
94 auto const key = build_canonical_string(factors);
96 auto& mutex = intern_mutex();
97 std::lock_guard<std::mutex> lock(mutex);
99 auto& map = intern_map();
100 if (
auto const it = map.find(key); it != map.end())
105 auto* impl =
new SymmetryImpl(std::move(factors));
106 map.emplace(impl->to_string(), impl);
113 static auto parse(std::string_view spec) -> SymmetryImpl
const*
115 register_builtin_symmetry_factors();
117 auto trimmed =
trim(spec);
120 throw std::invalid_argument(
"symmetry specification must not be empty");
123 std::vector<SymmetryFactorSpec> factors;
124 std::size_t cursor = 0;
125 while (cursor < trimmed.size())
127 auto const next = trimmed.find(
',', cursor);
129 trim(trimmed.substr(cursor, next == std::string_view::npos ? std::string_view::npos : next - cursor));
132 throw std::invalid_argument(
"symmetry specification contains an empty factor");
135 auto const colon = token.find(
':');
136 if (colon == std::string_view::npos)
138 throw std::invalid_argument(
"symmetry factor is missing ':' separator: " + std::string{token});
141 auto const name =
trim(token.substr(0, colon));
142 auto const kind =
trim(token.substr(colon + 1));
145 throw std::invalid_argument(
"symmetry factor has an empty name");
149 throw std::invalid_argument(
"symmetry factor has an empty type");
152 factors.push_back({std::string{name}, find_symmetry_factor(kind)});
154 if (next == std::string_view::npos)
161 return intern(std::move(factors));
167 auto pack(std::span<std::uint64_t const> factor_codes)
const -> std::uint64_t
169 if (factor_codes.size() != factors_.size())
171 throw std::invalid_argument(
"wrong number of factor codes for symmetry packing");
173 if (factor_codes.empty())
178 std::uint64_t combined = factor_codes.front();
179 if (combined > qnum_inline_code_max)
181 throw std::overflow_error(
"quantum number code requires the reserved out-of-line representation");
183 for (std::size_t i = 1; i < factor_codes.size(); ++i)
185 combined = cantor_pair(combined, factor_codes[i]);
193 auto unpack(std::uint64_t code)
const -> std::vector<std::uint64_t>
195 if (code > qnum_inline_code_max)
197 throw std::invalid_argument(
"cannot unpack a tagged out-of-line quantum number as an inline code");
199 if (factors_.empty())
203 if (factors_.size() == 1)
208 std::vector<std::uint64_t> codes(factors_.size());
210 for (std::size_t i = factors_.size(); i-- > 1;)
212 auto const [
left,
right] = cantor_unpair(current);
224 static auto trim(std::string_view
text) -> std::string_view
226 while (!
text.
empty() && std::isspace(
static_cast<unsigned char>(
text.front())) != 0)
228 text.remove_prefix(1);
230 while (!
text.
empty() && std::isspace(
static_cast<unsigned char>(
text.back())) != 0)
232 text.remove_suffix(1);
240 static auto build_canonical_string(std::span<SymmetryFactorSpec const> factors) -> std::string
242 std::ostringstream
out;
243 for (std::size_t i = 0; i < factors.size(); ++i)
249 out << factors[i].name <<
':' << factors[i].factor->type_name();
256 static auto intern_map() -> std::unordered_map<std::string, SymmetryImpl const*>&
260 static auto*
const map =
new std::unordered_map<std::string, SymmetryImpl const*>;
266 static auto intern_mutex() -> std::mutex&
268 static std::mutex mutex;
276 static auto triangular_leq(std::uint64_t
value, std::uint64_t limit) ->
bool
290 static auto triangular(std::uint64_t
value) -> std::uint64_t
301 static auto cantor_pair(std::uint64_t left, std::uint64_t right) -> std::uint64_t
303 if (left > qnum_inline_code_max || right > qnum_inline_code_max || left > qnum_inline_code_max - right)
305 throw std::overflow_error(
"quantum number code requires the reserved out-of-line representation");
309 if (!triangular_leq(
sum, qnum_inline_code_max - right))
311 throw std::overflow_error(
"quantum number code requires the reserved out-of-line representation");
319 static auto cantor_unpair(std::uint64_t code) -> std::pair<std::uint64_t, std::uint64_t>
321 std::uint64_t lo = 0;
322 std::uint64_t hi = code;
325 auto const mid = lo + (hi - lo + 1) / 2;
326 if (triangular_leq(mid, code))
337 auto const triangle = triangular(
sum);
338 auto const right = code - triangle;
343 std::vector<SymmetryFactorSpec> factors_;
344 std::unordered_map<std::string, std::size_t> index_by_name_;
345 std::string canonical_string_;
bool empty() const noexcept
Return whether this document has no spans.
Definition presentation.cpp:1182
locale_t value
Definition data_table.cpp:49
presentation::styled_text text
Definition display.cpp:141
std::string trim(const std::string &s)
Definition terminal.cpp:162
parse_result parse(CLI::App &app, int argc, char const *const *argv, parse_policy policy)
Parse argc/argv, preserving CLI11's argument boundaries and help precedence.
Definition cli.cpp:116
auto pack(Span const &span, bool read=true)
Definition binary80_common.hpp:23
void unpack(Tensor const &source, Span &target)
Definition packed_matrix.hpp:33
void sum(BackendSelector selector, async::Async< OutputTensor > &output, async::Async< InputTensor > const &input)
Schedule a full sum into an explicit async scalar tensor.
Definition linalg/async/reductions.hpp:309
auto to_string(basic_half_int< T > value) -> std::string
Format a half-integer as an integer or decimal .5.
Definition half_int.hpp:477
std::size_t span
Definition presentation.cpp:2017
Defines U1, the value-level quantum number type for one U(1) symmetry factor.