Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
qnum.hpp
Go to the documentation of this file.
1
9#pragma once
10
12
13#include <algorithm>
14#include <cstddef>
15#include <cstdint>
16#include <functional>
17#include <initializer_list>
18#include <stdexcept>
19#include <string>
20#include <string_view>
21#include <utility>
22#include <vector>
23
24namespace uni20
25{
26
29class QNum {
30 public:
32 QNum() = default;
33
37 explicit QNum(Symmetry sym, std::uint64_t code) : sym_(sym.impl()), code_(code)
38 {
39 if (code_ > detail::qnum_inline_code_max)
40 {
41 throw std::overflow_error("QNum code uses the reserved out-of-line tag bit");
42 }
43 }
44
47 auto valid() const -> bool { return sym_ != nullptr; }
48
51 auto symmetry() const -> Symmetry
52 {
53 this->ensure_valid();
54 return Symmetry{sym_};
55 }
56
59 auto raw_code() const -> std::uint64_t
60 {
61 this->ensure_valid();
62 return code_;
63 }
64
68 auto operator==(QNum const& other) const -> bool = default;
69
73 static auto identity(Symmetry sym) -> QNum { return QNum(sym, 0); }
74
75 private:
76 friend auto dual(QNum const&) -> QNum;
77 friend auto is_identity(QNum const&) -> bool;
78 friend auto qdim(QNum const&) -> double;
79 friend auto degree(QNum const&) -> int;
80 friend auto to_string(QNum const&) -> std::string;
81 friend auto make_qnum(Symmetry, std::initializer_list<std::pair<std::string_view, U1>>) -> QNum;
82 friend auto coerce(QNum const&, Symmetry) -> QNum;
83 friend auto u1_component(QNum const&, std::string_view) -> U1;
84 friend auto operator+(QNum const&, QNum const&) -> QNum;
85
87 void ensure_valid() const
88 {
89 if (!this->valid())
90 {
91 throw std::logic_error("QNum is not initialized");
92 }
93 }
94
95 detail::SymmetryImpl const* sym_ = nullptr;
97 std::uint64_t code_ = 0;
98};
99
102class QNumList {
103 public:
106 explicit QNumList(Symmetry sym) : sym_(sym) {}
107
111 QNumList(Symmetry sym, std::initializer_list<QNum> values) : sym_(sym), values_(values)
112 {
113 for (QNum const& q : values_)
114 {
115 this->verify_symmetry(q);
116 }
117 }
118
121 auto symmetry() const -> Symmetry { return sym_; }
122
125 auto size() const -> std::size_t { return values_.size(); }
126
129 auto empty() const -> bool { return values_.empty(); }
130
134 {
135 this->verify_symmetry(q);
136 values_.push_back(q);
137 }
138
140 void clear() { values_.clear(); }
141
145 auto contains(QNum q) const -> bool
146 {
147 this->verify_symmetry(q);
148 return std::find(values_.begin(), values_.end(), q) != values_.end();
149 }
150
152 void sort()
153 {
154 std::sort(values_.begin(), values_.end(),
155 [](QNum const& lhs, QNum const& rhs) { return lhs.raw_code() < rhs.raw_code(); });
156 }
157
160 {
161 this->sort();
162 values_.erase(std::unique(values_.begin(), values_.end()), values_.end());
163 }
164
168 auto operator[](std::size_t index) const -> QNum const& { return values_[index]; }
169
172 auto begin() const { return values_.begin(); }
173
176 auto end() const { return values_.end(); }
177
181 auto operator==(QNumList const& other) const -> bool = default;
182
183 private:
186 void verify_symmetry(QNum const& q) const
187 {
188 if (q.symmetry() != sym_)
189 {
190 throw std::invalid_argument("QNumList element has the wrong symmetry");
191 }
192 }
193
194 Symmetry sym_;
195 std::vector<QNum> values_;
196};
197
202inline auto make_qnum(Symmetry sym, std::initializer_list<std::pair<std::string_view, U1>> values) -> QNum
203{
204 auto const* impl = sym.impl();
205 std::vector<std::uint64_t> codes(impl->factor_count(), 0);
206 std::vector<bool> seen(impl->factor_count(), false);
207
208 for (auto const& [name, value] : values)
209 {
210 auto const index = impl->find_factor(name);
211 if (!index.has_value())
212 {
213 throw std::invalid_argument("unknown symmetry component name: " + std::string{name});
214 }
215 if (seen[*index])
216 {
217 throw std::invalid_argument("duplicate symmetry component assignment: " + std::string{name});
218 }
219 seen[*index] = true;
220
221 auto const& factor = impl->factors()[*index];
222 auto const* typed_factor = dynamic_cast<detail::SymmetryFactor<U1> const*>(factor.factor);
223 if (typed_factor == nullptr)
224 {
225 throw std::invalid_argument("make_qnum(U1) used with a non-U(1) symmetry component");
226 }
227 codes[*index] = typed_factor->encode(value);
228 }
229
230 return QNum(sym, impl->pack(codes));
231}
232
236inline auto dual(QNum const& q) -> QNum
237{
238 auto const* impl = q.sym_;
239 auto codes = impl->unpack(q.code_);
240 for (std::size_t i = 0; i < codes.size(); ++i)
241 {
242 codes[i] = impl->factors()[i].factor->dual_code(codes[i]);
243 }
244 return QNum(q.symmetry(), impl->pack(codes));
245}
246
250inline auto is_identity(QNum const& q) -> bool { return q.raw_code() == 0; }
251
255inline auto qdim(QNum const& q) -> double
256{
257 auto const* impl = q.sym_;
258 auto const codes = impl->unpack(q.code_);
259 double result = 1.0;
260 for (std::size_t i = 0; i < codes.size(); ++i)
261 {
262 result *= impl->factors()[i].factor->qdim_code(codes[i]);
263 }
264 return result;
265}
266
270inline auto degree(QNum const& q) -> int
271{
272 auto const* impl = q.sym_;
273 auto const codes = impl->unpack(q.code_);
274 int result = 1;
275 for (std::size_t i = 0; i < codes.size(); ++i)
276 {
277 result *= impl->factors()[i].factor->degree_code(codes[i]);
278 }
279 return result;
280}
281
286inline auto u1_component(QNum const& q, std::string_view name) -> U1
287{
288 auto const* impl = q.sym_;
289 auto const index = impl->find_factor(name);
290 if (!index.has_value())
291 {
292 throw std::invalid_argument("unknown symmetry component name: " + std::string{name});
293 }
294
295 auto const& factor = impl->factors()[*index];
296 auto const* typed_factor = dynamic_cast<detail::SymmetryFactor<U1> const*>(factor.factor);
297 if (typed_factor == nullptr)
298 {
299 throw std::invalid_argument("requested U(1) component from a non-U(1) factor");
300 }
301
302 auto const codes = impl->unpack(q.code_);
303 return typed_factor->decode(codes[*index]);
304}
305
310inline auto coerce(QNum const& q, Symmetry target) -> QNum
311{
312 auto const source = q.symmetry();
313 if (source == target)
314 {
315 return q;
316 }
317
318 auto const* source_impl = source.impl();
319 auto const* target_impl = target.impl();
320 auto const source_codes = source_impl->unpack(q.code_);
321
322 std::vector<std::uint64_t> target_codes(target_impl->factor_count(), 0);
323 for (std::size_t i = 0; i < target_impl->factor_count(); ++i)
324 {
325 auto const& target_factor = target_impl->factors()[i];
326 auto const source_index = source_impl->find_factor(target_factor.name);
327 if (!source_index.has_value())
328 {
329 target_codes[i] = 0;
330 continue;
331 }
332
333 auto const& source_factor = source_impl->factors()[*source_index];
334 if (source_factor.factor != target_factor.factor)
335 {
336 throw std::invalid_argument("cannot coerce between differently typed symmetry components");
337 }
338 target_codes[i] = source_codes[*source_index];
339 }
340
341 for (std::size_t i = 0; i < source_impl->factor_count(); ++i)
342 {
343 auto const& source_factor = source_impl->factors()[i];
344 auto const target_index = target_impl->find_factor(source_factor.name);
345 if (!target_index.has_value() && source_codes[i] != 0)
346 {
347 throw std::invalid_argument("cannot drop a non-identity symmetry component during coercion");
348 }
349 }
350
351 return QNum(target, target_impl->pack(target_codes));
352}
353
358inline auto operator+(QNum const& lhs, QNum const& rhs) -> QNum
359{
360 if (lhs.symmetry() != rhs.symmetry())
361 {
362 throw std::invalid_argument("QNum operator+ requires identical symmetry contexts");
363 }
364
365 auto const* impl = lhs.sym_;
366 auto const lhs_codes = impl->unpack(lhs.code_);
367 auto const rhs_codes = impl->unpack(rhs.code_);
368 std::vector<std::uint64_t> out_codes(impl->factor_count(), 0);
369
370 for (std::size_t i = 0; i < out_codes.size(); ++i)
371 {
372 auto const* factor = impl->factors()[i].factor;
373 out_codes[i] = factor->add_codes(lhs_codes[i], rhs_codes[i]);
374 }
375
376 return QNum(lhs.symmetry(), impl->pack(out_codes));
377}
378
382inline auto to_string(QNum const& q) -> std::string
383{
384 auto const* impl = q.sym_;
385 auto const codes = impl->unpack(q.code_);
386 std::string result;
387 for (std::size_t i = 0; i < codes.size(); ++i)
388 {
389 if (!result.empty())
390 {
391 result += ',';
392 }
393 result += impl->factors()[i].name;
394 result += '=';
395 result += impl->factors()[i].factor->format_code(codes[i]);
396 }
397 return result;
398}
399
400} // namespace uni20
401
402namespace std
403{
404template <> struct hash<uni20::QNum>
405{
409 auto operator()(uni20::QNum const& q) const noexcept -> std::size_t
410 {
411 auto const sym_hash = std::hash<void const*>{}(q.valid() ? q.symmetry().impl() : nullptr);
412 auto const code_hash = std::hash<std::uint64_t>{}(q.valid() ? q.raw_code() : 0);
413 return sym_hash ^ (code_hash + 0x9e3779b97f4a7c15ULL + (sym_hash << 6) + (sym_hash >> 2));
414 }
415};
416} // namespace std
Explicit sparse tensor space represented as quantum numbers with one shared symmetry.
Definition qnum.hpp:102
auto size() const -> std::size_t
Return the number of stored quantum numbers.
Definition qnum.hpp:125
QNumList(Symmetry sym)
Construct an empty list tied to one symmetry.
Definition qnum.hpp:106
auto operator==(QNumList const &other) const -> bool=default
Compare two sparse irrep lists for exact equality.
void push_back(QNum q)
Append a quantum number with the matching symmetry.
Definition qnum.hpp:133
void normalize()
Sort entries and remove duplicates.
Definition qnum.hpp:159
auto empty() const -> bool
Return whether the list is empty.
Definition qnum.hpp:129
auto end() const
Return an iterator past the last element.
Definition qnum.hpp:176
auto contains(QNum q) const -> bool
Return whether the list already contains the given quantum number.
Definition qnum.hpp:145
QNumList(Symmetry sym, std::initializer_list< QNum > values)
Construct a list from initial values.
Definition qnum.hpp:111
auto operator[](std::size_t index) const -> QNum const &
Return indexed element access.
Definition qnum.hpp:168
void sort()
Sort entries by canonical packed order.
Definition qnum.hpp:152
void clear()
Remove all quantum numbers while keeping the list symmetry.
Definition qnum.hpp:140
auto symmetry() const -> Symmetry
Return the symmetry shared by all entries.
Definition qnum.hpp:121
auto begin() const
Return an iterator to the first element.
Definition qnum.hpp:172
One irrep label together with the symmetry context needed to interpret it.
Definition qnum.hpp:29
friend auto degree(QNum const &) -> int
Return the integral degree of a quantum number.
Definition qnum.hpp:270
friend auto qdim(QNum const &) -> double
Return the quantum dimension of a quantum number.
Definition qnum.hpp:255
QNum(Symmetry sym, std::uint64_t code)
Construct from a symmetry handle and a 63-bit inline packed code.
Definition qnum.hpp:37
friend auto to_string(QNum const &) -> std::string
Format a quantum number as comma-separated named components.
Definition qnum.hpp:382
friend auto coerce(QNum const &, Symmetry) -> QNum
Coerce a quantum number into a related symmetry by named components.
Definition qnum.hpp:310
auto symmetry() const -> Symmetry
Return the symmetry context of this quantum number.
Definition qnum.hpp:51
auto valid() const -> bool
Return whether the quantum number carries a valid symmetry context.
Definition qnum.hpp:47
QNum()=default
Construct an invalid quantum number.
friend auto make_qnum(Symmetry, std::initializer_list< std::pair< std::string_view, U1 > >) -> QNum
Construct a packed QNum from named U(1) component values.
Definition qnum.hpp:202
auto operator==(QNum const &other) const -> bool=default
Compare two quantum numbers for exact identity.
friend auto dual(QNum const &) -> QNum
Return the dual irrep of a quantum number.
Definition qnum.hpp:236
friend auto u1_component(QNum const &, std::string_view) -> U1
Read one named U(1) component from a quantum number.
Definition qnum.hpp:286
friend auto operator+(QNum const &, QNum const &) -> QNum
Add two quantum numbers when the fusion result is uniquely defined.
Definition qnum.hpp:358
friend auto is_identity(QNum const &) -> bool
Return whether a quantum number is the identity irrep.
Definition qnum.hpp:250
auto raw_code() const -> std::uint64_t
Return the inline packed irrep code.
Definition qnum.hpp:59
static auto identity(Symmetry sym) -> QNum
Return the identity irrep of a given symmetry.
Definition qnum.hpp:73
Public handle to a canonicalized direct-product symmetry specification.
Definition symmetry.hpp:24
Value-level quantum number type representing one U(1) irrep.
Definition u1.hpp:29
locale_t value
Definition data_table.cpp:49
Definition qnum.hpp:403
Definition async/async.hpp:26
auto u1_component(QNum const &q, std::string_view name) -> U1
Read one named U(1) component from a quantum number.
Definition qnum.hpp:286
auto degree(QNum const &q) -> int
Return the integral degree of a quantum number.
Definition qnum.hpp:270
auto qdim(QNum const &q) -> double
Return the quantum dimension of a quantum number.
Definition qnum.hpp:255
auto dual(SpaceType const &space) -> Dual< std::remove_cvref_t< SpaceType > >
Construct an explicitly dual basis without changing occurrence order.
Definition dual_space.hpp:244
auto coerce(QNum const &q, Symmetry target) -> QNum
Coerce a quantum number into a related symmetry by named components.
Definition qnum.hpp:310
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
auto is_identity(QNum const &q) -> bool
Return whether a quantum number is the identity irrep.
Definition qnum.hpp:250
auto make_qnum(Symmetry sym, std::initializer_list< std::pair< std::string_view, U1 > > values) -> QNum
Construct a packed QNum from named U(1) component values.
Definition qnum.hpp:202
Defines the lightweight public handle for canonicalized direct-product symmetries.