1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
|
From 38b559464e5f8212225acc4c8aa213732ce8f6e8 Mon Sep 17 00:00:00 2001
From: Antony Lee <anntzer.lee@gmail.com>
Date: Sun, 6 Oct 2024 22:47:34 +0200
Subject: [PATCH] Reacquire GIL before constructing tuples containing numpy
arrays.
... as explicitly required by pybind11. Compile with `#define
PYBIND11_ASSERT_GIL_HELD_INCREF_DECREF` to observe GIL assertion
failures in absence of this patch.
---
src/_hmmc.cpp | 111 ++++++++++++++++++++++++++------------------------
1 file changed, 58 insertions(+), 53 deletions(-)
--- python-hmmlearn.orig/src/_hmmc.cpp
+++ python-hmmlearn/src/_hmmc.cpp
@@ -63,37 +63,39 @@
auto scaling_ = py::array_t<double>{{ns}};
auto scaling = scaling_.mutable_unchecked<1>();
auto log_prob = 0.;
- py::gil_scoped_release nogil;
- std::fill_n(fwd.mutable_data(0, 0), fwd.size(), 0);
- for (auto i = 0; i < nc; ++i) {
- fwd(0, i) = startprob(i) * frameprob(0, i);
- }
- auto sum = std::accumulate(&fwd(0, 0), &fwd(0, nc), 0.);
- if (sum < min_sum) {
- throw std::range_error{"forward pass failed with underflow; "
- "consider using implementation='log' instead"};
- }
- auto scale = scaling(0) = 1. / sum;
- log_prob -= std::log(scale);
- for (auto i = 0; i < nc; ++i) {
- fwd(0, i) *= scale;
- }
- for (auto t = 1; t < ns; ++t) {
- for (auto j = 0; j < nc; ++j) {
- for (auto i = 0; i < nc; ++i) {
- fwd(t, j) += fwd(t - 1, i) * transmat(i, j);
- }
- fwd(t, j) *= frameprob(t, j);
+ {
+ py::gil_scoped_release nogil;
+ std::fill_n(fwd.mutable_data(0, 0), fwd.size(), 0);
+ for (auto i = 0; i < nc; ++i) {
+ fwd(0, i) = startprob(i) * frameprob(0, i);
}
- auto sum = std::accumulate(&fwd(t, 0), &fwd(t, nc), 0.);
+ auto sum = std::accumulate(&fwd(0, 0), &fwd(0, nc), 0.);
if (sum < min_sum) {
throw std::range_error{"forward pass failed with underflow; "
"consider using implementation='log' instead"};
}
- auto scale = scaling(t) = 1. / sum;
+ auto scale = scaling(0) = 1. / sum;
log_prob -= std::log(scale);
- for (auto j = 0; j < nc; ++j) {
- fwd(t, j) *= scale;
+ for (auto i = 0; i < nc; ++i) {
+ fwd(0, i) *= scale;
+ }
+ for (auto t = 1; t < ns; ++t) {
+ for (auto j = 0; j < nc; ++j) {
+ for (auto i = 0; i < nc; ++i) {
+ fwd(t, j) += fwd(t - 1, i) * transmat(i, j);
+ }
+ fwd(t, j) *= frameprob(t, j);
+ }
+ auto sum = std::accumulate(&fwd(t, 0), &fwd(t, nc), 0.);
+ if (sum < min_sum) {
+ throw std::range_error{"forward pass failed with underflow; "
+ "consider using implementation='log' instead"};
+ }
+ auto scale = scaling(t) = 1. / sum;
+ log_prob -= std::log(scale);
+ for (auto j = 0; j < nc; ++j) {
+ fwd(t, j) *= scale;
+ }
}
}
return {log_prob, fwdlattice_, scaling_};
@@ -117,16 +119,18 @@
auto buf = std::vector<double>(nc);
auto fwdlattice_ = py::array_t<double>{{ns, nc}};
auto fwd = fwdlattice_.mutable_unchecked<2>();
- py::gil_scoped_release nogil;
- for (auto i = 0; i < nc; ++i) {
- fwd(0, i) = log_startprob(i) + log_frameprob(0, i);
- }
- for (auto t = 1; t < ns; ++t) {
- for (auto j = 0; j < nc; ++j) {
- for (auto i = 0; i < nc; ++i) {
- buf[i] = fwd(t - 1, i) + log_transmat(i, j);
+ {
+ py::gil_scoped_release nogil;
+ for (auto i = 0; i < nc; ++i) {
+ fwd(0, i) = log_startprob(i) + log_frameprob(0, i);
+ }
+ for (auto t = 1; t < ns; ++t) {
+ for (auto j = 0; j < nc; ++j) {
+ for (auto i = 0; i < nc; ++i) {
+ buf[i] = fwd(t - 1, i) + log_transmat(i, j);
+ }
+ fwd(t, j) = logsumexp(buf.data(), nc) + log_frameprob(t, j);
}
- fwd(t, j) = logsumexp(buf.data(), nc) + log_frameprob(t, j);
}
}
auto log_prob = logsumexp(&fwd(ns - 1, 0), nc);
@@ -290,30 +294,31 @@
auto viterbi_lattice_ = py::array_t<double>{{ns, nc}};
auto state_sequence = state_sequence_.mutable_unchecked<1>();
auto viterbi_lattice = viterbi_lattice_.mutable_unchecked<2>();
- py::gil_scoped_release nogil;
- for (auto i = 0; i < nc; ++i) {
- viterbi_lattice(0, i) = log_startprob(i) + log_frameprob(0, i);
- }
- for (auto t = 1; t < ns; ++t) {
+ {
+ py::gil_scoped_release nogil;
for (auto i = 0; i < nc; ++i) {
- auto max = -std::numeric_limits<double>::infinity();
- for (auto j = 0; j < nc; ++j) {
- max = std::max(max, viterbi_lattice(t - 1, j) + log_transmat(j, i));
+ viterbi_lattice(0, i) = log_startprob(i) + log_frameprob(0, i);
+ }
+ for (auto t = 1; t < ns; ++t) {
+ for (auto i = 0; i < nc; ++i) {
+ auto max = -std::numeric_limits<double>::infinity();
+ for (auto j = 0; j < nc; ++j) {
+ max = std::max(max, viterbi_lattice(t - 1, j) + log_transmat(j, i));
+ }
+ viterbi_lattice(t, i) = max + log_frameprob(t, i);
}
- viterbi_lattice(t, i) = max + log_frameprob(t, i);
}
- }
- auto row = &viterbi_lattice(ns - 1, 0);
- auto prev = state_sequence(ns - 1) = std::max_element(row, row + nc) - row;
- auto log_prob = row[prev];
- for (auto t = ns - 2; t >= 0; --t) {
- auto max = std::make_pair(-std::numeric_limits<double>::infinity(), 0);
- for (auto i = 0; i < nc; ++i) {
- max = std::max(max, {viterbi_lattice(t, i) + log_transmat(i, prev), i});
+ auto row = &viterbi_lattice(ns - 1, 0);
+ auto prev = state_sequence(ns - 1) = std::max_element(row, row + nc) - row;
+ for (auto t = ns - 2; t >= 0; --t) {
+ auto max = std::make_pair(-std::numeric_limits<double>::infinity(), 0);
+ for (auto i = 0; i < nc; ++i) {
+ max = std::max(max, {viterbi_lattice(t, i) + log_transmat(i, prev), i});
+ }
+ state_sequence(t) = prev = max.second;
}
- state_sequence(t) = prev = max.second;
}
- return {log_prob, state_sequence_};
+ return {viterbi_lattice(ns - 1, state_sequence(ns - 1)), state_sequence_};
}
PYBIND11_MODULE(_hmmc, m) {
|