91 const size_t dim =
size_t{1} << to.size(), sub =
size_t{1} << from.size();
92 std::vector<size_t> offsets(sub);
93 const size_t mask = Offsets(from, to, offsets.data());
95 Eigen::MatrixXcd result = Eigen::MatrixXcd::Zero(dim, dim);
96 for (
size_t base = 0; base < dim; ++base)
100 for (
size_t col = 0; col < sub; ++col)
101 for (
size_t row = 0; row < sub; ++row)
102 result(base | offsets[row], base | offsets[col]) = matrix(row, col);
112 const size_t k = order.size(), sub =
size_t{1} << k;
114 throw std::logic_error(
"Fused gates act on at most 3 qubits");
115 size_t offsets[8] = {};
116 const size_t mask = Offsets(order, to, offsets);
119 double gr[64], gi[64];
120 for (
size_t r = 0; r < sub; ++r)
121 for (
size_t l = 0; l < sub; ++l)
123 gr[r * sub + l] = gate(r, l).real();
124 gi[r * sub + l] = gate(r, l).imag();
126 auto *data = matrix.data();
127 switch (matrix.rows() * 4 + k)
130 return ApplyLeftFixed<1, 2>(gr, gi, offsets, mask, data);
132 return ApplyLeftFixed<1, 4>(gr, gi, offsets, mask, data);
134 return ApplyLeftFixed<2, 4>(gr, gi, offsets, mask, data);
136 return ApplyLeftFixed<1, 8>(gr, gi, offsets, mask, data);
138 return ApplyLeftFixed<2, 8>(gr, gi, offsets, mask, data);
140 return ApplyLeftFixed<3, 8>(gr, gi, offsets, mask, data);
142 throw std::logic_error(
"Fused blocks act on at most 3 qubits");
146 template <
class Emit>
void Submit(
const Gate &gate, uint64_t source, Emit &&emit,
bool preserveStructure =
false,
bool standalone =
false)
148 auto targets = gate.Targets();
149 if (targets.empty() || targets.size() > width_)
150 throw std::invalid_argument(
"Gate exceeds fusion width");
151 std::sort(targets.begin(), targets.end());
152 if (std::adjacent_find(targets.begin(), targets.end()) != targets.end())
153 throw std::invalid_argument(
"Gate targets must be distinct");
155 std::vector<uint64_t> overlapping;
156 for (
auto q : targets)
158 auto it = owners_.find(q);
159 if (it != owners_.end())
160 overlapping.push_back(it->second);
162 std::sort(overlapping.begin(), overlapping.end());
163 overlapping.erase(std::unique(overlapping.begin(), overlapping.end()), overlapping.end());
168 for (
auto id : overlapping)
170 emit(Block{std::move(targets), {}, gate, {source, source, 1}, preserveStructure, gate.SchmidtRank()});
176 if (preserveStructure && !overlapping.empty() &&
177 std::all_of(overlapping.begin(), overlapping.end(), [&](
auto id) { return blocks_.at(id).preserveStructure; }))
179 for (
auto id : overlapping)
183 auto support = targets;
184 for (
auto id : overlapping)
186 const auto &old = blocks_.at(
id);
187 support.insert(support.end(), old.targets.begin(), old.targets.end());
189 std::sort(support.begin(), support.end());
190 support.erase(std::unique(support.begin(), support.end()), support.end());
191 if (support.size() > width_)
198 std::vector<uint64_t> retained;
199 for (
auto id : overlapping)
201 auto candidate = support;
202 const auto &old = blocks_.at(
id);
203 candidate.insert(candidate.end(), old.targets.begin(), old.targets.end());
204 std::sort(candidate.begin(), candidate.end());
205 candidate.erase(std::unique(candidate.begin(), candidate.end()), candidate.end());
206 if (candidate.size() <= width_)
208 retained.push_back(
id);
209 support = std::move(candidate);
214 overlapping = std::move(retained);
217 if (overlapping.empty())
219 for (
auto q : support)
221 blocks_.emplace(source, Block{std::move(support), {}, gate, {source, source, 1}, preserveStructure, gate.SchmidtRank()});
225 Block next{{}, {}, Gate{}, {source, source, 1},
false, gate.SchmidtRank()};
231 for (
auto id : overlapping)
233 const auto &old = blocks_.at(
id);
234 next.sources.
first = std::min(next.sources.
first, old.sources.first);
235 next.sources.
count += old.sources.count;
236 next.schmidtRank = std::min(4., next.schmidtRank * old.schmidtRank);
241 const auto order = old.original.Targets();
243 next.matrix =
Embed(old.original.Matrix(), order, support);
245 ApplyLeft(old.original.Matrix(), order, support, next.matrix);
248 next.matrix =
Embed(old.matrix, old.targets, support);
250 ApplyLeft(old.matrix, old.targets, support, next.matrix);
254 ApplyLeft(gate.Matrix(), gate.Targets(), support, next.matrix);
261 const auto id = next.sources.
first;
262 for (
auto q : support)
264 next.targets = std::move(support);
266 for (
auto old : overlapping)
272 blocks_.at(
id) = std::move(next);
274 blocks_.emplace(
id, std::move(next));