// // Semigroups package for GAP // Copyright (C) 2016 James D. Mitchell // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. //
// TODO(later) 1) if we use clear before resize maybe don't need fill // 2) in-place product
#include"bipart.hpp"
#include <algorithm> // for fill, min, max, all_of, max_element #include <cstddef> // for size_t, NULL #include <cstdint> // for uint32_t #include <string> // for string #include <thread> // for thread #include <type_traits> // for conditional<>::type #include <utility> // for pair, make_pair #include <vector> // for vector
// GAP headers #include"gap_all.h"
// libsemigroups headers #include"libsemigroups/bipart.hpp"// for Blocks, Bipartition, validate #include"libsemigroups/report.hpp"// for Reporter, etc #include"libsemigroups/timer.hpp"// for Timer #include"semigroups-config.hpp"// for SEMIGROUPS_KERNEL_DEBUG
#include"gapbind14/gapbind14.hpp"// for GAPBIND14_TRY
using libsemigroups::Bipartition; using libsemigroups::Blocks; using libsemigroups::REPORTER; using libsemigroups::detail::Timer;
//////////////////////////////////////////////////////////////////////////////// // GAP level functions ////////////////////////////////////////////////////////////////////////////////
// Create a bipartition from the list gap_blocks. The argument gap_blocks must // be a list which either represents: // // * the internal rep of a bipartition (list of positive integers such that // gap_blocks[i] is the index of the class containing i); or // // * the external rep of a bipartition (a list of lists of ints that // partition [-n ... -1] union [1 .. n]. // // BIPART_NC does only minimal checks, and doesn't fully check if its argument // is valid.
if (LEN_LIST(gap_blocks) != 0) { if (IS_LIST(ELM_LIST(gap_blocks, 1))) { // gap_blocks is a list of lists
nr_blocks = LEN_LIST(gap_blocks); for (size_t i = 1; i <= nr_blocks; i++) {
SEMIGROUPS_ASSERT(IS_LIST(ELM_LIST(gap_blocks, i)));
degree += LEN_LIST(ELM_LIST(gap_blocks, i));
}
blocks.resize(degree);
degree /= 2;
for (size_t i = 1; i <= nr_blocks; i++) {
Obj block = ELM_LIST(gap_blocks, i); for (size_t j = 1; j <= static_cast<size_t>(LEN_LIST(block)); j++) {
SEMIGROUPS_ASSERT(IS_INTOBJ(ELM_LIST(block, j))); int jj = INT_INTOBJ(ELM_LIST(block, j)); if (jj < 0) {
blocks[-jj + degree - 1] = i - 1;
} else {
nr_left_blocks = i;
blocks[jj - 1] = i - 1;
}
}
}
} else { // gap_blocks is the internal rep of a bipartition
blocks.reserve(LEN_LIST(gap_blocks)); for (size_t i = 1; i <= static_cast<size_t>(LEN_LIST(gap_blocks)) / 2;
i++) {
SEMIGROUPS_ASSERT(IS_INTOBJ(ELM_LIST(gap_blocks, i))
&& INT_INTOBJ(ELM_LIST(gap_blocks, i)) > 0);
uint32_t index = INT_INTOBJ(ELM_LIST(gap_blocks, i)) - 1;
blocks.push_back(index);
nr_blocks = (index > nr_blocks ? index : nr_blocks);
}
nr_left_blocks = nr_blocks + 1; for (size_t i = (static_cast<size_t>(LEN_LIST(gap_blocks)) / 2) + 1;
i <= static_cast<size_t>(LEN_LIST(gap_blocks));
i++) {
SEMIGROUPS_ASSERT(IS_INTOBJ(ELM_LIST(gap_blocks, i))
&& INT_INTOBJ(ELM_LIST(gap_blocks, i)) > 0);
uint32_t index = INT_INTOBJ(ELM_LIST(gap_blocks, i)) - 1;
blocks.push_back(index);
nr_blocks = (index > nr_blocks ? index : nr_blocks);
}
nr_blocks++;
}
}
// construct C++ object
Bipartition* x; // We could use Bipartition::make in the next line, but then we are repeating // the checks in the Semigroups package, which give more meaningful error // messages.
GAPBIND14_TRY(x = new Bipartition(Bipartition(blocks));)
x->set_number_of_left_blocks(nr_left_blocks);
x->set_number_of_blocks(nr_blocks);
return bipart_new_obj(x);
}
// Returns the external rep of a GAP bipartition, see description before // BIPART_NC for more details.
// Returns the permutation of the indices of the transverse blocks of (x ^ * y) // induced by (x ^ * y). Assumes that the left and right blocks of x and y are // equal.
for (size_t i = 0; i < deg; i++) { if (_BUFFER_size_t[xx->at(i + deg)] != static_cast<size_t>(-1)) {
blocks[i] = _BUFFER_size_t[xx->at(i + deg)];
} else {
_BUFFER_size_t[xx->at(i + deg)] = next;
blocks[i] = next;
next++;
}
}
size_t nr_left = next;
for (size_t i = 0; i < deg; i++) { if (_BUFFER_size_t[xx->at(i)] != static_cast<size_t>(-1)) {
blocks[i + deg] = _BUFFER_size_t[xx->at(i)];
} else {
_BUFFER_size_t[xx->at(i)] = next;
blocks[i + deg] = next;
next++;
}
}
Bipartition* out = new Bipartition(blocks);
out->set_number_of_blocks(next);
out->set_number_of_left_blocks(nr_left);
return bipart_new_obj(out);
}
// Returns a permutation mapping the indices of the right transverse blocks of // x and to the right transverse blocks of y. Assumes that x and y have equal // left blocks.
// Create a GAP blocks object from the list gap_blocks. The argument gap_blocks // must be a list which is the external rep of a GAP blocks object (a list of // lists of integers such that the absolute values of these lists partition // [1 .. n], transverse blocks are indicated by positive integers and // non-transverse by negative integers). // // BLOCKS_NC does only minimal checks, and doesn't fully check if its argument // is valid.
// Returns the degree of a GAP blocks from the C++ object. A blocks object // is of degree n if the union of the sets of absolute values of its blocks is // [1 .. n].
// Returns the rank of a GAP blocks from the C++ object. The rank of a blocks // object is the number of transverse blocks (equivalently the number of blocks // consisting of positive integers).
// prepare the _BUFFER_bool for detecting transverse fused blocks
_BUFFER_bool.clear();
_BUFFER_bool.resize(right->number_of_blocks() + 2 * left->number_of_blocks());
std::copy(right->cbegin_lookup(),
right->cend_lookup(),
_BUFFER_bool.begin() + left->number_of_blocks()); auto seen = _BUFFER_bool.begin() + right->number_of_blocks()
+ left->number_of_blocks();
// after the following line: // // 1) [_BUFFER_size_t.begin() .. _BUFFER_size_t.begin() + left_nr_blocks + // right_nr_blocks - 1] is the fuse table for left and right // // 2) _BUFFER_bool is a lookup for the transverse blocks of the fused left // and right
auto tab1 = _BUFFER_size_t.begin() + left->number_of_blocks()
+ right->number_of_blocks(); auto tab2 = _BUFFER_size_t.begin()
+ 2 * (left->number_of_blocks() + right->number_of_blocks());
// find new names for the signed blocks of right for (size_t i = 0; i < right->number_of_blocks(); i++) { if (right->is_transverse_block(i)) {
tab1[fuse_it(i + left->number_of_blocks())] = i;
}
}
std::vector<uint32_t> blocks(2 * left->degree());
size_t next = right->number_of_blocks();
for (size_t i = 0; i < left->degree(); i++) {
blocks[i] = (*right)[i];
size_t j = (*left)[i]; if (left->is_transverse_block(j)) {
blocks[i + left->degree()] = tab1[fuse_it(j)];
} else { if (tab2[j] == static_cast<size_t>(-1)) {
tab2[j] = next;
next++;
}
blocks[i + left->degree()] = tab2[j];
}
}
Bipartition* out = new Bipartition(blocks);
out->set_number_of_blocks(next);
out->set_number_of_left_blocks(right->number_of_blocks());
return bipart_new_obj(out);
}
// Returns the left blocks of BLOCKS_PROJ(blocks_gap) * x_gap where the latter // is a GAP bipartition.
Blocks* out_blocks = new Blocks(x->degree());
uint32_t next = 0;
uint32_t const n = x->degree(); for (uint32_t i = n; i < 2 * n; i++) {
uint32_t j = fuse_it(x->at(i) + blocks->number_of_blocks()); if (tab[j] == static_cast<size_t>(-1)) {
tab[j] = next;
next++;
}
out_blocks->set_block(i - n, tab[j]);
out_blocks->set_is_transverse_block(tab[j], _BUFFER_bool[j]);
} #ifdef SEMIGROUPS_KERNEL_DEBUG
libsemigroups::validate(*out_blocks); #endif return blocks_new_obj(out_blocks);
}
// Returns a GAP bipartition y such that if BLOCKS_LEFT_ACT(blocks_gap, x_gap) // = Y, then BLOCKS_LEFT_ACT(Y, y) = blocks_gap, and y acts on Y as the inverse // of x_gap on Y.
for (uint32_t i = 0; i < blocks->number_of_blocks(); i++) { if (blocks->is_transverse_block(i)) {
SEMIGROUPS_ASSERT(fuse_it(i) < blocks->number_of_blocks());
SEMIGROUPS_ASSERT(tab + fuse_it(i) < _BUFFER_size_t.end());
tab[fuse_it(i)] = i;
}
}
// find the left blocks of the output for (uint32_t i = 0; i < blocks->degree(); i++) {
out_blocks[i] = (*blocks)[i];
uint32_t j = fuse_it(x->at(i) + blocks->number_of_blocks()); if (j >= blocks->number_of_blocks() || tab[j] == static_cast<size_t>(-1)) {
out_blocks[i + x->degree()] = blocks->number_of_blocks(); // junk
} else {
out_blocks[i + x->degree()] = tab[j];
}
}
Bipartition* out = new Bipartition(out_blocks);
out->set_number_of_left_blocks(blocks->number_of_blocks());
return bipart_new_obj(out);
}
// Returns a GAP bipartition y such that if BLOCKS_RIGHT_ACT(blocks_gap, x_gap) // = Y, then BLOCKS_RIGHT_ACT(Y, y) = blocks_gap, and y acts on Y as the inverse // of x_gap on Y. // // We fuse <blocks_gap> with the left blocks of <x_gap> keeping track of // the signs of the fused classes. // // The left blocks of the output are then: // // 1) disconnected right blocks of <x_gap> (before fusing) // // 2) disconnected right blocks of <x_gap> (after fusing) // // 3) connected right blocks of <x_gap> (after fusing) // // both types 1+2 of the disconnected blocks are unioned into one left block of // the output with index <junk>. The connected blocks 3 of <x_gap> are given the // next // available index, if they have not been seen before. The table <tab1> keeps // track of which connected right blocks of <x_gap> have been seen before and // the // corresponding index in the output, i.e. <tab1[x]> is the index in <out> of // the // fused block with index <x>. // // The right blocks of the output are: // // 1) disconnected blocks of <blocks_gap>; or // // 2) connected blocks of <blocks_gap>. // // The disconnected blocks 1 are given the next available index, if they have // not // been seen before. The table <tab2> keeps track of which disconnected blocks // of // <blocks_gap> have been seen before and the corresponding index in the output, // i.e. // <tab2[x]> is the index in <out> of the disconnected block of <blocks_gap> // with // index <x>. The connected blocks 2 of <blocks_gap> is given the index // <tab1[x]> // where <x> is the fused index of the block.
// find the left blocks of the output for (uint32_t i = 0; i < blocks->degree(); i++) { if (x->at(i + x->degree()) < x->number_of_left_blocks()) {
uint32_t j = fuse_it(x->at(i + x->degree()) + blocks->number_of_blocks()); if (_BUFFER_bool[j]) { if (tab1[j] == static_cast<size_t>(-1)) {
tab1[j] = next;
next++;
}
out_blocks[i] = tab1[j]; continue;
}
} if (junk == static_cast<uint32_t>(-1)) {
junk = next;
next++;
}
out_blocks[i] = junk;
}
uint32_t out_nr_left_blocks = next;
// find the right blocks of the output
for (uint32_t i = blocks->degree(); i < 2 * blocks->degree(); i++) {
uint32_t j = (*blocks)[i - blocks->degree()]; if (blocks->is_transverse_block(j)) {
out_blocks[i] = tab1[fuse_it(j)];
} else { if (tab2[j] == static_cast<size_t>(-1)) {
tab2[j] = next;
next++;
}
out_blocks[i] = tab2[j];
}
}
Bipartition* out = new Bipartition(out_blocks);
out->set_number_of_left_blocks(out_nr_left_blocks);
out->set_number_of_blocks(next); return bipart_new_obj(out);
}
// The functions below do the Union-Find algorithm for finding the least // equivalence relation containing two other equivalence relations. These are // used by many of the functions above for blocks.
// Returns the class containing the number i (i.e. this is the Find part of // Union-Find). This strongly relies on everything being set up correctly.
staticinline size_t fuse_it(size_t i) { while (_BUFFER_size_t[i] < i) {
i = _BUFFER_size_t[i];
} return i;
}
// This function performs Union-Find to find the least equivalence relation // containing the equivalence relations starting at left_begin, and // right_begin (named left and right below). // // If it = left_begin or right_begin, then it must be an iterator such that: // // * it.end() >= it.begin() + deg // // * *(it.begin() + i) is the index of the block containing i in the // equivalence relation represented by it // // * left_nr_blocks/right_nr_blocks is the number of blocks in the // equivalence relation represented by left_begin/right_begin. // // * if we want to keep track of transverse blocks, then sign should be true, // otherwise it is false. // // After running fuse: // // 1) [_BUFFER_size_t.begin() .. _BUFFER_size_t.begin() + left_nr_blocks + // right_nr_blocks - 1] is the fuse table for left and right // // 2) If sign == true, then _BUFFER_bool is a lookup for the transverse blocks // of the fused left and right // // Note that _BUFFER_bool has to be pre-assigned with the correct values, i.e. // it must be at least initialized (and have the appropriate length).
//////////////////////////////////////////////////////////////////////////////// // Counting idempotents in regular *-semigroups of bipartitions ////////////////////////////////////////////////////////////////////////////////
// The class and functions below implement a parallel idempotent counting // method for regular *-semigroups of bipartitions.
// IdempotentCounter is a class for containing the data required for the // search for idempotents.
for (unpr_t index : _unprocessed[thread_id]) { if (tester(thread_id, index.first, index.first)) {
_vals[thread_id][index.second]++;
}
std::vector<size_t> comp = _scc[index.second]; auto begin = comp.begin() + _scc_pos[index.first] + 1; for (auto it = begin; it < comp.end(); it++) { if (tester(thread_id, index.first, *it)) {
_vals[thread_id][index.second] += 2;
}
}
}
REPORT_DEFAULT("finished in %llu", timer.string().c_str());
}
// This is basically the same as BLOCKS_E_TESTER, but is required because we // must have different temporary storage for every thread. bool tester(size_t thread_id, size_t i, size_t j) {
Blocks* left = _orbit[i];
Blocks* right = _orbit[j];
// prepare the _lookup for detecting transverse fused blocks
_lookup[thread_id].clear();
_lookup[thread_id].resize(right->number_of_blocks()
+ left->number_of_blocks());
std::copy(right->cbegin_lookup(),
right->cend_lookup(),
_lookup[thread_id].begin() + left->number_of_blocks());
// prepare the _fuse_tab
_fuse_tab[thread_id].clear();
_fuse_tab[thread_id].reserve(left->number_of_blocks()
+ right->number_of_blocks());
for (size_t k = 0; k < left->number_of_blocks() + right->number_of_blocks();
k++) {
_fuse_tab[thread_id].push_back(k);
}
for (auto left_it = left->cbegin(), right_it = right->cbegin();
left_it < left->cbegin() + left->degree();
left_it++, right_it++) {
size_t k = fuse_it(thread_id, *left_it);
size_t l = fuse_it(thread_id, *right_it + left->number_of_blocks());
if (k != l) { if (k < l) {
_fuse_tab[thread_id][l] = k; if (_lookup[thread_id][l]) {
_lookup[thread_id][k] = true;
}
} else {
_fuse_tab[thread_id][k] = l; if (_lookup[thread_id][k]) {
_lookup[thread_id][l] = true;
}
}
}
}
for (uint32_t k = 0; k < left->number_of_blocks(); k++) { if (left->is_transverse_block(k)) {
size_t l = fuse_it(thread_id, k); if (!_lookup[thread_id][l] || _seen[thread_id][l]) { returnfalse;
}
_seen[thread_id][l] = true;
}
} returntrue;
}
inline size_t fuse_it(size_t thread_id, size_t i) { while (_fuse_tab[thread_id][i] < i) {
i = _fuse_tab[thread_id][i];
} return i;
}
size_t _nr_threads;
thrds_size_t _fuse_tab;
thrds_bool_t _lookup;
size_t _min_scc;
std::vector<Blocks*> _orbit;
std::vector<size_t> _ranks;
thrds_size_t _scc;
std::vector<size_t> _scc_pos; // _scc_pos[i] is the position of _orbit[i] in its scc
thrds_bool_t _seen;
std::vector<std::thread> _threads;
thrds_unpr_t _unprocessed;
thrds_size_t _vals; // map from the scc indices to the rank of elements in that scc
};
Obj out = NEW_PLIST(T_PLIST_CYC, vals.size());
SET_LEN_PLIST(out, vals.size());
for (size_t i = 1; i <= vals.size(); i++) {
SET_ELM_PLIST(out, i, INTOBJ_INT(vals[i - 1]));
}
return out;
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.7Bemerkung:
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.