Uni20
Asynchronous tensor and runtime library.
Loading...
Searching...
No Matches
spin_half_heisenberg.hpp
Go to the documentation of this file.
1
7#pragma once
8
11#include <uni20/symmetry/u1.hpp>
13
14#include <cstddef>
15#include <stdexcept>
16#include <string>
17#include <string_view>
18#include <utility>
19#include <vector>
20
22{
23
25enum class SpinHalfState
26{
27 up,
28 down
29};
30
35{
44
48 static constexpr auto coordinate(SpinHalfState state) noexcept -> std::size_t
49 {
50 return state == SpinHalfState::up ? 0 : 1;
51 }
52
56 auto qnum(SpinHalfState state) const -> QNum const& { return state == SpinHalfState::up ? up : down; }
57};
58
62template <RealOrComplex Scalar, BlockTensorStorage Storage = PackedSparseBlockStorage<>>
64
66template <RealOrComplex Scalar>
68
73inline auto make_spin_half_u1_site(std::string_view charge_name = "Sz") -> SpinHalfU1Site
74{
75 if (charge_name.empty()) throw std::invalid_argument("spin-half U(1) charge name must not be empty");
76
77 Symmetry const symmetry(std::string(charge_name) + ":U(1)");
78 QNum const up = make_qnum(symmetry, {{charge_name, U1{half_int{0.5}}}});
79 QNum const down = make_qnum(symmetry, {{charge_name, U1{half_int{-0.5}}}});
80 return {.symmetry = symmetry, .space = LocalSpace(symmetry, {up, down}, "spin-1/2"), .up = up, .down = down};
81}
82
83namespace detail
84{
85
86inline auto opposite(SpinHalfState state) noexcept -> SpinHalfState
87{
89}
90
91inline auto mps_bond_label(std::size_t bond) -> std::string { return "neel-mps-bond-" + std::to_string(bond); }
92
93inline auto mpo_bond_label(std::size_t bond) -> std::string { return "heisenberg-mpo-bond-" + std::to_string(bond); }
94
95template <RealOrComplex Scalar> struct MpoCoefficient
96{
97 using site_type = typename SpinHalfU1Mpo<Scalar>::site_type;
98
99 typename site_type::key_type key;
100 Scalar value;
101};
102
103template <RealOrComplex Scalar>
104void append_mpo_coefficient(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t ket,
105 std::size_t right, std::size_t bra, Scalar value)
106{
107 if (value == Scalar{}) return;
108 coefficients.push_back(
109 {.key = typename MpoCoefficient<Scalar>::site_type::key_type{{left, ket, right, bra}}, .value = value});
110}
111
112template <RealOrComplex Scalar>
113void append_identity(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t right)
114{
115 append_mpo_coefficient(coefficients, left, 0, right, 0, Scalar{1});
116 append_mpo_coefficient(coefficients, left, 1, right, 1, Scalar{1});
117}
118
119template <RealOrComplex Scalar>
120void append_sz(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t right, Scalar scale)
121{
122 append_mpo_coefficient(coefficients, left, 0, right, 0, scale * Scalar{0.5});
123 append_mpo_coefficient(coefficients, left, 1, right, 1, scale * Scalar{-0.5});
124}
125
126template <RealOrComplex Scalar>
127void append_sp(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t right, Scalar scale)
128{
129 append_mpo_coefficient(coefficients, left, 1, right, 0, scale);
130}
131
132template <RealOrComplex Scalar>
133void append_sm(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t right, Scalar scale)
134{
135 append_mpo_coefficient(coefficients, left, 0, right, 1, scale);
136}
137
138template <RealOrComplex Scalar>
139auto make_mpo_site(SpinHalfU1Site const& local, LocalSpace const& left, LocalSpace const& right,
140 std::vector<MpoCoefficient<Scalar>> const& coefficients) -> typename SpinHalfU1Mpo<Scalar>::site_type
141{
142 using site_type = typename SpinHalfU1Mpo<Scalar>::site_type;
143 std::vector<typename site_type::key_type> keys;
144 keys.reserve(coefficients.size());
145 for (auto const& coefficient : coefficients)
146 keys.push_back(coefficient.key);
147
148 site_type result(local.symmetry, Domain{left, local.space}, Codomain{right, local.space}, std::move(keys));
149 for (auto const& coefficient : coefficients)
150 result.block(coefficient.key)[] = coefficient.value;
151 return result;
152}
153
154template <RealOrComplex Scalar>
155auto first_heisenberg_site(SpinHalfU1Site const& local, LocalSpace const& left, LocalSpace const& right,
156 Scalar longitudinal_field) -> typename SpinHalfU1Mpo<Scalar>::site_type
157{
158 std::vector<MpoCoefficient<Scalar>> coefficients;
159 append_identity(coefficients, 0, 0);
160 append_sp(coefficients, 0, 1, Scalar{1});
161 append_sm(coefficients, 0, 2, Scalar{1});
162 append_sz(coefficients, 0, 3, Scalar{1});
163 append_sz(coefficients, 0, 4, longitudinal_field);
164 return make_mpo_site(local, left, right, coefficients);
165}
166
167template <RealOrComplex Scalar>
168auto bulk_heisenberg_site(SpinHalfU1Site const& local, LocalSpace const& left, LocalSpace const& right, Scalar exchange,
169 Scalar longitudinal_field) -> typename SpinHalfU1Mpo<Scalar>::site_type
170{
171 std::vector<MpoCoefficient<Scalar>> coefficients;
172 append_identity(coefficients, 0, 0);
173 append_sp(coefficients, 0, 1, Scalar{1});
174 append_sm(coefficients, 0, 2, Scalar{1});
175 append_sz(coefficients, 0, 3, Scalar{1});
176 append_sz(coefficients, 0, 4, longitudinal_field);
177 append_sm(coefficients, 1, 4, exchange * Scalar{0.5});
178 append_sp(coefficients, 2, 4, exchange * Scalar{0.5});
179 append_sz(coefficients, 3, 4, exchange);
180 append_identity(coefficients, 4, 4);
181 return make_mpo_site(local, left, right, coefficients);
182}
183
184template <RealOrComplex Scalar>
185auto last_heisenberg_site(SpinHalfU1Site const& local, LocalSpace const& left, LocalSpace const& right, Scalar exchange,
186 Scalar longitudinal_field) -> typename SpinHalfU1Mpo<Scalar>::site_type
187{
188 std::vector<MpoCoefficient<Scalar>> coefficients;
189 append_sz(coefficients, 0, 0, longitudinal_field);
190 append_sm(coefficients, 1, 0, exchange * Scalar{0.5});
191 append_sp(coefficients, 2, 0, exchange * Scalar{0.5});
192 append_sz(coefficients, 3, 0, exchange);
193 append_identity(coefficients, 4, 0);
194 return make_mpo_site(local, left, right, coefficients);
195}
196
197template <RealOrComplex Scalar>
198auto single_heisenberg_site(SpinHalfU1Site const& local, LocalSpace const& left, LocalSpace const& right,
199 Scalar longitudinal_field) -> typename SpinHalfU1Mpo<Scalar>::site_type
200{
201 std::vector<MpoCoefficient<Scalar>> coefficients;
202 append_sz(coefficients, 0, 0, longitudinal_field);
203 return make_mpo_site(local, left, right, coefficients);
204}
205
206} // namespace detail
207
219template <RealOrComplex Scalar = double, BlockTensorStorage Storage = PackedSparseBlockStorage<>>
220 requires ImmediateLocalBlockStorageFor<Storage, Scalar, 3, 2>
221[[nodiscard]] auto make_neel_product_mps(std::size_t length, SpinHalfU1Site const& local,
223{
224 if (length == 0) throw std::invalid_argument("Neel product MPS requires at least one site");
225
226 std::vector<BlockSpace> bonds;
227 bonds.reserve(length + 1);
228 QNum cumulative = QNum::identity(local.symmetry);
229 bonds.emplace_back(local.symmetry, std::initializer_list<BlockSector>{{cumulative, 1}}, detail::mps_bond_label(0));
230 for (std::size_t site = 0; site < length; ++site)
231 {
232 SpinHalfState const state = site % 2 == 0 ? first : detail::opposite(first);
233 cumulative = cumulative + local.qnum(state);
234 bonds.emplace_back(local.symmetry, std::initializer_list<BlockSector>{{cumulative, 1}},
235 detail::mps_bond_label(site + 1));
236 }
237
238 using mps_type = SpinHalfU1Mps<Scalar, Storage>;
239 using site_type = typename mps_type::site_type;
240 std::vector<site_type> sites;
241 sites.reserve(length);
242 for (std::size_t site = 0; site < length; ++site)
243 {
244 SpinHalfState const state = site % 2 == 0 ? first : detail::opposite(first);
245 typename site_type::key_type const key{{0, SpinHalfU1Site::coordinate(state), 0}};
246 site_type value = [&]() -> site_type {
247 if constexpr (SparseBlockStorage<Storage>)
248 return site_type(local.symmetry, Domain{bonds[site], local.space}, Codomain{bonds[site + 1]}, {key});
249 else
250 return site_type(local.symmetry, Domain{bonds[site], local.space}, Codomain{bonds[site + 1]});
251 }();
252 value.block(key)[0, 0] = Scalar{1};
253 sites.push_back(std::move(value));
254 }
255 return mps_type(std::move(sites));
256}
257
272template <RealOrComplex Scalar = double>
273[[nodiscard]] auto make_spin_half_heisenberg_mpo(std::size_t length, SpinHalfU1Site const& local,
275 make_real_t<Scalar> longitudinal_field = make_real_t<Scalar>{})
276 -> SpinHalfU1Mpo<Scalar>
277{
278 if (length == 0) throw std::invalid_argument("spin-half Heisenberg MPO requires at least one site");
279
280 QNum const scalar = QNum::identity(local.symmetry);
281 std::string const& charge_name = local.symmetry.factors().front().name;
282 QNum const minus_one = make_qnum(local.symmetry, {{charge_name, U1{-1}}});
283 QNum const plus_one = make_qnum(local.symmetry, {{charge_name, U1{1}}});
284 std::vector<LocalSpace> auxiliaries;
285 auxiliaries.reserve(length + 1);
286 auxiliaries.emplace_back(local.symmetry, std::initializer_list<QNum>{scalar}, detail::mpo_bond_label(0));
287 for (std::size_t bond = 1; bond < length; ++bond)
288 {
289 auxiliaries.emplace_back(local.symmetry, std::initializer_list<QNum>{scalar, minus_one, plus_one, scalar, scalar},
290 detail::mpo_bond_label(bond));
291 }
292 auxiliaries.emplace_back(local.symmetry, std::initializer_list<QNum>{scalar}, detail::mpo_bond_label(length));
293
294 using mpo_type = SpinHalfU1Mpo<Scalar>;
295 using site_type = typename mpo_type::site_type;
296 std::vector<site_type> sites;
297 sites.reserve(length);
298 if (length == 1)
299 {
300 sites.push_back(
301 detail::single_heisenberg_site<Scalar>(local, auxiliaries[0], auxiliaries[1], Scalar{longitudinal_field}));
302 }
303 else
304 {
305 sites.push_back(
306 detail::first_heisenberg_site<Scalar>(local, auxiliaries[0], auxiliaries[1], Scalar{longitudinal_field}));
307 for (std::size_t site = 1; site + 1 < length; ++site)
308 {
309 sites.push_back(detail::bulk_heisenberg_site<Scalar>(local, auxiliaries[site], auxiliaries[site + 1],
310 Scalar{exchange}, Scalar{longitudinal_field}));
311 }
312 sites.push_back(detail::last_heisenberg_site<Scalar>(local, auxiliaries[length - 1], auxiliaries[length],
313 Scalar{exchange}, Scalar{longitudinal_field}));
314 }
315 return mpo_type(std::move(sites));
316}
317
318} // namespace uni20::models
Defines immutable block-structured tensor spaces and regularization helpers.
Ordered local-state space whose quantum numbers may repeat.
Definition local_space.hpp:28
One irrep label together with the symmetry context needed to interpret it.
Definition qnum.hpp:29
auto symmetry() const -> Symmetry
Return the symmetry context of this quantum number.
Definition qnum.hpp:51
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
auto factors() const -> std::span< detail::SymmetryFactorSpec const >
Return the named factor list.
Definition symmetry.hpp:56
Value-level quantum number type representing one U(1) irrep.
Definition u1.hpp:29
Homogeneous finite MPO owner with exact auxiliary connectivity.
Definition finite_chain.hpp:147
MpoSite< Scalar, Auxiliary, Physical, Auxiliary, Physical, Storage > site_type
Definition finite_chain.hpp:153
Homogeneous finite MPS owner with exact bond connectivity.
Definition finite_chain.hpp:32
locale_t value
Definition data_table.cpp:49
Owns validated finite MPS and MPO chains.
typename make_real< T >::type make_real_t
Alias for the underlying real-valued type of T.
Definition scalar_traits.hpp:168
Definition spin_half_heisenberg.hpp:22
SpinHalfState
Physical state selected at the first site of a spin-half product pattern.
Definition spin_half_heisenberg.hpp:26
auto make_spin_half_heisenberg_mpo(std::size_t length, SpinHalfU1Site const &local, make_real_t< Scalar > exchange=make_real_t< Scalar >{1}, make_real_t< Scalar > longitudinal_field=make_real_t< Scalar >{}) -> SpinHalfU1Mpo< Scalar >
Construct the open spin-half Heisenberg Hamiltonian as a finite MPO.
Definition spin_half_heisenberg.hpp:273
auto make_neel_product_mps(std::size_t length, SpinHalfU1Site const &local, SpinHalfState first=SpinHalfState::up) -> SpinHalfU1Mps< Scalar, Storage >
Construct a normalized alternating spin-half product MPS.
Definition spin_half_heisenberg.hpp:221
auto make_spin_half_u1_site(std::string_view charge_name="Sz") -> SpinHalfU1Site
Construct the standard U(1)-symmetric spin-half local space.
Definition spin_half_heisenberg.hpp:73
Codomain(Spaces &&...) -> Codomain< std::remove_cvref_t< Spaces >... >
Deduce a codomain's concrete value types.
void scale(Tensor &tensor, Scalar factor)
Multiply every stored numerical block by a scalar in place.
Definition block_tensor_linear.hpp:400
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
U(1)-symmetric spin-half local space and its two state charges.
Definition spin_half_heisenberg.hpp:35
static constexpr auto coordinate(SpinHalfState state) noexcept -> std::size_t
Return the physical-space coordinate of one local state.
Definition spin_half_heisenberg.hpp:48
auto qnum(SpinHalfState state) const -> QNum const &
Return the quantum number of one local state.
Definition spin_half_heisenberg.hpp:56
QNum down
Quantum number of |down>.
Definition spin_half_heisenberg.hpp:43
QNum up
Quantum number of |up>.
Definition spin_half_heisenberg.hpp:41
LocalSpace space
Physical state space in up-then-down order.
Definition spin_half_heisenberg.hpp:39
Symmetry symmetry
U(1) symmetry context shared by every model object.
Definition spin_half_heisenberg.hpp:37
Defines U1, the value-level quantum number type for one U(1) symmetry factor.