52 !(settings.
gap >= 1.0) || settings.
modes < 3 ||
54 throw std::runtime_error(
55 "TrapFinder: needs 0 < threshold < 1, gap >= 1, modes >= 3, "
56 "0 < mass <= 1 and max_size >= 1");
60 std::vector<Index> local(n, -1);
61 std::vector<Index> sites;
62 for (
Index i = 0; i < n; ++i) {
64 local[i] =
Index(sites.size());
70 throw std::runtime_error(
"TrapFinder: needs at least three sites");
73 throw std::runtime_error(
74 "TrapFinder: " + std::to_string(m) +
75 " sites; the dense eigenvalue problem is limited to " +
81 std::vector<std::vector<Index>> fwd(m), bwd(m);
82 for (
Index a = 0; a < m; ++a) {
85 if (b >= 0 &&
e.rate > 0.0) {
91 for (
const auto* g : {&fwd, &bwd}) {
92 std::vector<char> seen(m, 0);
93 std::vector<Index> stack{0};
96 while (!stack.empty()) {
97 Index a = stack.back();
99 for (
Index b : (*g)[a]) {
108 throw std::runtime_error(
109 "TrapFinder: the hopping network is not strongly connected (" +
110 std::to_string(m - count) +
111 " sites unreachable); traps are not defined");
117 for (
Index i : sites) {
120 if (!(scale > 0.0)) {
121 throw std::runtime_error(
"TrapFinder: no hops");
123 Eigen::MatrixXd At = Eigen::MatrixXd::Zero(m, m);
124 for (
Index a = 0; a < m; ++a) {
127 if (b < 0 ||
e.rate <= 0.0) {
130 At(b, a) -=
e.rate / scale;
131 At(a, a) +=
e.rate / scale;
134 Eigen::EigenSolver<Eigen::MatrixXd> es(At,
true);
135 if (es.info() != Eigen::Success) {
136 throw std::runtime_error(
"TrapFinder: eigenvalue problem failed");
138 const Eigen::VectorXcd& lambda = es.eigenvalues();
139 std::vector<Index> order(m);
140 std::iota(order.begin(), order.end(), 0);
141 std::sort(order.begin(), order.end(), [&lambda](
Index a,
Index b) {
142 return lambda(a).real() < lambda(b).real();
145 const Index modes = std::min(settings.
modes, m);
146 for (
Index k = 0; k < modes; ++k) {
147 res.
eigenvalues.push_back(lambda(order[k]).real() * scale);
150 Eigen::VectorXd pi = Normalized(es.eigenvectors().col(order[0]));
151 pi = pi.cwiseMax(0.0);
154 for (
Index a = 0; a < m; ++a) {
158 for (
Index i = 1; i + 1 < modes; ++i) {
169 std::vector<std::vector<Index>> adj(m);
170 for (
Index a = 0; a < m; ++a) {
173 if (b >= 0 &&
e.rate > 0.0) {
180 std::vector<Index> sites;
183 std::vector<Candidate> candidates;
184 std::vector<char> in(m, 0);
185 std::vector<char> seen(m, 0);
187 Eigen::VectorXd v = Normalized(es.eigenvectors().col(order[k]));
194 const double tiny = 1
e-12 * v.cwiseAbs().maxCoeff();
195 for (
Index a = 0; a < m; ++a) {
196 positive += v(a) > tiny;
197 negative += v(a) < -tiny;
199 const double sign = positive <= negative ? 1.0 : -1.0;
200 Eigen::VectorXd side = (sign * v).cwiseMax(0.0);
201 side /= side.maxCoeff();
202 const double side_mass = side.sum();
203 for (
Index a = 0; a < m; ++a) {
207 for (
Index a = 0; a < m; ++a) {
208 if (!in[a] || seen[a]) {
211 std::vector<Index> comp{a};
213 for (std::size_t q = 0; q < comp.size(); ++q) {
214 for (
Index b : adj[comp[q]]) {
215 if (in[b] && !seen[b]) {
223 for (
Index b : comp) {
227 mass < settings.
mass * side_mass) {
230 std::sort(comp.begin(), comp.end());
231 candidates.push_back({std::move(comp), k});
236 candidates.begin(), candidates.end(),
237 [](
const Candidate& a,
const Candidate& b) {
238 return a.sites.size() > b.sites.size() ||
239 (a.sites.size() == b.sites.size() &&
240 (a.sites < b.sites || (a.sites == b.sites && a.mode < b.mode)));
242 std::vector<Candidate> traps;
243 for (
const Candidate& c : candidates) {
245 std::any_of(traps.begin(), traps.end(), [&c](
const Candidate& t) {
246 return std::includes(t.sites.begin(), t.sites.end(), c.sites.begin(),
253 for (
const Candidate& t : traps) {
255 std::vector<char> member(m, 0);
256 for (
Index a : t.sites) {
260 for (
Index a : t.sites) {
264 if (b >= 0 && !member[b]) {
265 out += pi(a) *
e.rate;
268 trap.
sites.push_back(sites[a]);
282 return graph.getSite(a).energy < graph.getSite(b).energy;
284 res.
traps.push_back(std::move(trap));
288 return a.release_rate < b.release_rate;