chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:47:05 +08:00
commit 4f3b7da785
7394 changed files with 2005594 additions and 0 deletions
@@ -0,0 +1 @@
This folder contains generic helpers implementations, for operations that do not require platform-specific code, or have no real sense of having platform-specific code
@@ -0,0 +1,154 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author sgazeos@gmail.com
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_choose)
#include <array/NDArrayFactory.h>
#include <ops/declarable/helpers/choose.h>
#include <ops/ops.h>
namespace sd {
namespace ops {
namespace helpers {
template <typename T>
static NDArray* processCondition_(int mode, NDArray* arg, NDArray* comp, NDArray& compScalar);
template <typename T>
static T processElementCondition(int mode, T d1, T d2);
template <typename T>
NDArray* processCondition_(int mode, NDArray* arg, NDArray* comp, NDArray* output, NDArray* numResult,
NDArray& compScalar) {
// Convert to straight ndarray based on input
int numResults = 0;
if (comp != nullptr) {
if (comp->isScalar()) {
// Other input for compare could be an ndarray or a secondary scalar
// for comparison
// sd::NDArray arg1 = *arg;
// sd::NDArray comp1 = *comp;
for (LongType i = 0; i < arg->lengthOf(); i++) {
T result2 = processElementCondition(mode, arg->e<T>(i), comp->e<T>(0));
if (result2 > static_cast<T>(0)) {
if (output != nullptr) output->p(numResults, arg->e<T>(i));
numResults++;
}
}
} else {
// Other input for compare could be an ndarray or a secondary scalar
// for comparison
NDArray arg1 = *arg;
for (LongType i = 0; i < arg->lengthOf(); i++) {
T result2 = processElementCondition(mode, arg->e<T>(i), comp->e<T>(i));
if (result2 > static_cast<T>(0)) {
if (output != nullptr) output->p(numResults, arg->e<T>(i));
numResults++;
}
}
}
} else {
// sd::NDArray arg1 = *arg;
// Other input for compare could be an ndarray or a secondary scalar
// for comparison
for (LongType i = 0; i < arg->lengthOf(); i++) {
T result2 = processElementCondition(mode, arg->e<T>(i), compScalar.e<T>(0));
if (result2 > static_cast<T>(0)) {
if (output != nullptr) output->p(numResults, arg->e<T>(i));
numResults++;
}
}
}
if (numResult != nullptr) numResult->p(0, numResults);
return output;
}
NDArray* processCondition(LaunchContext* context, int mode, NDArray* arg, NDArray* comp, NDArray* output,
NDArray* numResult, NDArray& compScalar) {
arg->syncToHost();
if (comp != nullptr) comp->syncToHost();
if (output != nullptr) output->syncToHost();
if (numResult != nullptr) numResult->syncToHost();
compScalar.syncToHost();
BUILD_SINGLE_SELECTOR(arg->dataType(), return processCondition_, (mode, arg, comp, output, numResult, compScalar),
SD_FLOAT_TYPES);
arg->syncToDevice();
if (comp != nullptr) comp->syncToDevice();
if (output != nullptr) output->syncToDevice();
if (numResult != nullptr) numResult->syncToDevice();
compScalar.syncToDevice();
return nullptr;
}
BUILD_SINGLE_TEMPLATE( NDArray* processCondition_,
(int mode, sd::NDArray* arg, sd::NDArray* comp, sd::NDArray* output, sd::NDArray* numResult,
sd::NDArray& compScalar),
SD_FLOAT_TYPES);
template <typename T>
T processElementCondition(int mode, T d1, T d2) {
T input[3] = {d2, (T)SD_EPSILON, (T)mode};
T res = simdOps::MatchCondition<T, T>::op(d1, input);
return res;
}
void chooseFunctorArray(LaunchContext* context, NDArray* arg, NDArray* comp, int mode, NDArray* result,
NDArray* numResults) {
if (arg->isScalar() || comp->isScalar()) {
if (arg->isScalar()) {
processCondition(context, mode, comp, nullptr, result, numResults, *arg);
} else {
processCondition(context, mode, arg, nullptr, result, numResults, *comp);
}
} else {
auto zero = NDArrayFactory::create<float>(0);
processCondition(context, mode, arg, comp, result, numResults, *zero);
delete zero;
}
}
void chooseFunctorScalar(LaunchContext* context, NDArray* arg, double scalar, int mode, NDArray* result,
NDArray* numResults) {
auto scalarA = NDArrayFactory::create(scalar);
processCondition(context, mode, arg, nullptr, result, numResults, *scalarA);
delete scalarA;
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,658 @@
/*******************************************************************************
* Copyright (c) 2021 Deeplearning4j Contributors
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
*******************************************************************************/
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_ctcBeam)
//
// @author AbdelRauf
//
#include <execution/ThreadPool.h>
#include <execution/Threads.h>
#include <helpers/LoopsCoordsHelper.h>
#include <ops/declarable/helpers/ctc.h>
#include <algorithm>
#include <cassert>
#include <cmath>
#include <limits>
#include <numeric>
#include <vector>
#include <system/selective_rendering.h>
namespace sd {
namespace ops {
namespace helpers {
template <typename T>
struct BeamProb {
T total = negative_infinity<T>();
T non_blank = negative_infinity<T>();
T blank = negative_infinity<T>(); // log(1)
};
template <typename T, typename T2 = void>
struct DefaultInvalid {
static constexpr T value = T();
};
template <typename T>
struct DefaultInvalid<T, typename std::enable_if<std::is_integral<T>::value>::type> {
static constexpr T value = static_cast<T>(-1);
};
template <typename T>
struct SequenceNode {
// intrusive double links
SequenceNode<T>* prev = nullptr;
SequenceNode<T>* next = nullptr;
// sequence prefix/parent
SequenceNode<T>* prefix = nullptr;
T value = DefaultInvalid<T>::value;
int state = 0;
void markAsFullyExtended() { state |= 1; }
void increaseRef() {
// we will have just two copies in bad case. so just or
state = state | 2;
}
void decreaseRef() {
// we will have just two cases in bad case, so just remove that
state = state & (-2);
}
bool safeToRemove() {
if (state & 1) return false;
decreaseRef();
// we do not want to remove parent nodes in our case. otherwise just returning state<=1 is ok
return state == 0;
}
bool isFullyExtended() const { return state & 1; }
};
/***
* Sequence container.
*
* NOTE: it is not thread-safe
*
* Extend path - O(1)
* Remove path - O(1)
* Generating Sequence with backtracking prefix: O(n)
*
* Note: Sequence container is implemented primitively and only usable within this task.
* As it does not behave as a fully capable tree. some cases should be handled manually
*
* Here is special cases that should be handled manually to exploit tree/graph behaviour:
*
* Extending new path value:
*
* To extend the path one need to give path and value and in return get new_path:
* new_path = container.extendPath ( path, new_value );
*
* Also note that:
* SequenceContainer has already default empty path as a beginning point for paths.
* So as an initial node one should use it.
* initial_path = container.getEmptyPath();
*
* Adding new path that could be already in container:
*
* Assume we have two paths that can overlap in next step
* 1st path: node#0() -> node#1(1) => generated sequence {},{1}
* 2nd path: node#0() -> node#1(1) -> node#2(2) => generated sequence {},{1}, {2}
*
* While extending the first path with value (2). it will be:
*
* node#0() -> node#0(1) -> node#( either new or old)(2) => generated sequence {},{1}, {2}
*
* For some tasks its not desired to have additional node that will generate the same sequence.
* For example:
* Assume you wanted to use it as sequence entry in map with just (entry->prefix, entry->value).
* so in that case having different paths is not correct and will not be unique in map.
*
* there is not direct way to handle that in our container other than searching.
* So one should look for the node with prefix node#1(1) and value(2) and return that node instead of adding new
one
* Fortunately, for our beam search case:
*
* we need only look for such overlapped cases within the candidates list.
* which makes it easy to determine them beforehand while finding and marking overlapped cases. instead of looking
for it in SequenceContainer
*
* Removing the same nodes multiple times:
* It is fast to remove nodes. As nodes can be stored externally One should follow this rule:
*
* One should not remove the same node twice as it will lead to double free. as Nodes are pointers the same
applies to removing a copy
*
* There could be cases where you would like to store copy of nodes. in that cases you can use below method to be
able to safely remove:
* node should have mutable method named safeToRemove().
* Basic implementation will be decreasing reference/copy counts and returning true if it is safe to delete
*
*
*/
template <typename T>
class SequenceContainer {
public:
SequenceContainer() : count_(1) {
empty_path = new SequenceNode<T>();
current_ = empty_path;
}
SequenceContainer(const SequenceContainer& s) = delete;
SequenceContainer(SequenceContainer&& other) noexcept {
this->current_ = other.current_;
other.current_ = nullptr;
}
SequenceContainer& operator=(const SequenceContainer& other) = delete;
SequenceContainer& operator=(SequenceContainer&& other) noexcept {
if (this != other) {
clear();
this->current_ = other.current_;
this->count_ = other.count_;
other.current_ = nullptr;
other.count_ = 0;
}
return *this;
}
SequenceNode<T>* getEmptyPath() { return current_; }
SequenceNode<T>* extendPath(SequenceNode<T>* prefix, T value) {
auto new_node = new SequenceNode<T>();
new_node->value = value;
new_node->prefix = prefix;
// add in the holder
new_node->next = nullptr;
new_node->prev = current_;
if (current_) current_->next = new_node;
current_ = new_node;
count_++;
return new_node;
}
void remove(SequenceNode<T>* seq) {
if (seq == nullptr) return;
if (!seq->safeToRemove()) return;
SequenceNode<T>* previous = seq->prev;
SequenceNode<T>* next = seq->next;
if (previous) previous->next = next;
if (next) next->prev = previous;
if (current_ == seq) {
current_ = previous;
}
delete seq;
count_--;
}
static std::vector<T> getSequence(SequenceNode<T>* seq, size_t reserve_size = 1024) {
std::vector<T> ret;
ret.reserve(reserve_size);
SequenceNode<T>* backtrack = seq;
while (backtrack) {
ret.push_back(backtrack->value);
backtrack = backtrack->prefix;
}
if (ret.size() > 1) {
// remove last default node
ret.pop_back();
// reverse
std::reverse(std::begin(ret), std::end(ret));
return ret;
}
return {};
}
void clear() {
// destruct all nodes
SequenceNode<T>* del = current_;
// int i = 0;
while (del) {
//++i;
SequenceNode<T>* temp = del->prev;
delete del;
del = temp;
}
current_ = nullptr;
}
~SequenceContainer() { clear(); }
private:
SequenceNode<T>* current_ = nullptr;
SequenceNode<T>* empty_path = nullptr;
int count_ = 0;
};
template <typename T, typename U>
struct BeamEntry {
SequenceNode<U>* sequence{};
BeamProb<T> prob;
};
template <typename T, typename U>
struct BeamEntryEx {
BeamEntry<T, U> entry;
// keep indices for lookUp
int index_as_child = -1;
int index_as_parent = -1;
int children_count = 0;
};
template <typename T, typename U>
struct LookUpEntry {
U last_c; // this is is the same as node->value. just we added for the speed
SequenceNode<U>* node = nullptr;
int next_beam_index = -1; // index inside next_beam array
};
template <typename T, typename U>
static bool compare_beam_prob(const BeamEntry<T, U>& i1, const BeamEntry<T, U>& i2) {
return (i1.prob.total > i2.prob.total);
}
template <typename T, typename U>
SD_INLINE T pr(const int c, const BeamProb<T>& beam_prob, const SequenceNode<U>* seq, const T prob) {
return seq->value == c ? beam_prob.blank + prob : beam_prob.total + prob;
}
template <bool HasElementStride = false, typename Type, typename IndexType>
void inner_beam_search(const Type* log_p, const uint64_t inc_p, IndexType* result_sequence, const uint64_t inc_res_seq,
const uint64_t max_len_t, Type* result_prob, IndexType* result_seq_length, uint64_t len_t,
const uint64_t len_c, const int blank_index, int beam_width, int nbest_len,
bool normalize_logits, const uint64_t element_stride = 1L) {
using BeamEntryType = BeamEntry<Type, IndexType>;
using BeamEntryTypeEx = BeamEntryEx<Type, IndexType>;
if (beam_width < 1) beam_width = 1;
if (nbest_len > beam_width) nbest_len = beam_width;
// if len_t is greater than max_len_t truncate it
len_t = len_t > max_len_t ? max_len_t : len_t;
SequenceContainer<IndexType> sequence_container;
BeamEntryType empty;
empty.prob.blank = 0;
empty.prob.total = log_sum_exp(empty.prob.blank, empty.prob.non_blank);
empty.sequence = sequence_container.getEmptyPath();
// vectors: we will use it as array, here
std::vector<BeamEntryTypeEx> last_beams;
std::vector<BeamEntryType> next_beams;
last_beams.resize(beam_width);
// as we skip blank indexes the count is beam_width * len_c
next_beams.resize(beam_width * len_c);
last_beams[0].entry = empty;
last_beams[0].index_as_child = -1;
last_beams[0].index_as_parent = -1;
last_beams[0].children_count = 0;
auto last_beam_size = 1;
// lookupContainer:
// it will keep sorted entries. so we will just move and compare the entry
// in each step there will be overlapped cases
// the size of overlapped cases in last_beam[0:beam_width]:
// as we have beam_width size in each step after sort and pruning
// there is at least one item who will not have any parent
// and for the rest (beam_width-1) it will check has_parent_in_container() ? 1 : 0
// so maximum size of overlapped pairs is beam_width-1
std::vector<LookUpEntry<Type, IndexType>> lookUp;
lookUp.resize(beam_width - 1);
// additional storage to sort overlapped case by classes
std::vector<std::pair<IndexType, int>> child_class_sorter_help;
child_class_sorter_help.resize(beam_width - 1);
Type norm_offset = static_cast<Type>(0);
for (uint64_t t = 0; t < len_t; t++) {
auto next_beam_size = 0;
if (normalize_logits) {
norm_offset = softmax_normalization_term<HasElementStride, Type, IndexType>(log_p, len_c, element_stride);
}
for (auto j = 0; j < last_beam_size; j++) {
SequenceNode<IndexType>* seq = last_beams[j].entry.sequence;
auto& cur_prob = last_beams[j].entry.prob;
// if len(seq) > 0 then
const auto log_p_blank = element<HasElementStride>(log_p, blank_index, element_stride);
Type blank_prob, non_blank_prob;
// log_p[seq->value]
non_blank_prob = seq->value != -1
? (element<HasElementStride>(log_p, seq->value, element_stride) + cur_prob.non_blank)
: negative_infinity<Type>();
blank_prob = log_p_blank + cur_prob.total;
if (normalize_logits) {
non_blank_prob = non_blank_prob - norm_offset;
blank_prob = blank_prob - norm_offset;
}
auto look_up_beam_index = -1;
if (last_beams[j].index_as_child != -1) {
// check entry
look_up_beam_index = lookUp[last_beams[j].index_as_child].next_beam_index;
}
if (look_up_beam_index == -1) {
BeamEntryType entry;
entry.sequence = seq;
entry.prob.blank = blank_prob;
entry.prob.non_blank = non_blank_prob;
entry.prob.total = log_sum_exp(blank_prob, non_blank_prob);
next_beams[next_beam_size] = entry;
// map if its overlapped one. in this case just being child is enough
if (last_beams[j].index_as_child != -1) {
lookUp[last_beams[j].index_as_child].next_beam_index = next_beam_size;
}
++next_beam_size;
} else {
// note: here we took as ref &
auto& entry_prob = next_beams[look_up_beam_index].prob;
entry_prob.blank = log_sum_exp(entry_prob.blank, blank_prob);
entry_prob.non_blank = log_sum_exp(entry_prob.non_blank, non_blank_prob);
entry_prob.total = log_sum_exp(entry_prob.blank, entry_prob.non_blank);
}
// check to see if it is overlapped parent
auto start_index = last_beams[j].index_as_parent;
auto end_index = last_beams[j].index_as_parent + last_beams[j].children_count;
for (int c = 0; c < static_cast<int>(len_c); c++) {
if (c == blank_index) continue;
const auto prob = element<HasElementStride>(log_p, c, element_stride); // log_p[c];
non_blank_prob = pr(c, cur_prob, seq, prob);
if (normalize_logits) non_blank_prob = non_blank_prob - norm_offset;
// extend by new character
auto look_up_beam_index_ex = -1;
int found_index = -1;
// get index within array if its that class index
if (start_index < end_index && lookUp[start_index].last_c == c) {
look_up_beam_index_ex = lookUp[start_index].next_beam_index;
found_index = start_index;
++start_index;
}
if (look_up_beam_index_ex == -1) {
BeamEntryType entry;
SequenceNode<IndexType>* extended_sequence;
if (found_index != -1) {
extended_sequence = lookUp[found_index].node;
// assing next_beam_index for lookup
lookUp[found_index].next_beam_index = next_beam_size;
extended_sequence->increaseRef();
} else {
extended_sequence = sequence_container.extendPath(seq, c);
}
entry.prob.non_blank = non_blank_prob;
entry.prob.total = non_blank_prob;
entry.sequence = extended_sequence;
next_beams[next_beam_size] = entry;
++next_beam_size;
} else {
auto& entry_prob = next_beams[look_up_beam_index_ex].prob;
entry_prob.non_blank = log_sum_exp(entry_prob.non_blank, non_blank_prob);
entry_prob.total = log_sum_exp(entry_prob.total, non_blank_prob);
}
} // iteration over classes
// mark it as extended
seq->markAsFullyExtended();
} // iteration over beams
log_p += inc_p;
last_beam_size = std::min(next_beam_size, beam_width);
#if !defined(NTH_ELEMENT)
// sort next beams to get candidates
std::partial_sort(std::begin(next_beams), std::begin(next_beams) + last_beam_size,
std::begin(next_beams) + next_beam_size, compare_beam_prob<Type, IndexType>);
#else
std::nth_element(std::begin(next_beams), std::begin(next_beams) + last_beam_size,
std::begin(next_beams) + next_beam_size, compare_beam_prob<Type, IndexType>);
#endif
if (t < len_t) {
// copy top beams
for (int j = 0; j < last_beam_size; j++) {
last_beams[j].entry = next_beams[j];
last_beams[j].index_as_child = -1;
last_beams[j].index_as_parent = -1;
last_beams[j].children_count = 0;
}
// delete sequences from the sequence_holder to decrease memory
for (auto j = beam_width; j < next_beam_size; j++) {
sequence_container.remove(next_beams[j].sequence);
}
// check overlapping cases and create lookUp with sorted classes as well
int look_up_index = 0;
for (auto j = 0; j < last_beam_size; j++) {
// if it is not parent node then there is not any need to check
if (last_beams[j].entry.sequence->isFullyExtended()) {
auto parent_seq = last_beams[j].entry.sequence;
int children_count = 0;
for (int k = 0; k < last_beam_size; k++) {
auto current = last_beams[k].entry.sequence;
if (current->prefix == parent_seq) {
child_class_sorter_help[children_count].second = k;
++children_count;
}
}
if (children_count > 0) {
// sort by class
if (children_count < 2) {
//
if (children_count > 1 && child_class_sorter_help[0].first > child_class_sorter_help[1].first) {
std::swap(child_class_sorter_help[0], child_class_sorter_help[1]);
}
} else {
std::sort(std::begin(child_class_sorter_help), std::begin(child_class_sorter_help) + children_count,
[](const std::pair<int, int>& left, const std::pair<int, int>& right) {
return left.first < right.first;
});
}
last_beams[j].index_as_parent = look_up_index;
last_beams[j].children_count = children_count;
for (int l = 0; l < children_count; l++) {
int c = child_class_sorter_help[l].first;
int k = child_class_sorter_help[l].second;
// std::cout << c <<" , " << k << std::endl;
last_beams[k].index_as_child = look_up_index;
auto seq = last_beams[k].entry.sequence;
lookUp[look_up_index].last_c = c;
lookUp[look_up_index].node = seq;
lookUp[look_up_index].next_beam_index = -1;
// next one
++look_up_index;
}
} // add sorted lookUps
}
} // overlap_direction identified to speed up lookUp
}
} // iterate over t
#if defined(NTH_ELEMENT)
// use sort for n elements as only nth_element was used
std::sort(std::begin(next_beams), std::begin(next_beams) + last_beam_size, compare_beam_prob<Type, IndexType>);
#endif
// store nbest results
if (nbest_len <= last_beam_size) {
for (int j = 0; j < nbest_len; j++) {
auto top = next_beams[j];
auto result_vector = SequenceContainer<IndexType>::getSequence(top.sequence, len_t);
const auto seq_size = result_vector.size();
result_prob[j] = top.prob.total;
result_seq_length[j] = seq_size;
// copy sequence
for (size_t s = 0; s < seq_size; s++) {
result_sequence[s] = result_vector[s];
}
result_sequence += inc_res_seq;
}
} else {
for (int j = 0; j < nbest_len; j++) {
result_prob[j] = negative_infinity<Type>();
result_seq_length[j] = 0;
;
}
}
return;
}
template <typename Type, typename IndexType = int>
void beamSearch_(NDArray& logit, NDArray& sequence_length, NDArray& result_sequences, NDArray& result_probs,
NDArray& result_sequences_length, int blank_index, int beam_width, int nbest_len,
bool normalize_logits) {
const auto shapes = logit.shapeOf();
const auto strides = logit.stridesOf();
const auto rank = logit.rankOf();
uint64_t element_stride_t = 1;
// checks before
if (rank < 2) return;
auto batch_len = rank > 2 ? shapes[0] : 1;
auto max_len_t = shapes[rank - 2];
auto len_c = shapes[rank - 1];
if (len_c < 1 || max_len_t < 1) return;
// defaulting blankIndex to the last class if its incorrect or -1
if (blank_index > len_c || blank_index < 0) blank_index = static_cast<int>(len_c) - 1;
// strides
auto batch_stride = rank > 2 ? strides[0] : 0;
auto inc_p = strides[rank - 2];
auto element_stride = logit.stridesOf()[rank - 1];
#if defined(ASSERT_INNER)
// result_probs should be [batch_len, nbest_len]
assert(result_probs.ews() == 1 && result_probs.rankOf() == 2 && result_probs.shapeOf()[0] == batch_len &&
result_probs.shapeOf()[1] == nbest_len);
// result sequence should be [batch_len, nbest_len, max_len_t]
assert(result_sequences.ews() == 1 && result_sequences.rankOf() == 3 && result_sequences.shapeOf()[0] == batch_len &&
result_sequences.shapeOf()[1] == nbest_len && result_sequences.shapeOf()[2] == max_len_t);
#endif
// as ctcBeam search runs on Cpu we should make NdArray buffers available on the host side as well
NDArray::preparePrimaryUse({&result_sequences, &result_probs, &result_sequences_length}, {&sequence_length, &logit});
auto logits_ptr = logit.bufferAsT<Type>();
auto result_seq_ptr = result_sequences.bufferAsT<IndexType>();
auto result_probs_ptr = result_probs.bufferAsT<Type>();
auto result_seq_length_ptr = result_sequences_length.bufferAsT<IndexType>();
const IndexType* len_t_ptr = nullptr;
if (sequence_length.rankOf() == 1 && sequence_length.shapeOf()[0] == batch_len) {
len_t_ptr = sequence_length.bufferAsT<IndexType>();
element_stride_t = sequence_length.stridesOf()[0];
}
const auto batch_stride_res = result_sequences.stridesOf()[0];
const auto inc_res = result_sequences.stridesOf()[1];
const auto batch_stride_res_prob = result_probs.stridesOf()[0];
const auto batch_stride_res_seq_length = result_sequences_length.stridesOf()[0];
auto func = [max_len_t, len_c, batch_stride, inc_p, element_stride, element_stride_t, logits_ptr, len_t_ptr,
blank_index, beam_width, normalize_logits, nbest_len, result_seq_ptr, result_seq_length_ptr,
result_probs_ptr, batch_stride_res, inc_res, batch_stride_res_prob, batch_stride_res_seq_length](
uint64_t thread_id, int64_t start, int64_t stop, int64_t increment) -> void {
auto ptr = logits_ptr + start * batch_stride;
if (element_stride == 1) {
// choose ews one
for (auto b = start; b < stop; b += increment) {
auto prob_ptr = &(result_probs_ptr[b * batch_stride_res_prob]);
auto seq_length_ptr = &(result_seq_length_ptr[b * batch_stride_res_seq_length]);
auto seq_ptr = &(result_seq_ptr[b * batch_stride_res]);
auto len_t = len_t_ptr ? len_t_ptr[b * element_stride_t] : max_len_t;
inner_beam_search<false, Type, IndexType>(ptr, inc_p, seq_ptr, inc_res, max_len_t, prob_ptr, seq_length_ptr,
len_t, len_c, blank_index, beam_width, nbest_len, normalize_logits);
ptr += batch_stride;
}
} else {
// element with stride case
for (auto b = start; b < stop; b += increment) {
auto prob_ptr = &(result_probs_ptr[b * batch_stride_res_prob]);
auto seq_length_ptr = &(result_seq_length_ptr[b * batch_stride_res_seq_length]);
auto seq_ptr = &(result_seq_ptr[b * batch_stride_res]);
auto len_t = len_t_ptr ? len_t_ptr[b * element_stride_t] : max_len_t;
inner_beam_search<false, Type, IndexType>(ptr, inc_p, seq_ptr, inc_res, max_len_t, prob_ptr, seq_length_ptr,
len_t, len_c, blank_index, beam_width, nbest_len, normalize_logits,
element_stride);
ptr += batch_stride;
}
}
};
samediff::Threads::parallel_for(func, 0, batch_len, 1);
NDArray::registerPrimaryUse({&result_sequences, &result_probs, &result_sequences_length}, {&sequence_length, &logit});
return;
}
void beamSearch(NDArray& logit, NDArray& sequence_length, NDArray& result_sequences, NDArray& result_probs,
NDArray& result_sequences_length, int blank_index, int beam_width, int nbest_len,
bool normalize_logits = true) {
auto logitDType = logit.dataType();
auto resSeqDType = result_sequences.dataType();
BUILD_DOUBLE_SELECTOR(logit.dataType(), result_sequences.dataType(), beamSearch_,
(logit, sequence_length, result_sequences, result_probs, result_sequences_length, blank_index,
beam_width, nbest_len, normalize_logits),
SD_FLOAT_TYPES, SD_INDEXING_TYPES);
}
BUILD_DOUBLE_TEMPLATE( void beamSearch_,
(NDArray& logit, NDArray& sequence_length, NDArray& result_sequences,
NDArray& result_probs, NDArray& result_sequences_length, int blank_index, int beam_width,
int nbest_len, bool normalize_logits),
SD_FLOAT_TYPES, SD_INDEXING_TYPES);
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,669 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* * under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
//
// @author Yurii Shyrma (iuriish@yahoo.com), created on 15.02.2018, Alex Black
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_gru)
// implementation of gated Recurrent Unit cell
// (cf. https://arxiv.org/abs/1406.1078).
// Kyunghyun Cho, Bart van Merrienboer, Caglar Gulcehre, Dzmitry Bahdanau, Fethi Bougares, Holger Schwenk, Yoshua Bengio
// "Learning Phrase Representations using RNN Encoder-Decoder for Statistical Machine Translation"
#include <helpers/MmulHelper.h>
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/gru.h>
#include <ops/declarable/helpers/transforms.h>
namespace sd {
namespace ops {
namespace helpers {
//////////////////////////////////////////////////////////////////////////
void gruCell(sd::LaunchContext* context, NDArray* x, NDArray* hI, NDArray* W, NDArray* Wc,
NDArray* b, NDArray* bc, NDArray* r, NDArray* u, NDArray* c, NDArray* h) {
// Inputs:
// x input [bS, nIn], nIn - input size
// hI previous cell output [bS, nOut], that is at previous time step t-1, nOut - number of units
// W RU weights - [nIn+nOut, 2*nOut] - reset and update gates
// Wc C weights - [nIn+nOut, nOut] - cell gate
// b r and u biases, [2*nOut] - reset and update gates
// bc c biases, [nOut] - cell gate
// Outputs:
// r Reset gate output [bS, nOut]
// u Update gate output [bS, nOut]
// c Cell gate output [bS, nOut]
// h current cell output [bS, nOut]
/***************************************************************************************/
/************************ THIS IS NOT OPTIMIZED CODE ***********************************/
/** however it is more math-friendly and convenient for backprop formulas derivation) **/
const int bS = x->sizeAt(0);
const int nIn = x->sizeAt(1);
const int nOut = hI->sizeAt(1);
NDArray *Wrx = (*W)({0, nIn, 0, nOut}); // [nIn, nOut]
NDArray *Wux = (*W)({0, nIn, nOut, 2 * nOut}); // [nIn, nOut]
NDArray *Wrh = (*W)({nIn, nIn + nOut, 0, nOut}); // [nOut, nOut]
NDArray *Wuh = (*W)({nIn, nIn + nOut, nOut, 2 * nOut}); // [nOut, nOut]
NDArray *Wcx = (*Wc)({0, nIn, 0, 0}); // reset cell weights [nIn, nOut]
NDArray *Wch = (*Wc)({nIn, nIn + nOut, 0, 0}); // updates cell weights [nOut, nOut]
NDArray *br = (*b)({0, nOut}); // [nOut]
NDArray *bu = (*b)({nOut, 2 * nOut}); // [nOut]
// × means matrix multiplication
// * means element-wise product or so called Hadamard product
// r = sigmoid(x × Wrx + hI × Wrh + br)
auto xWrx = mmul(*x, *Wrx);
auto hIWrh = mmul(*hI, *Wrh);
auto* sum1 = *xWrx + *hIWrh;
auto* rAssign = (*sum1) + (*br);
delete sum1;
delete hIWrh;
r->assign(rAssign); // [bS, nIn] × [nIn, nOut] + [bS, nOut] × [nOut, nOut] + [nOut] = [bS, nOut]
delete rAssign;
r->applyTransform(transform::Sigmoid, r);
// u = sigmoid(x × Wux + hI × Wuh + bu)
auto xWux = mmul(*x, *Wux);
auto hIWuh = mmul(*hI, *Wuh);
auto* sum2 = *xWux + *hIWuh;
auto* uAssign = (*sum2) + (*bu);
delete sum2;
delete xWux;
u->assign(uAssign); // [bS, nIn] × [nIn, nOut] + [bS, nOut] × [nOut, nOut] + [nOut] = [bS, nOut]
delete uAssign;
u->applyTransform(transform::Sigmoid, u);
// c = tanh(x × Wcx + (r * hI) × Wch + bc)
auto* rTimesHi = (*r) * (*hI);
auto xWcx = mmul(*x, *Wcx);
auto rTimesHiWch = mmul(*rTimesHi, *Wch);
delete rTimesHi;
auto* sum3 = *xWcx + *rTimesHiWch;
auto* cAssign = (*sum3) + (*bc);
delete sum3;
delete xWcx;
delete rTimesHiWch;
c->assign(cAssign); // [bS, nIn] × [nIn, nOut] + [bS, nOut] × [nOut, nOut] + [nOut] = [bS, nOut]
delete cAssign;
c->applyTransform(transform::Tanh, c);
// h = (1 - u) * c + u * hI
auto* uTimesHi = (*u) * (*hI);
auto* oneMinusU = 1.f - (*u);
auto* oneMinusUTimesC = (*oneMinusU) * (*c);
delete oneMinusU;
auto* hAssign = (*uTimesHi) + (*oneMinusUTimesC);
delete uTimesHi;
delete oneMinusUTimesC;
h->assign(hAssign);
delete hAssign;
delete Wrx;
delete Wux;
delete Wrh;
delete Wuh;
delete Wcx;
delete Wch;
delete br;
delete bu;
}
//////////////////////////////////////////////////////////////////////////
void gruCell(NDArray* x, NDArray* hI, NDArray* Wx, NDArray* Wh, NDArray* b,
NDArray* gates, NDArray* h, bool linearBeforeReset) {
if(linearBeforeReset) {
THROW_EXCEPTION("GRU: Linear before reset not implemented. Please set to false.");
}
// Inputs:
// x input [bS, nIn]
// hI previous cell output [bS, nOut], that is at previous time step t-1
// Wx weights for x - [nIn, 3*nOut]
// Wh weights for h - [nOut, 3*nOut]
// b biases [3*nOut]
// 3*nOut means following sequence: reset, update, cell
// Outputs:
// gates [bS, 3*nOut] = reset gate [bS, nOut] + update gate [bS, nOut] + cell gate [bS, nOut]
// h current cell output [bS, nOut]
// formulas:
// zr = x × Wxr + hI × Whr + br
// zu = x × Wxu + hI × Whu + bu
// r = sigmoid(zr)
// u = sigmoid(zu)
// zc = x × Wxc + (r * hI) × Whc + bc
// c = tanh(zc)
// h = (1-u)*c + u*hI
const int bS = x->sizeAt(0);
const int nIn = x->sizeAt(1);
const int nOut = hI->sizeAt(1);
NDArray *gatesULike = gates->ulike();
NDArray temp = *gatesULike;
MmulHelper::mmul(x, Wx, &temp); // [bS, nIn] × [nIn, 3*nOut] = [bS, 3*nOut]
temp += *b;
MmulHelper::mmul(hI, Wh, gates); // [bS, nOut] × [nOut, 3*nOut] = [bS, 3*nOut]
NDArray *ru = (*gates)({0, 0, 0, 2 * nOut}); // [bS, 2*nOut]
NDArray *r = (*gates)({0, 0, 0, nOut}); // [bS, nOut]
NDArray *u = (*gates)({0, 0, nOut, 2 * nOut}); // [bS, nOut]
NDArray *c = (*gates)({0, 0, 2 * nOut, 3 * nOut}); // [bS, nOut]
NDArray *tempView1 = temp({0, 0, 0, 2 * nOut});
NDArray *tempView2 = temp({0, 0, 2 * nOut, 3 * nOut});
// reset and update gates
*ru += *tempView1;
ru->applyTransform(transform::Sigmoid, ru);
// cell gate
auto* cTimesR = (*c) * (*r);
auto* cAssign = (*cTimesR) + (*tempView2);
delete cTimesR;
c->assign(cAssign);
delete cAssign;
c->applyTransform(transform::Tanh, c);
// h = (1-u)*c + u*hI
auto* uTimesHi = (*u) * (*hI);
auto* oneMinusU = 1.f - (*u);
auto* oneMinusUTimesC = (*oneMinusU) * (*c);
delete oneMinusU;
auto* hAssign = (*uTimesHi) + (*oneMinusUTimesC);
delete uTimesHi;
delete oneMinusUTimesC;
h->assign(hAssign);
delete hAssign;
delete gatesULike;
delete ru;
delete r;
delete u;
delete c;
delete tempView1;
delete tempView2;
}
//////////////////////////////////////////////////////////////////////////
void gruTimeLoop(sd::LaunchContext* context, NDArray* x, NDArray* hI, NDArray* Wx, NDArray* Wh,
NDArray* b, NDArray* h, bool linearBeforeReset) {
// sL means time steps
// x input [sL, bS, nIn]
// hI initial cell output (at time step = 0) [bS, nOut]
// Wx input-to-hidden weights, [nIn, 3*nOut]
// Wh hidden-to-hidden weights, [nOut, 3*nOut]
// b biases, [3*nOut]
// h cell outputs at each time step [sL, bS, nOut]
const int sL = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int nOut = hI->sizeAt(1);
std::vector<LongType> shape = {bS, 3 * nOut};
NDArray gates(h->ordering(), shape, h->dataType(), context);
auto xSet = x->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nIn]
auto hSet = h->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nOut]
// time loop
for (int t = 0; t < sL; ++t) {
gruCell(xSet.at(t), t == 0 ? hI : hSet.at(t - 1), Wx, Wh, b, &gates, hSet.at(t), linearBeforeReset);
}
}
//////////////////////////////////////////////////////////////////////////
void gruCellBp(sd::LaunchContext* context, NDArray* x, NDArray* hLast, NDArray* W, NDArray* Wc,
NDArray* b, NDArray* bc, NDArray* dLdr, NDArray* dLdu, NDArray* dLdc,
NDArray* dLdh, NDArray* dLdx, NDArray* dLdhLast, NDArray* dLdW, NDArray* dLdWc, NDArray* dLdb,
NDArray* dLdbc) {
// Inputs:
// x input [bS, iS]
// hLast previous cell output [bS, nU], that is at previous time step t-1
// W weights - [iS+nU, 2*nU] - reset and update gates
// Wc C weights - [iS+nU, nU] - cell gate
// b r and u biases, [2*nU] - reset and update gates
// bc c biases, [nU] - cell gate
// dLdr gradient wrt reset gate, [bS, nU]
// dLdu gradient wrt update gate, [bS, nU]
// dLdc gradient wrt cell state, [bS, nU]
// dLdh gradient wrt current cell output, [bS, nU]
// Outputs:
// dLdx gradient wrt x, [bS, iS],
// dLdhLast gradient wrt hLast, [bS, nU]
// dLdW gradient wrt W, [iS+nU, 2*nU]
// dLdWc gradient wrt Wc, [iS+nU, nU]
// dLdb gradient wrt bru [2*nU]
// dLdbc gradient wrt bc [nU]
// * means element-wise product or so called Hadamard product
// × means matrix multiplication
/************************************************************************************************/
/******************************* THIS IS NOT OPTIMIZED CODE *************************************/
/*** aim is to have math-readable code in order to keep track of backprop formulas derivation ***/
const int bS = x->sizeAt(0);
const int iS = x->sizeAt(1);
const int nU = hLast->sizeAt(1);
NDArray *xT = x->transpose(); // [iS, bS]
NDArray *hLastT = hLast->transpose(); // [nU, bS]
NDArray *Wrx = (*W)({0, iS, 0, nU}); // [iS, nU]
NDArray *Wux = (*W)({0, iS, nU, 2 * nU}); // [iS, nU]
NDArray *Wrh = (*W)({iS, iS + nU, 0, nU}); // [nU, nU]
NDArray *Wuh = (*W)({iS, iS + nU, nU, 2 * nU}); // [nU, nU]
NDArray *Wcx = (*Wc)({0, iS, 0, 0}); // reset cell weights [iS, nU]
NDArray *Wch = (*Wc)({iS, iS + nU, 0, 0}); // updates cell weights [nU, nU]
NDArray *br = (*b)({0, nU}); // [nU]
NDArray *bu = (*b)({nU, 2 * nU}); // [nU]
NDArray *WrxT = Wrx->transpose(); // [nU, iS]
NDArray *WuxT = Wux->transpose(); // [nU, iS]
NDArray *WrhT = Wrh->transpose(); // [nU, nU]
NDArray *WuhT = Wuh->transpose(); // [nU, nU]
NDArray *WcxT = Wcx->transpose(); // [nU, iS]
NDArray *WchT = Wch->transpose(); // [nU, nU]
NDArray *dLdWrx = (*dLdW)({0, iS, 0, nU}); // [iS, nU]
NDArray *dLdWux = (*dLdW)({0, iS, nU, 2 * nU}); // [iS, nU]
NDArray *dLdWrh = (*dLdW)({iS, iS + nU, 0, nU}); // [nU, nU]
NDArray *dLdWuh = (*dLdW)({iS, iS + nU, nU, 2 * nU}); // [nU, nU]
NDArray *dLdWcx = (*dLdWc)({0, iS, 0, 0}); // [iS, nU]
NDArray *dLdWch = (*dLdWc)({iS, iS + nU, 0, 0}); // [nU, nU]
NDArray *dLdbr = (*dLdb)({0, nU}); // [nU]
NDArray *dLdbu = (*dLdb)({nU, 2 * nU}); // [nU]
// ***** feed forward step ***** //
// r = sigmoid(x × Wrx + hLast × Wrh + br)
auto xWrx = mmul(*x, *Wrx);
auto hLastWrh = mmul(*hLast, *Wrh);
auto* sum1 = *xWrx + *hLastWrh;
auto* rTemp = (*sum1) + (*br);
delete sum1;
NDArray r = *rTemp; // [bS, iS] × [iS, nU] + [bS, nU] × [nU, nU] + [nU] = [bS, nU]
delete rTemp;
r.applyTransform(transform::Sigmoid, &r);
// u = sigmoid(x × Wux + hLast × Wuh + bu)
auto xWux = mmul(*x, *Wux);
auto hLastWuh = mmul(*hLast, *Wuh);
auto* sum2 = *xWux + *hLastWuh;
auto* uTemp = (*sum2) + (*bu);
delete sum2;
NDArray u = *uTemp; // [bS, iS] × [iS, nU] + [bS, nU] × [nU, nU] + [nU] = [bS, nU]
delete uTemp;
delete xWux;
u.applyTransform(transform::Sigmoid, &u);
// c = tanh(x × Wcx + (r * hLast) × Wch + bc)
auto* rTimesHLast2 = r * (*hLast);
auto xWcx = mmul(*x, *Wcx);
auto rTimesHLast2Wch = mmul(*rTimesHLast2, *Wch);
delete rTimesHLast2;
auto* sum3 = *xWcx + *rTimesHLast2Wch;
auto* cTemp = (*sum3) + (*bc);
delete sum3;
delete xWcx;
delete rTimesHLast2Wch;
NDArray c = *cTemp; // [bS, iS] × [iS, nU] + [bS, nU] × [nU, nU] + [nU] = [bS, nU]
delete cTemp;
c.applyTransform(transform::Tanh, &c);
// h = (1 - u) * c + u * hPrev
// ***** back prop step ***** //
auto* hLastMinusC = (*hLast) - c;
auto* oneMinusU = 1.f - u;
auto* dudZu = u * (*oneMinusU);
delete oneMinusU;
auto* oneMinusR = 1.f - r;
auto* drdZr = r * (*oneMinusR);
delete oneMinusR;
auto* cSquared = c * c;
auto* oneMinusCSquared = 1.f - (*cSquared);
delete cSquared;
auto dcdZc = *oneMinusCSquared;
delete oneMinusCSquared;
auto* dLdZc = (*dLdc) * dcdZc;
auto* dLdZu = (*dLdu) * (*dudZu);
delete dudZu;
auto* dLdZr = (*dLdr) * (*drdZr);
delete drdZr;
NDArray *dhdc = 1.f - u; // [bS, nU]
NDArray dhdu = *hLastMinusC; // [bS, nU]
delete hLastMinusC;
// dLdx = dLdZu × WuxT + dLdZc × WcxT + dLdZr × WrxT
auto dLdZuWuxT = mmul(*dLdZu, *WuxT);
auto dLdZcWcxT = mmul(*dLdZc, *WcxT);
auto dLdZrWrxT = mmul(*dLdZr, *WrxT);
auto* temp1 = *dLdZuWuxT + *dLdZcWcxT;
auto* dLdxTemp = (*temp1) + *dLdZrWrxT;
delete temp1;
delete dLdZuWuxT;
delete dLdZcWcxT;
delete dLdZrWrxT;
dLdx->assign(dLdxTemp); // [bS, iS]
delete dLdxTemp;
// dldZTimeR = dLdZc * r
auto* dldZTimeR = (*dLdZc) * r;
// dLdhLast = dLdh * u + dLdZu × WuhT + dldZTimeR × WchT + dLdZr × WrhT
auto* dLdhTimesU = (*dLdh) * u;
auto dLdZuWuhT = mmul(*dLdZu, *WuhT);
auto dldZTimeRWchT = mmul(*dldZTimeR, *WchT);
auto dLdZrWrhT = mmul(*dLdZr, *WrhT);
auto* temp2 = (*dLdhTimesU) + *dLdZuWuhT;
delete dLdhTimesU;
delete dLdZuWuhT;
auto* temp3 = (*temp2) + *dldZTimeRWchT;
delete temp2;
delete dldZTimeRWchT;
auto* dLdhLastTemp = (*temp3) + *dLdZrWrhT;
delete temp3;
delete dLdZrWrhT;
dLdhLast->assign(dLdhLastTemp); // [bS, nU]
delete dLdhLastTemp;
// dLdWrx = xT × dLdZr
auto dLdWrxTemp = mmul(*xT, *dLdZr);
dLdWrx->assign(dLdWrxTemp); // [iS, bS] × [bS, nU] = [iS, nU]
delete dLdWrxTemp;
// dLdWrh = hLastT × dLdZr
auto dLdWrhTemp = mmul(*hLastT, *dLdZr);
dLdWrh->assign(dLdWrhTemp); // [nU, bS] × [bS, nU] = [nU, nU]
delete dLdWrhTemp;
// dLdWux = xT × dLdZu
auto dLdWuxTemp = mmul(*xT, *dLdZu);
dLdWux->assign(dLdWuxTemp); // [iS, bS] × [bS, nU] = [iS, nU]
delete dLdWuxTemp;
// dLdWuh = hLastT × dLdZu
auto dLdWuhTemp = mmul(*hLastT, *dLdZu);
dLdWuh->assign(dLdWuhTemp); // [nU, bS] × [bS, nU] = [nU, nU]
delete dLdWuhTemp;
// dLdWcx = xT × dLdZc
auto dLdWcxTemp = mmul(*xT, *dLdZc);
dLdWcx->assign(dLdWcxTemp); // [iS, bS] × [bS, nU] = [iS, nU]
delete dLdWcxTemp;
// dLdWch = (r * hLast)T × dLdZc
auto* rTimesHLast = r * (*hLast);
NDArray* rTimesHLastT = rTimesHLast->transpose();
delete rTimesHLast;
auto dLdWchTemp = mmul(*rTimesHLastT, *dLdZc);
dLdWch->assign(dLdWchTemp); // [nU, bS] × [bS, nU] = [nU, nU]
delete dLdWchTemp;
// Calculate reduction for bias gradients
std::vector<sd::LongType> zeroVec = {0};
auto* dLdbrTemp = dLdZr->reduceAlongDimension(reduce::Sum, &zeroVec);
dLdbr->assign(dLdbrTemp); // [nU]
delete dLdbrTemp;
auto* dLdbuTemp = dLdZu->reduceAlongDimension(reduce::Sum, &zeroVec);
dLdbu->assign(dLdbuTemp); // [nU]
delete dLdbuTemp;
auto* dLdbcTemp = dLdZc->reduceAlongDimension(reduce::Sum, &zeroVec);
dLdbc->assign(dLdbcTemp); // [nU]
delete dLdbcTemp;
delete dhdc;
delete dLdZc;
delete dLdZu;
delete dLdZr;
delete dldZTimeR;
delete Wrx;
delete Wux;
delete Wrh;
delete Wuh;
delete Wcx;
delete Wch;
delete br;
delete bu;
delete WrxT;
delete WuxT;
delete WrhT;
delete WuhT;
delete WcxT;
delete WchT;
delete dLdWrx;
delete dLdWux;
delete dLdWrh;
delete dLdWuh;
delete dLdWcx;
delete dLdWch;
delete dLdbr;
delete dLdbu;
delete xT;
delete hLastT;
delete rTimesHLastT;
}
//////////////////////////////////////////////////////////////////////////
void gruCellBp(sd::LaunchContext* context, NDArray* x, NDArray* hI, NDArray* Wx, NDArray* Wh,
NDArray* b, NDArray* dLdh, NDArray* gates, NDArray* dLdx, NDArray* dLdhI,
NDArray* dLdWx, NDArray* dLdWh, NDArray* dLdb) {
// Inputs:
// x input [bS, nIn]
// hI previous cell output [bS, nOut], that nIn at previous time step t-1
// Wx input-to-hidden weights - [nIn, 3*nOut]
// Wh hidden-to-hidden weights - [nOut, 3*nOut]
// b biases, [3*nOut] - reset and update gates
// dLdh gradient vs. ff output, [bS, nOut]
// Outputs:
// dLdx gradient vs. x, [bS, nIn],
// dLdhI gradient vs. hI, [bS, nOut]
// dLdWx gradient vs. W, [nIn, 3*nOut]
// dLdWh gradient vs. Wc, [nOut, 3*nOut]
// dLdb gradient vs. b [3*nOut]
// 3*nOut means following sequence: reset, update, cell
// * means element-wise product or so called Hadamard product
// × means matrix multiplication
const int nOut = hI->sizeAt(1);
NDArray *gatesULike = gates->ulike();
NDArray dLdz = *gatesULike; // [bS, 3*nOut]
NDArray *dLdzru = dLdz({0, 0, 0, 2 * nOut}); // [bS, 2*nOut]
NDArray *dLdzr = dLdz({0, 0, 0, nOut}); // [bS, nOut]
NDArray *dLdzu = dLdz({0, 0, nOut, 2 * nOut}); // [bS, nOut]
NDArray *dLdzc = dLdz({0, 0, 2 * nOut, 3 * nOut}); // [bS, nOut]
NDArray *r = (*gates)({0, 0, 0, nOut}); // [bS, nOut]
NDArray *u = (*gates)({0, 0, nOut, 2 * nOut}); // [bS, nOut]
NDArray *c = (*gates)({0, 0, 2 * nOut, 3 * nOut}); // [bS, nOut]
NDArray *WhView = (*Wh)({0, 0, 2 * nOut, 3 * nOut});
NDArray *WhcT = WhView->transpose();
if (dLdh) *dLdhI += *dLdh;
auto* oneMinusU = 1 - (*u); // [bS, nOut]
// dLdzc = dLdhI * (1-u) * (1-c²)
auto* cSquared = (*c) * (*c);
auto* oneMinusCSquared = 1.f - (*cSquared);
delete cSquared;
auto* temp1 = (*dLdhI) * (*oneMinusU);
auto* dLdzcTemp = (*temp1) * (*oneMinusCSquared);
delete temp1;
delete oneMinusCSquared;
dLdzc->assign(dLdzcTemp); // [bS, nOut]
delete dLdzcTemp;
// dLdzu = dLdhI * (hI - c) * u * (1-u)
auto* hIMinusC = (*hI) - (*c);
auto* uTimesOneMinusU = (*u) * (*oneMinusU);
auto* temp2 = (*dLdhI) * (*hIMinusC);
auto* dLdzuTemp = (*temp2) * (*uTimesOneMinusU);
delete temp2;
delete hIMinusC;
delete uTimesOneMinusU;
dLdzu->assign(dLdzuTemp); // [bS, nOut]
delete dLdzuTemp;
delete oneMinusU;
// dLdzr = (dLdzc * hI * r * (1-r)) × WhcT
auto* oneMinusR = 1 - (*r);
auto* rTimesOneMinusR = (*r) * (*oneMinusR);
delete oneMinusR;
auto* temp3 = (*dLdzc) * (*hI);
auto* temp4 = (*temp3) * (*rTimesOneMinusR);
delete temp3;
delete rTimesOneMinusR;
MmulHelper::mmul(temp4, WhcT, dLdzr); // [bS, nOut] x [nOut, nOut] = [bS, nOut]
delete temp4;
// dLdx = dLdz × WxT
NDArray *WxT = Wx->transpose();
MmulHelper::mmul(&dLdz, WxT, dLdx); // [bS, 3*nOut] x [3*nOut, nIn] = [bS, nIn]
// dLdWx += xT × dLdz
NDArray *xT = x->transpose();
auto dLdWxAdd = mmul(*xT, dLdz);
*dLdWx += *dLdWxAdd; // [nIn, bS] x [bS, 3*nOut] = [nIn, 3*nOut]
delete dLdWxAdd;
// dLdb += sum(dLdz, axis=0)
std::vector<sd::LongType> zeroVec = {0};
auto* dLdbAdd = dLdz.reduceAlongDimension(reduce::Sum, &zeroVec);
*dLdb += (*dLdbAdd); // [bS, 3*nOut] -> reduce -> [3*nOut];
delete dLdbAdd;
*dLdzc *= (*r);
// dLdhI = dLdhI * u + dLdz × WhT
NDArray *WhT = Wh->transpose();
auto* dLdhIU = (*dLdhI) * (*u);
auto mmulResult = mmul(dLdz, *WhT);
auto* dLdhIAssign = (*dLdhIU) + *mmulResult;
delete dLdhIU;
delete mmulResult;
dLdhI->assign(dLdhIAssign); // [bS, 3*nOut] x [3*nOut, nOut] = [bS, nOut]
delete dLdhIAssign;
// dLdWh += hIT × dLdz
NDArray *hITranspose = hI->transpose();
auto dLdWhAdd = mmul(*hITranspose, dLdz);
*dLdWh += *dLdWhAdd; // [nOut, bS] x [bS, 3*nOut] = [nOut, 3*nOut]
delete dLdWhAdd;
delete gatesULike;
delete dLdzru;
delete dLdzr;
delete dLdzu;
delete dLdzc;
delete r;
delete u;
delete c;
delete WhView;
delete WhcT;
delete hITranspose;
delete WhT;
delete xT;
delete WxT;
}
//////////////////////////////////////////////////////////////////////////
void gruTimeLoopBp(sd::LaunchContext* context, NDArray* x, NDArray* hI, NDArray* Wx,
NDArray* Wh, NDArray* b, NDArray* dLdh, NDArray* dLdx, NDArray* dLdhI,
NDArray* dLdWx, NDArray* dLdWh, NDArray* dLdb) {
// sL means time steps
// x input [sL, bS, nIn]
// hI initial cell output (at time step = 0) [bS, nOut]
// Wx input-to-hidden weights, [nIn, 3*nOut]
// Wh hidden-to-hidden weights, [nOut, 3*nOut]
// b biases, [3*nOut]
// dLdh gradient vs. ff output, [sL, bS, nOut]
// dLdx gradient vs. x, [sL, bS, nIn],
// dLdhI gradient vs. hI, [bS, nOut]
// dLdWx gradient vs. W, [nIn, 3*nOut]
// dLdWh gradient vs. Wc, [nOut, 3*nOut]
// dLdb gradient vs. b [3*nOut]
const int sL = x->sizeAt(0);
const int bS = x->sizeAt(1);
const int nOut = hI->sizeAt(1);
std::vector<sd::LongType> shape = {bS, 3 * nOut};
std::vector<sd::LongType> hShape = {sL + 1, bS, nOut};
NDArray gates(x->ordering(), shape, dLdh->dataType(), x->getContext());
NDArray h(x->ordering(), hShape, dLdh->dataType(), x->getContext());
auto xSet = x->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nIn]
auto dLdhSet = dLdh->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nOut]
auto hSet = h.allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nOut]
auto gatesSet = gates.allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nOut]
auto dLdxSet = dLdx->allTensorsAlongDimension({1, 2}); // sub-arrays with shape [bS, nIn]
hSet.at(0)->assign(hI);
// forward time loop
for (int t = 0; t < sL; ++t) gruCell(xSet.at(t), hSet.at(t), Wx, Wh, b, gatesSet.at(t), hSet.at(t + 1), false);
// backward time loop
for (int t = sL - 1; t >= 0; --t)
gruCellBp(context, xSet.at(t), hSet.at(t), Wx, Wh, b, dLdhSet.at(t), gatesSet.at(t), dLdxSet.at(t), dLdhI, dLdWx,
dLdWh, dLdb);
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,70 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author raver119@gmail.com
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_knn_mindistance)
#include <ops/declarable/helpers/knn.h>
namespace sd {
namespace ops {
namespace helpers {
template <typename T>
void mindistance_(const void *vinput, const void *vlow, const void *vhigh, int32_t length, void *vout) {
auto input = reinterpret_cast<const T *>(vinput);
auto low = reinterpret_cast<const T *>(vlow);
auto high = reinterpret_cast<const T *>(vhigh);
auto output = reinterpret_cast<T *>(vout);
T res = static_cast<T>(0.0f);
T po = static_cast<T>(2.f);
T o = static_cast<T>(1.f);
for (auto e = 0; e < length; e++) {
T p = input[e];
T l = low[e];
T h = high[e];
if (!(l <= p || h <= p)) {
if (p < l)
res += math::sd_pow<T, T, T>((p - o), po);
else
res += math::sd_pow<T, T, T>((p - h), po);
}
}
output[0] = math::sd_pow<T, T, T>(res, static_cast<T>(0.5f));
}
void knn_mindistance(NDArray&input, NDArray&lowest, NDArray&highest, NDArray &output) {
NDArray::preparePrimaryUse({&output}, {&input, &lowest, &highest});
BUILD_SINGLE_SELECTOR(input.dataType(), mindistance_,
(input.buffer(), lowest.buffer(), highest.buffer(), input.lengthOf(), output.buffer()),
SD_FLOAT_TYPES);
NDArray::registerPrimaryUse({&output}, {&input, &lowest, &highest});
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,134 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author sgazeos@gmail.com
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_listdiff)
#include <ops/declarable/helpers/listdiff.h>
#include <vector>
namespace sd {
namespace ops {
namespace helpers {
template <typename T>
static sd::LongType listDiffCount_(NDArray* values, NDArray* keep) {
sd::LongType saved = 0L;
for (sd::LongType e = 0; e < values->lengthOf(); e++) {
auto v = values->e<double>(e);
ExtraArguments extras({v, 0.0, 10.0});
auto idx = keep->indexReduceNumber(indexreduce::FirstIndex, &extras);
auto index = idx->e<sd::LongType>(0);
if (index < 0) saved++;
delete idx;
}
return saved;
}
sd::LongType listDiffCount(sd::LaunchContext* context, NDArray* values, NDArray* keep) {
auto xType = values->dataType();
NDArray::preparePrimaryUse({}, {values, keep});
BUILD_SINGLE_SELECTOR(xType, return listDiffCount_, (values, keep), SD_COMMON_TYPES);
NDArray::registerPrimaryUse({}, {values, keep});
return 0;
}
BUILD_SINGLE_TEMPLATE( sd::LongType listDiffCount_, (NDArray * values, NDArray* keep);, SD_COMMON_TYPES);
template <typename T>
static sd::Status listDiffFunctor_(NDArray* values, NDArray* keep, NDArray* output1, NDArray* output2) {
std::vector<T> saved;
std::vector<sd::LongType> indices;
for (sd::LongType e = 0; e < values->lengthOf(); e++) {
auto v = values->e<double>(e);
ExtraArguments extras({v, 0.0, 10.0});
NDArray *idxScalar = keep->indexReduceNumber(indexreduce::FirstIndex, &extras);
sd::LongType idx = idxScalar->e<sd::LongType>(0);
if (idx < 0) {
saved.emplace_back(v);
indices.emplace_back(e);
}
delete idxScalar;
}
if (saved.size() == 0) {
sd_printf("ListDiff: search returned no results", "");
THROW_EXCEPTION("Op validation failed");
} else {
auto z0 = output1;
auto z1 = output2;
if (static_cast<size_t>(z0->lengthOf()) != saved.size()) {
sd_printf("ListDiff: output/actual size mismatch", "");
THROW_EXCEPTION("Op validation failed");
}
if (static_cast<size_t>(z1->lengthOf()) != saved.size()) {
sd_printf("ListDiff: output/actual indices size mismatch", "");
THROW_EXCEPTION("Op validation failed");
}
memcpy(z0->buffer(), saved.data(), saved.size() * sizeof(T));
for (size_t e = 0; e < indices.size(); e++) {
z1->p(e, indices[e]);
}
}
return sd::Status::OK;
}
sd::Status listDiffFunctor(sd::LaunchContext* context, NDArray* values, NDArray* keep, NDArray* output1,
NDArray* output2) {
auto xType = values->dataType();
NDArray::preparePrimaryUse({output1, output2}, {values, keep});
sd::Status result = sd::Status::OK;
if (DataTypeUtils::isR(xType)) {
BUILD_SINGLE_SELECTOR(xType, result = listDiffFunctor_, (values, keep, output1, output2), SD_FLOAT_TYPES);
} else if (DataTypeUtils::isZ(xType)) {
BUILD_SINGLE_SELECTOR(xType, result = listDiffFunctor_, (values, keep, output1, output2), SD_INTEGER_TYPES);
} else {
return sd::Status::KERNEL_FAILURE;
}
NDArray::registerPrimaryUse({output1, output2}, {values, keep});
return result;
}
BUILD_SINGLE_TEMPLATE( sd::Status listDiffFunctor_,
(NDArray * values, NDArray* keep, NDArray* output1, NDArray* output2);
, SD_FLOAT_TYPES);
BUILD_SINGLE_TEMPLATE( sd::Status listDiffFunctor_,
(NDArray * values, NDArray* keep, NDArray* output1, NDArray* output2);
, SD_INTEGER_TYPES);
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,158 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* * under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
//
// @author Yurii Shyrma, created on 14.02.2018
//
// implementation of operation for LSTM cell with peep hole connections:
// http://www.bioinf.jku.at/publications/older/2604.pdf
// S. Hochreiter and J. Schmidhuber. "Long Short-Term Memory". Neural Computation, 9(8):1735-1780, 1997.
// and
// https://research.google.com/pubs/archive/43905.pdf
// Hasim Sak, Andrew Senior, and Francoise Beaufays. "Long short-term memory recurrent neural network architectures for
// large scale acoustic modeling." INTERSPEECH, 2014.
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_lstm)
#include <array/NDArrayList.h>
#include <graph/VariableSpace.h>
#include <helpers/MmulHelper.h>
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/legacy_helpers.h>
#include <ops/declarable/helpers/lstm.h>
#include <ops/declarable/helpers/transforms.h>
#include <iterator>
namespace sd {
namespace ops {
namespace helpers {
/////////////////////////////////////////////////////////////////////////////
void lstmBlockTimeLoop(NDArray* maxSeqLength, NDArray* xSeq, NDArray* c0, NDArray* y0,
NDArray* W, NDArray* Wci, NDArray* Wcf, NDArray* Wco, NDArray* b,
NDArray* iSeq, NDArray* cSeq, NDArray* fSeq, NDArray* oSeq,
NDArray* zSeq, NDArray* hSeq, NDArray* ySeq, const std::vector<double>& params,
const int dataFormat) {
int seqLen, bS, nIn, nOut;
if (dataFormat == 0) {
seqLen = xSeq->sizeAt(0);
bS = xSeq->sizeAt(1);
nIn = xSeq->sizeAt(2);
nOut = iSeq->sizeAt(2);
} else if (dataFormat == 1) {
seqLen = xSeq->sizeAt(2);
bS = xSeq->sizeAt(0);
nIn = xSeq->sizeAt(1);
nOut = iSeq->sizeAt(1);
} else if (dataFormat == 2) {
seqLen = xSeq->sizeAt(1);
bS = xSeq->sizeAt(0);
nIn = xSeq->sizeAt(2);
nOut = iSeq->sizeAt(2);
}
const std::vector<sd::LongType> inSliceShape({bS, nIn});
const std::vector<sd::LongType> outSliceShape({bS, nOut});
auto c_t1 = const_cast<NDArray*>(c0);
auto y_t1 = const_cast<NDArray*>(y0);
// loop through time steps
for (int t = 0; t < seqLen; ++t) {
auto xt = timeSubset(xSeq, t, dataFormat);
auto it = timeSubset(iSeq, t, dataFormat);
auto ct = timeSubset(cSeq, t, dataFormat);
auto ft = timeSubset(fSeq, t, dataFormat);
auto ot = timeSubset(oSeq, t, dataFormat);
auto zt = timeSubset(zSeq, t, dataFormat);
auto ht = timeSubset(hSeq, t, dataFormat);
auto yt = timeSubset(ySeq, t, dataFormat);
helpers::lstmBlockCell(xt, c_t1, y_t1, W, Wci, Wcf, Wco, b, it, ct, ft, ot, zt, ht, yt, params);
if (t != 0) {
delete c_t1;
delete y_t1;
}
if (t < seqLen - 1) {
c_t1 = ct;
y_t1 = yt;
} else {
delete ct;
delete yt;
}
delete it;
delete ft;
delete ot;
delete zt;
delete ht;
}
}
//////////////////////////////////////////////////////////////////////////
void lstmTimeLoop(sd::LaunchContext* context, NDArray* x, NDArray* h0, NDArray* c0, NDArray* Wx,
NDArray* Wh, NDArray* Wc, NDArray* Wp, NDArray* b, NDArray* h, NDArray* c,
const std::vector<double>& params) {
// x input [time x bS x nIn]
// h0 initial cell output (at time step = 0) [bS x numProj], in case of projection=false -> numProj == numUnits !!!
// c0 initial cell state (at time step = 0) [bS x numUnits],
// Wx input-to-hidden weights, [nIn x 4*numUnits]
// Wh hidden-to-hidden weights, [numProj x 4*numUnits]
// Wc diagonal weights for peephole connections [3*numUnits]
// Wp projection weights [numUnits x numProj]
// b biases, [4*numUnits]
// h cell outputs [time x bS x numProj], that is per each time step
// c cell states [time x bS x numUnits] that is per each time step
const int time = x->sizeAt(0);
NDArray currentH(*h0);
NDArray currentC(*c0);
// loop through time steps
for (int t = 0; t < time; ++t) {
auto xt = (*x)({t, t + 1, 0, 0, 0, 0});
auto ht = (*h)({t, t + 1, 0, 0, 0, 0});
auto ct = (*c)({t, t + 1, 0, 0, 0, 0});
helpers::lstmCell(context, xt, &currentH, &currentC, Wx, Wh, Wc, Wp, b, ht, ct, params);
currentH.assign(ht);
currentC.assign(ct);
delete ht;
delete ct;
}
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,73 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author sgazeos@gmail.com
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_unique)
#include <ops/declarable/CustomOperations.h>
#include <ops/declarable/helpers/multiUnique.h>
namespace sd {
namespace ops {
namespace helpers {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool multiUnique(std::vector<NDArray*> const& inputList, sd::memory::Workspace* workspace) {
sd::LongType length = 0;
std::vector<NDArray*> reshaped(inputList.size());
int pos = 0;
sd::LongType axis = 0;
Context cContext(1);
for (auto array : inputList) {
if (array->dataType() != sd::DataType::INT32)
THROW_EXCEPTION("multiUnique: this op support INT32 data type only.");
std::vector<sd::LongType> reshape = {-1};
reshaped[pos] = array->reshape(array->ordering(), reshape);
cContext.setInputArray(pos, reshaped[pos]);
length += array->lengthOf();
pos++;
}
std::vector<LongType> shape = {length};
NDArray arrayFull('c',shape, sd::DataType::INT32, inputList[0]->getContext());
cContext.setOutputArray(0, &arrayFull);
cContext.setIArguments(&axis, 1);
sd::ops::concat opConcat;
auto cResult = opConcat.execute(&cContext);
if (sd::Status::OK != cResult) THROW_EXCEPTION("multiUnique: cannot execute concat op properly.");
sd::ops::unique opUnique;
auto uResult = opUnique.evaluate({&arrayFull});
if (sd::Status::OK != uResult.status()) THROW_EXCEPTION("multiUnique: cannot execute unique op properly.");
auto uniqueVals = uResult.at(0);
bool res = uniqueVals->lengthOf() == arrayFull.lengthOf();
return res;
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,125 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author Yurii Shyrma (iuriish@yahoo.com), created on 16.04.2018
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_rnn)
// function nnCell implements an Elman RNN cell: output = activation(Wx*x + bx + Wh*ht + bh)
#include <helpers/BlasHelper.h>
#include <ops/declarable/helpers/rnn.h>
namespace sd {
namespace ops {
namespace helpers {
//////////////////////////////////////////////////////////////////////////
void rnnCell(sd::LaunchContext* context, NDArray* xt, NDArray* Wx, NDArray* Wh, NDArray* b,
NDArray* hPrev, NDArray* ht) {
// xt input [bS x iS]
// Wx input-to-hidden weights, [iS x nU]
// Wh hidden-to-hidden weights, [nU x nU]
// b biases, [2*nU]: {0, nU} are input-to-hidden biases and {nU, 2*nU} are hidden-to-hidden biases
// hPrev previous cell output [bS x nU], that is at previous time step t-1, in case of projection=false -> nU=nU!!!
const int nU = hPrev->sizeAt(1);
// ht is current cell output [bS x nU], that is at current time step t
NDArray *bFirst = (*b)({{0, nU}});
NDArray *bSecond = (*b)({{nU, 2 * nU}});
NDArray *mmulOne = mmul(*xt, *Wx);
NDArray *mmulTwo = mmul(*hPrev, *Wh);
// Chain additions with proper dereferencing since operators return NDArray*
NDArray *temp1 = (*mmulOne) + (*bFirst);
NDArray *temp2 = (*temp1) + (*mmulTwo);
NDArray *temp3 = (*temp2) + (*bSecond);
ht->assign(temp3); // [bS x nU] + [nU] + [bS x nU] + [nU] = [bS x nU]
ht->applyTransform(transform::Tanh, ht);
// Clean up intermediate results
delete temp1;
delete temp2;
delete temp3;
delete mmulOne;
delete mmulTwo;
delete bFirst;
delete bSecond;
}
//////////////////////////////////////////////////////////////////////////
void rnnTimeLoop(sd::LaunchContext* context, NDArray* x, NDArray* Wx, NDArray* Wh, NDArray* b,
NDArray* h0, NDArray* maxTimeStep, NDArray* h, NDArray* hFinal) {
// x input [time x bS x iS]
// Wx input-to-hidden weights, [iS x nU]
// Wh hidden-to-hidden weights, [nU x nU]
// b biases for, [2*nU]
// h0 initial cell output (at time step = 0) [bS x nU]
// maxTimeStep vector [bS] containing integer values within [0,time), each element of this vector set max time step
// per each input in batch, this means there are no calculations for time >= maxTimeStep
const int time = x->sizeAt(0);
const int bS = x->sizeAt(1);
// at first time step
if (h0)
hFinal->assign(h0);
else
*hFinal = 0.;
BlasHelper::getInstance(); // to avoid memory leak in pragma parallel loops
// loop through batch of inputs
for (int e = 0; e < bS; ++e) {
// loop through time steps
for (int t = 0; t < time; ++t) {
int maxStep = maxTimeStep ? maxTimeStep->e<int>(e) : time;
NDArray *xt = (*x)({t, t + 1, e, e + 1, 0, 0}, true);
NDArray *ht = (*h)({t, t + 1, e, e + 1, 0, 0}, true);
NDArray *hPrev = (*hFinal)({e, e + 1, 0, 0}, true); // previous state
if (t >= maxStep) {
*ht = 0.;
NDArray *hPrevAssign = (*h)({maxStep - 1, maxStep, e, e + 1, 0, 0});
if (maxStep != 0) hPrev->assign(hPrevAssign);
delete hPrevAssign;
} else {
helpers::rnnCell(context, xt, Wx, Wh, b, hPrev, ht);
hPrev->assign(ht);
}
delete xt;
delete ht;
delete hPrev;
}
}
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,133 @@
/*
* ******************************************************************************
* *
* *
* * This program and the accompanying materials are made available under the
* * terms of the Apache License, Version 2.0 which is available at
* * https://www.apache.org/licenses/LICENSE-2.0.
* *
* * See the NOTICE file distributed with this work for additional
* * information regarding copyright ownership.
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* * License for the specific language governing permissions and limitations
* * under the License.
* *
* * SPDX-License-Identifier: Apache-2.0
* *****************************************************************************
*/
//
// @author raver119@gmail.com
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_compat_sparse_to_dense)
#include <helpers/ShapeUtils.h>
#include <helpers/StringUtils.h>
#include <ops/declarable/helpers/sparse_to_dense.h>
#include <system/selective_rendering.h>
namespace sd {
namespace ops {
namespace helpers {
template <typename X, typename I>
static void fill_(const void *vvalues, const void *vindices, void *voutput, const LongType *zShapeInfo,
uint8_t rank, uint64_t length) {
auto values = reinterpret_cast<const X *>(vvalues);
auto indices = reinterpret_cast<const I *>(vindices);
auto output = reinterpret_cast<X *>(voutput);
LongType coords[SD_MAX_RANK];
uint64_t pos = 0;
for (uint64_t e = 0L; e < length; e++) {
// indices come in blocks
for (uint8_t p = 0; p < rank; p++) {
coords[p] = indices[pos++];
}
// fill output at given coords with sparse value
LongType offset;
COORDS2INDEX(rank, shape::stride(zShapeInfo), coords, offset);
output[offset] = values[e];
}
}
void compat_sparse_to_dense(NDArray& values, NDArray& indices, NDArray* def, NDArray& output) {
// make sure host buffer is updated
auto rank = output.rankOf();
if (output.isS()) {
NDArray::preparePrimaryUse({&output}, {&values, &indices, def});
// string case is not so trivial, since elements might, and probably will, have different sizes
auto numValues = values.lengthOf();
auto numElements = output.lengthOf();
// first of all we calculate final buffer sizes and offsets
auto defaultLength = def == nullptr ? 0 : StringUtils::byteLength(*def);
auto valuesLength = StringUtils::byteLength(values);
auto bufferLength = defaultLength * (output.lengthOf() - numValues) + valuesLength;
auto headerLength = ShapeUtils::stringBufferHeaderRequirements(numElements);
// now we make sure our output buffer can hold results
output.dataBuffer()->expand(bufferLength + headerLength);
std::vector<LongType> outputCoords(rank);
std::vector<LongType> valueCoords(rank);
auto offsetsBuffer = output.bufferAsT<LongType>();
auto dataBuffer = reinterpret_cast<uint8_t*>(offsetsBuffer + output.lengthOf());
offsetsBuffer[0] = 0;
// getting initial value coords
for (int e = 0; e < rank; e++) valueCoords[e] = indices.e<LongType>(e);
// write results individually
for (LongType e = 0; e < numElements; e++) {
LongType vIndex;
COORDS2INDEX(rank, shape::stride(output.shapeInfo()), valueCoords.data(), vIndex);
auto cLength = 0L;
std::string str;
if (vIndex == e) {
// we're writing down sparse value here
str = values.e<std::string>(e);
} else {
// we're writing down default value if it exists
if (def != nullptr)
str = def->e<std::string>(0);
else
str = "";
}
// TODO: make it unicode compliant
memcpy(&dataBuffer[offsetsBuffer[e]], str.c_str(), str.length());
// writing down offset
offsetsBuffer[e + 1] = cLength;
}
NDArray::registerPrimaryUse({&output}, {&values, &indices, def});
} else {
// numeric case is trivial, since all elements have equal sizes
// write out default values, if they are present
if (def != nullptr) {
output.assign(def);
}
NDArray::preparePrimaryUse({&output}, {&values, &indices});
// write out values
auto valuesDType = values.dataType();
auto indicesDataType = indices.dataType();
BUILD_DOUBLE_SELECTOR(
values.dataType(), indices.dataType(), fill_,
(values.buffer(), indices.buffer(), output.buffer(), output.shapeInfo(), rank, values.lengthOf()),
SD_COMMON_TYPES, SD_INDEXING_TYPES);
NDArray::registerPrimaryUse({&output}, {&values, &indices});
}
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,60 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author Yurii Shyrma (iuriish@yahoo.com)
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_sqrtm)
#include <helpers/Sqrtm.h>
#include <ops/declarable/CustomOperations.h>
namespace sd {
namespace ops {
namespace helpers {
//////////////////////////////////////////////////////////////////////////
template <typename T>
static void sqrtm_(NDArray* x, NDArray* z) {
if (x->rankOf() == 2) {
Sqrtm<T>::calc(*x, *z);
} else {
auto listX = x->allTensorsAlongDimension({-2, -1});
auto listZ = z->allTensorsAlongDimension({-2, -1});
auto func = PRAGMA_THREADS_FOR {
for (auto i = start; i < stop; i++) Sqrtm<T>::calc(*listX.at(i), *listZ.at(i));
};
samediff::Threads::parallel_tad(func, 0, listX.size());
}
}
//////////////////////////////////////////////////////////////////////////
void sqrtm(LaunchContext* context, NDArray* x, NDArray* z) {
x->syncToHost();
BUILD_SINGLE_SELECTOR(z->dataType(), sqrtm_, (x, z), SD_FLOAT_TYPES);
z->syncToDevice();
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,117 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// @author sgazeos@gmail.com
//
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_unique)
#include <execution/Threads.h>
#include <graph/Variable.h>
#include <ops/declarable/helpers/unique.h>
namespace sd {
namespace ops {
namespace helpers {
template <typename T>
static LongType uniqueCount_(NDArray* input) {
LongType count = 0;
std::vector<T> values;
for (LongType e = 0; e < input->lengthOf(); e++) {
T v = input->e<T>(e);
if (std::find(values.begin(), values.end(), v) == values.end()) {
values.push_back(v);
count++;
}
}
return count;
}
LongType uniqueCount(LaunchContext* context, NDArray* input) {
BUILD_SINGLE_SELECTOR(input->dataType(), return uniqueCount_, (input), SD_COMMON_TYPES);
return 0;
}
BUILD_SINGLE_TEMPLATE( sd::LongType uniqueCount_, (NDArray * input), SD_COMMON_TYPES);
template <typename T>
static Status uniqueFunctor_(NDArray* input, NDArray* values, NDArray* indices, NDArray* counts) {
std::vector<T> valuesVector;
SD_MAP_IMPL<T, int> indicesMap;
SD_MAP_IMPL<T, int> countsMap;
for (LongType e = 0; e < input->lengthOf(); e++) {
T v = input->e<T>(e);
if (std::find(valuesVector.begin(), valuesVector.end(), v) == valuesVector.end()) {
valuesVector.push_back(v);
indicesMap[v] = e;
countsMap[v] = 1;
} else {
countsMap[v]++;
}
}
auto func = PRAGMA_THREADS_FOR {
for (auto e = start; e < stop; e++) {
values->p(e, static_cast<T>(valuesVector[e]));
if (counts != nullptr) counts->p(e, countsMap[valuesVector[e]]);
}
};
samediff::Threads::parallel_for(func, 0, values->lengthOf());
for (LongType e = 0; e < indices->lengthOf(); e++) {
auto posI = std::find(valuesVector.begin(), valuesVector.end(), input->e<T>(e));
auto dist = std::distance(valuesVector.begin(), posI);
indices->p(e, LongType(dist)); // indicesMap[(*input)(e)];
}
return Status::OK;
}
Status uniqueFunctor(LaunchContext* context, NDArray* input, NDArray* values, NDArray* indices,
NDArray* counts) {
input->syncToHost();
values->syncToHost();
indices->syncToHost();
if (counts != nullptr) counts->syncToHost();
BUILD_SINGLE_SELECTOR(input->dataType(), return uniqueFunctor_, (input, values, indices, counts), SD_COMMON_TYPES);
input->syncToDevice();
values->syncToDevice();
indices->syncToDevice();
if (counts != nullptr) counts->syncToDevice();
return sd::Status::KERNEL_FAILURE;
}
BUILD_SINGLE_TEMPLATE( sd::Status uniqueFunctor_,
(NDArray * input, NDArray* values, NDArray* indices, NDArray* counts), SD_COMMON_TYPES);
} // namespace helpers
} // namespace ops
} // namespace sd
#endif
@@ -0,0 +1,70 @@
/* ******************************************************************************
*
*
* This program and the accompanying materials are made available under the
* terms of the Apache License, Version 2.0 which is available at
* https://www.apache.org/licenses/LICENSE-2.0.
*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
* SPDX-License-Identifier: Apache-2.0
******************************************************************************/
//
// Created by raver119 on 24/09/18.
//
#include <array/NDArrayList.h>
#include <system/op_boilerplate.h>
#if NOT_EXCLUDED(OP_Where)
#include <ops/declarable/helpers/where.h>
namespace sd {
namespace ops {
namespace helpers {
template <typename T>
static void __where(NDArray &condition, NDArray &output, memory::Workspace *workspace) {
NDArrayList list(0, true);
sd::LongType cnt = 0;
for (sd::LongType e = 0; e < condition.lengthOf(); e++) {
sd::LongType coords[SD_MAX_RANK];
INDEX2COORDS(e, condition.rankOf(), condition.shapeOf(), coords);
sd::LongType offset;
COORDS2INDEX(condition.rankOf(), shape::stride(condition.shapeInfo()), coords, offset);
if (condition.e<bool>(offset)) {
std::vector<sd::LongType> arrShape = {1, condition.rankOf()};
auto array = NDArrayFactory::create_('c', arrShape, output.dataType(), output.getContext());
for (sd::LongType f = 0; f < condition.rankOf(); f++) {
array->p(f, (T)coords[f]);
}
list.write(cnt++, array);
}
}
auto s = list.stack();
output.assign(s);
delete s;
}
BUILD_SINGLE_TEMPLATE( void __where, (NDArray & condition, NDArray &output, memory::Workspace *workspace),
SD_COMMON_TYPES);
void _where(sd::LaunchContext *context, NDArray &condition, NDArray &output, memory::Workspace *workspace) {
condition.syncToHost();
BUILD_SINGLE_SELECTOR(output.dataType(), __where, (condition, output, workspace), SD_COMMON_TYPES);
output.syncToDevice();
}
} // namespace helpers
} // namespace ops
} // namespace sd
#endif