62template <RealOrComplex Scalar, BlockTensorStorage Storage = PackedSparseBlockStorage<>>
66template <RealOrComplex Scalar>
75 if (charge_name.empty())
throw std::invalid_argument(
"spin-half U(1) charge name must not be empty");
77 Symmetry const symmetry(std::string(charge_name) +
":U(1)");
91inline auto mps_bond_label(std::size_t bond) -> std::string {
return "neel-mps-bond-" + std::to_string(bond); }
93inline auto mpo_bond_label(std::size_t bond) -> std::string {
return "heisenberg-mpo-bond-" + std::to_string(bond); }
95template <RealOrComplex Scalar>
struct MpoCoefficient
99 typename site_type::key_type key;
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)
107 if (
value == Scalar{})
return;
108 coefficients.push_back(
109 {.key =
typename MpoCoefficient<Scalar>::site_type::key_type{{
left, ket,
right, bra}}, .value =
value});
112template <RealOrComplex Scalar>
113void append_identity(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t right)
115 append_mpo_coefficient(coefficients, left, 0, right, 0, Scalar{1});
116 append_mpo_coefficient(coefficients, left, 1, right, 1, Scalar{1});
119template <RealOrComplex Scalar>
120void append_sz(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t right, Scalar
scale)
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});
126template <RealOrComplex Scalar>
127void append_sp(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t right, Scalar
scale)
129 append_mpo_coefficient(coefficients, left, 1, right, 0,
scale);
132template <RealOrComplex Scalar>
133void append_sm(std::vector<MpoCoefficient<Scalar>>& coefficients, std::size_t left, std::size_t right, Scalar
scale)
135 append_mpo_coefficient(coefficients, left, 0, right, 1,
scale);
138template <RealOrComplex Scalar>
139auto make_mpo_site(SpinHalfU1Site
const& local, LocalSpace
const& left, LocalSpace
const& right,
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);
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;
154template <RealOrComplex Scalar>
155auto first_heisenberg_site(SpinHalfU1Site
const& local, LocalSpace
const& left, LocalSpace
const& right,
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);
167template <RealOrComplex Scalar>
168auto bulk_heisenberg_site(SpinHalfU1Site
const& local, LocalSpace
const& left, LocalSpace
const& right, Scalar exchange,
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);
184template <RealOrComplex Scalar>
185auto last_heisenberg_site(SpinHalfU1Site
const& local, LocalSpace
const& left, LocalSpace
const& right, Scalar exchange,
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);
197template <RealOrComplex Scalar>
198auto single_heisenberg_site(SpinHalfU1Site
const& local, LocalSpace
const& left, LocalSpace
const& right,
201 std::vector<MpoCoefficient<Scalar>> coefficients;
202 append_sz(coefficients, 0, 0, longitudinal_field);
203 return make_mpo_site(local, left, right, coefficients);
219template <RealOrComplex Scalar =
double, BlockTensorStorage Storage = PackedSparseBlockStorage<>>
220 requires ImmediateLocalBlockStorageFor<Storage, Scalar, 3, 2>
224 if (length == 0)
throw std::invalid_argument(
"Neel product MPS requires at least one site");
226 std::vector<BlockSpace> bonds;
227 bonds.reserve(length + 1);
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)
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));
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)
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});
250 return site_type(local.symmetry, Domain{bonds[site], local.space}, Codomain{bonds[site + 1]});
252 value.block(key)[0, 0] = Scalar{1};
253 sites.push_back(std::move(
value));
255 return mps_type(std::move(sites));
272template <RealOrComplex Scalar =
double>
276 -> SpinHalfU1Mpo<Scalar>
278 if (length == 0)
throw std::invalid_argument(
"spin-half Heisenberg MPO requires at least one site");
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}}});
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)
289 auxiliaries.emplace_back(local.
symmetry, std::initializer_list<QNum>{scalar, minus_one, plus_one, scalar, scalar},
290 detail::mpo_bond_label(bond));
292 auxiliaries.emplace_back(local.
symmetry, std::initializer_list<QNum>{scalar}, detail::mpo_bond_label(length));
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);
301 detail::single_heisenberg_site<Scalar>(local, auxiliaries[0], auxiliaries[1], Scalar{longitudinal_field}));
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)
309 sites.push_back(detail::bulk_heisenberg_site<Scalar>(local, auxiliaries[site], auxiliaries[site + 1],
310 Scalar{exchange}, Scalar{longitudinal_field}));
312 sites.push_back(detail::last_heisenberg_site<Scalar>(local, auxiliaries[length - 1], auxiliaries[length],
313 Scalar{exchange}, Scalar{longitudinal_field}));
315 return mpo_type(std::move(sites));