diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/include/graph.hpp | 1 | ||||
-rw-r--r-- | lib/include/network.hpp | 50 | ||||
-rw-r--r-- | lib/src/network.cpp | 204 |
3 files changed, 182 insertions, 73 deletions
diff --git a/lib/include/graph.hpp b/lib/include/graph.hpp index 1714dc2..45cc478 100644 --- a/lib/include/graph.hpp +++ b/lib/include/graph.hpp @@ -8,6 +8,7 @@ #include <random> #include <list> #include <exception> +#include <algorithm> #include "array_hash.hpp" diff --git a/lib/include/network.hpp b/lib/include/network.hpp index f12c9c7..ba98086 100644 --- a/lib/include/network.hpp +++ b/lib/include/network.hpp @@ -26,25 +26,55 @@ #endif -class network { +class problem { private: - cholmod_dense *b; - cholmod_factor *factor; - cholmod_sparse *voltcurmat; - cholmod_common *c; + const graph& G; + cholmod_dense* b; + cholmod_factor* factor; + cholmod_sparse* voltcurmat; + cholmod_common* c; + unsigned axis; public: + problem(const graph&, unsigned axis, cholmod_common*); + problem(const graph&, unsigned axis, cholmod_sparse*, cholmod_common*); + problem(const problem&); + ~problem(); + current_info solve(std::vector<bool>& fuses); + void break_edge(unsigned, bool unbreak = false); +}; + +class network { + public: const graph& G; std::vector<bool> fuses; std::vector<long double> thresholds; - network(const graph&, cholmod_common*); - network(const network &other); - ~network(); + network(const graph&); + network(const network&); void set_thresholds(double, std::mt19937&); - void break_edge(unsigned, bool unbreak = false); - current_info get_current_info(); +}; + +class fuse_network : public network { + public: + problem ohm; + + fuse_network(const graph&, cholmod_common*); + void fracture(hooks&, double cutoff = 1e-13); + current_info get_current_info(); +}; + +class elastic_network : public network { + public: + problem hook_x; + problem hook_y; + + elastic_network(const graph&, cholmod_common*); + elastic_network(const elastic_network&); + + void fracture(hooks&, double weight = 0.5, double cutoff = 1e-13); + current_info get_current_info(); }; diff --git a/lib/src/network.cpp b/lib/src/network.cpp index 1edc973..8b0e8df 100644 --- a/lib/src/network.cpp +++ b/lib/src/network.cpp @@ -17,14 +17,19 @@ class nofuseException: public std::exception } } nofuseex; -network::network(const graph& G, cholmod_common *c) : c(c), G(G), fuses(G.edges.size(), false), thresholds(G.edges.size(), 1) { +problem::problem(const graph& G, unsigned axis, cholmod_sparse *vcmat, cholmod_common *c) : G(G), voltcurmat(vcmat), c(c), axis(axis) { b = CHOL_F(zeros)(G.vertices.size(), 1, CHOLMOD_REAL, c); for (unsigned i = 0; i < G.edges.size(); i++) { - double v0y = G.vertices[G.edges[i].v[0]].r.y; - double v1y = G.vertices[G.edges[i].v[1]].r.y; - - if (G.edges[i].crossings[1]) { - bool ind = v0y < v1y ? 0 : 1; + graph::coordinate v0 = G.vertices[G.edges[i].v[0]].r; + graph::coordinate v1 = G.vertices[G.edges[i].v[1]].r; + + if (G.edges[i].crossings[axis]) { + bool ind; + if (axis == 1) { + ind = v0.y < v1.y ? 0 : 1; + } else { + ind = v0.x < v1.x ? 0 : 1; + } ((double *)b->x)[G.edges[i].v[ind]] += 1.0; ((double *)b->x)[G.edges[i].v[!ind]] -= 1.0; @@ -78,8 +83,10 @@ network::network(const graph& G, cholmod_common *c) : c(c), G(G), fuses(G.edges. factor = CHOL_F(analyze)(laplacian, c); CHOL_F(factorize)(laplacian, factor, c); CHOL_F(free_sparse)(&laplacian, c); +} - t = CHOL_F(allocate_triplet)(G.edges.size(), G.vertices.size(), 2 * G.edges.size(), 0, CHOLMOD_REAL, c); +problem::problem(const graph& G, unsigned axis, cholmod_common *c) : problem(G, axis, NULL, c) { + cholmod_triplet *t = CHOL_F(allocate_triplet)(G.edges.size(), G.vertices.size(), 2 * G.edges.size(), 0, CHOLMOD_REAL, c); t->nnz = 2 * G.edges.size(); @@ -98,39 +105,70 @@ network::network(const graph& G, cholmod_common *c) : c(c), G(G), fuses(G.edges. CHOL_F(free_triplet)(&t, c); } -network::network(const network &other) : c(other.c), G(other.G), fuses(other.fuses), thresholds(other.thresholds) { +problem::problem(const problem& other) : G(other.G), c(other.c), axis(other.axis) { b = CHOL_F(copy_dense)(other.b, c); factor = CHOL_F(copy_factor)(other.factor, c); voltcurmat = CHOL_F(copy_sparse)(other.voltcurmat, c); } -network::~network() { - CHOL_F(free_sparse)(&voltcurmat, c); +problem::~problem() { CHOL_F(free_dense)(&b, c); CHOL_F(free_factor)(&factor, c); + CHOL_F(free_sparse)(&voltcurmat, c); } -void network::set_thresholds(double beta, std::mt19937& rng) { - if (beta == 0.0) { - /* zero beta doesn't make any sense computationally, we interpret it as "infinity" */ - for (long double& threshold : thresholds) { - threshold = 1.0; - } - } else { - std::uniform_real_distribution<long double> d(0.0, 1.0); +current_info problem::solve(std::vector<bool>& fuses) { + cholmod_dense *x = CHOL_F(solve)(CHOLMOD_A, factor, b, c); - for (long double& threshold : thresholds) { - threshold = std::numeric_limits<long double>::lowest(); + if (((double *)x->x)[0] != ((double *)x->x)[0]) { + throw nanex; + } - while (threshold == std::numeric_limits<long double>::lowest()) { - threshold = logl(d(rng)) / (long double)beta; + cholmod_dense *y = CHOL_F(allocate_dense)(G.edges.size(), 1, G.edges.size(), CHOLMOD_REAL, c); + + double alpha[2] = {1, 0}; + double beta[2] = {0, 0}; + CHOL_F(sdmult)(voltcurmat, 0, alpha, beta, x, y, c); + + std::vector<double> currents(G.edges.size()); + + double total_current = 0; + + for (int i = 0; i < G.edges.size(); i++) { + if (fuses[i]) { + currents[i] = 0; + } else { + currents[i] = ((double *)y->x)[i]; + + graph::coordinate v0 = G.vertices[G.edges[i].v[0]].r; + graph::coordinate v1 = G.vertices[G.edges[i].v[1]].r; + + if (G.edges[i].crossings[axis]) { + bool comp; + if (axis == 1) { + comp = v0.y > v1.y; + } else { + comp = v0.x > v1.x; + } + + if (comp) { + currents[i] += 1.0; + total_current += currents[i]; + } else { + currents[i] -= 1.0; + total_current -= currents[i]; + } } } } + + CHOL_F(free_dense)(&x, c); + CHOL_F(free_dense)(&y, c); + + return {total_current, currents}; } -void network::break_edge(unsigned e, bool unbreak) { - fuses[e] = !unbreak; +void problem::break_edge(unsigned e, bool unbreak) { unsigned v0 = G.edges[e].v[0]; unsigned v1 = G.edges[e].v[1]; @@ -167,78 +205,116 @@ void network::break_edge(unsigned e, bool unbreak) { CHOL_F(free_sparse)(&perm_update_mat, c); CHOL_F(free_sparse)(&update_mat, c); - double v0y = G.vertices[v0].r.y; - double v1y = G.vertices[v1].r.y; + graph::coordinate r0 = G.vertices[v0].r; + graph::coordinate r1 = G.vertices[v1].r; - if (G.edges[e].crossings[1]) { - bool ind = v0y < v1y ? unbreak : !unbreak; + if (G.edges[e].crossings[axis]) { + bool ind; + if (axis == 1) { + ind = r0.y < r1.y ? unbreak : !unbreak; + } else { + ind = r0.x < r1.x ? unbreak : !unbreak; + } ((double *)b->x)[G.edges[e].v[ind]] -= 1.0; ((double *)b->x)[G.edges[e].v[!ind]] += 1.0; } } -current_info network::get_current_info() { - cholmod_dense *x = CHOL_F(solve)(CHOLMOD_A, factor, b, c); +network::network(const graph& G) : G(G), fuses(G.edges.size()), thresholds(G.edges.size()) {} - if (((double *)x->x)[0] != ((double *)x->x)[0]) { - throw nanex; - } +network::network(const network& n) : G(n.G), fuses(n.fuses), thresholds(n.thresholds) {} - cholmod_dense *y = CHOL_F(allocate_dense)(G.edges.size(), 1, G.edges.size(), CHOLMOD_REAL, c); +void network::set_thresholds(double beta, std::mt19937& rng) { + if (beta == 0.0) { + /* zero beta doesn't make any sense computationally, we interpret it as "infinity" */ + for (long double& threshold : thresholds) { + threshold = 1.0; + } + } else { + std::uniform_real_distribution<long double> d(0.0, 1.0); - double alpha[2] = {1, 0}; - double beta[2] = {0, 0}; - CHOL_F(sdmult)(voltcurmat, 0, alpha, beta, x, y, c); + for (long double& threshold : thresholds) { + threshold = std::numeric_limits<long double>::lowest(); - std::vector<double> currents(G.edges.size()); + while (threshold == std::numeric_limits<long double>::lowest()) { + threshold = logl(d(rng)) / (long double)beta; + } + } + } +} - double total_current = 0; +fuse_network::fuse_network(const graph& G, cholmod_common *c) : network(G), ohm(G, 1, c) { +} - for (int i = 0; i < G.edges.size(); i++) { - if (fuses[i]) { - currents[i] = 0; - } else { - currents[i] = ((double *)y->x)[i]; +void fuse_network::fracture(hooks& m, double cutoff) { + m.pre_fracture(*this); - double v0y = G.vertices[G.edges[i].v[0]].r.y; - double v1y = G.vertices[G.edges[i].v[1]].r.y; + while (true) { + current_info ci = ohm.solve(fuses); - if (G.edges[i].crossings[1]) { - if (v0y > v1y) { - currents[i] += 1.0; - total_current += currents[i]; - } else { - currents[i] -= 1.0; - total_current -= currents[i]; + if (ci.conductivity < cutoff * G.vertices.size()) { + break; + } + + unsigned max_pos = UINT_MAX; + long double max_val = std::numeric_limits<long double>::lowest(); + + for (unsigned i = 0; i < G.edges.size(); i++) { + if (!fuses[i] && fabs(ci.currents[i]) > cutoff) { + long double val = logl(fabs(ci.currents[i])) - thresholds[i]; + if (val > max_val) { + max_val = val; + max_pos = i; } } } + if (max_pos == UINT_MAX) { + throw nofuseex; + } + + fuses[max_pos] = true; + ohm.break_edge(max_pos); + + m.bond_broken(*this, ci, max_pos); } - CHOL_F(free_dense)(&x, c); - CHOL_F(free_dense)(&y, c); + m.post_fracture(*this); +} - return {total_current, currents}; +elastic_network::elastic_network(const graph& G, cholmod_common *c) : network(G), hook_x(G, 1, c), hook_y(G, 0, c) { +} + +elastic_network::elastic_network(const elastic_network& n) : network(n), hook_x(n.hook_x), hook_y(n.hook_y) { } -void network::fracture(hooks& m, double cutoff) { +void elastic_network::fracture(hooks& m, double weight, double cutoff) { m.pre_fracture(*this); while (true) { - current_info ci = this->get_current_info(); + current_info cx = hook_x.solve(fuses); + current_info cy = hook_y.solve(fuses); - if (ci.conductivity < cutoff * G.vertices.size()) { + current_info ctot; + + ctot.conductivity = sqrt(pow((1 - weight) * cx.conductivity, 2) + pow(weight * cy.conductivity, 2)); + + if (ctot.conductivity < cutoff * G.vertices.size()) { break; } + ctot.currents.resize(G.edges.size()); + for (unsigned i = 0; i < G.edges.size(); i++) { + ctot.currents[i] = sqrt(pow((1 - weight) * cx.currents[i], 2) + pow(weight * cy.currents[i], 2)); + } + unsigned max_pos = UINT_MAX; long double max_val = std::numeric_limits<long double>::lowest(); for (unsigned i = 0; i < G.edges.size(); i++) { - if (!fuses[i] && fabs(ci.currents[i]) > cutoff) { - long double val = logl(fabs(ci.currents[i])) - thresholds[i]; + if (!fuses[i] && fabs(ctot.currents[i]) > cutoff) { + long double val = logl(fabs(ctot.currents[i])) - thresholds[i]; if (val > max_val) { max_val = val; max_pos = i; @@ -250,9 +326,11 @@ void network::fracture(hooks& m, double cutoff) { throw nofuseex; } - this->break_edge(max_pos); + fuses[max_pos] = true; + hook_x.break_edge(max_pos); + hook_y.break_edge(max_pos); - m.bond_broken(*this, ci, max_pos); + m.bond_broken(*this, ctot, max_pos); } m.post_fracture(*this); |