Files
cmc-finite-functions/main.cpp

711 lines
26 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <iostream>
#include <fstream>
#include <vector>
#include <deque>
#include <algorithm>
#include <array>
#include <map>
#include <set>
#include <unordered_set>
#include <utility>
#include <cstring>
#include <functional>
#include <list>
#include <cctype>
#include <thread>
#include <mutex>
#include <atomic>
#include <iterator>
#include "al_utility.hpp"
#include "finite_function.hpp"
using namespace std;
const CellType CUR_BASE = 3;
const int ARGS_COUNT = 2;
const string CLASSES_FILENAME = "classes.txt";
FiniteFunction<CUR_BASE> identical_x;
FiniteFunction<CUR_BASE> identical_y;
template <CellType BASE>
pair<FiniteFunction<BASE>, FiniteFunction<BASE>> get_identicals() {
if ( BASE == 3 )
return make_pair(
FiniteFunction<CUR_BASE>(string("000 111 222")),
FiniteFunction<CUR_BASE>(string("012 012 012"))
);
else if ( BASE == 4 )
return make_pair(
FiniteFunction<CUR_BASE>(string("0000 1111 2222 3333")),
FiniteFunction<CUR_BASE>(string("0123 0123 0123 0123"))
);
}
struct FunctionTask {
bool is_finished;
vector<FiniteFunction<CUR_BASE>> current;
};
template <size_t BASE>
class FixedIniter {
public:
// rules это отображение обязательных значений аргументов, к
// значениями функции, которые не должны при них изменяться
explicit FixedIniter(map<pair<size_t, size_t>, int> rules) : _rules(rules), _cur_values(BASE*BASE, 0) {
for (auto it = rules.begin(); it != rules.end(); ++it) {
size_t arg1 = it->first.first;
size_t arg2 = it->first.second;
int value = it->second;
_cur_values.at(FiniteFunction<BASE>::get_index(arg1, arg2)) = value;
_used_indexes.insert(FiniteFunction<BASE>::get_index(arg1, arg2));
}
}
int operator() (int first, int second) const {
return _cur_values[FiniteFunction<BASE>::get_index(first, second)];
}
// Возвращает true, пока может построить следующую функцию
// если не может это сделать, то возвращает false
bool set_next() {
bool is_overflow = true;
for (size_t i = 0; is_overflow; ++i) {
if ( i >= _cur_values.size() )
return false;
if ( _used_indexes.count(i) > 0 )
continue;
_cur_values.at(i) += 1;
bool is_overflow = (_cur_values.at(i) > static_cast<int>(BASE) - 1);
if ( is_overflow )
_cur_values.at(i) = 0;
else
return true;
}
throw "Wrong logic";
}
private:
set<size_t> _used_indexes;
map<pair<size_t, size_t>, int> _rules;
vector<int> _cur_values;
};
template <class Iterable>
void write_function_class(ofstream &f_out, Iterable begin, Iterable end) {
for (;begin != end; ++begin)
f_out << *begin << " ";
f_out << endl;
}
template <class ClassesContainer>
void append_classes(const ClassesContainer& classes) {
std::ofstream f_out(CLASSES_FILENAME.c_str(), ios_base::app);
for (const auto& func_class: classes) {
write_function_class(f_out, func_class.begin(), func_class.end());
}
f_out.close();
}
/*
template <int BASE>
void get_permutations(vector<array<int, BASE>> &permutations) {
permutations.clear();
array<int, BASE> cur_perm;
for (int i = 0; i < BASE; ++i)
cur_perm[i] = i;
do {
permutations.push_back(cur_perm);
} while ( next_permutation(cur_perm.begin(), cur_perm.end()) );
}
*/
template<class TripleArgsFiniteFunction>
bool is_one_arg_func(const TripleArgsFiniteFunction &h) {
bool is_equaled_x = true;
bool is_equaled_y = true;
bool is_equaled_z = true;
for (CellType x = 0; x < CUR_BASE; ++x)
for (CellType y = 0; y < CUR_BASE; ++y) {
for (CellType z = 0; z < CUR_BASE; ++z) {
is_equaled_x = is_equaled_x && (h(x,y,z) == x);
is_equaled_y = is_equaled_y && (h(x,y,z) == y);
is_equaled_z = is_equaled_z && (h(x,y,z) == z);
}
if ( !is_equaled_x && !is_equaled_y && !is_equaled_z )
return false;
}
return true;
}
template<class TripleArgsFiniteFunction>
bool is_projection(const TripleArgsFiniteFunction &h) {
bool is_projection = true;
for (CellType x = 0; x < CUR_BASE; ++x)
for (CellType y = 0; y < CUR_BASE; ++y) {
is_projection = (is_projection
&& h(x,x,y) == h(x,y,x)
&& h(x,y,x) == h(y,x,x)
&& h(y,x,x) == x);
}
return is_projection;
}
template<class TripleArgsFiniteFunction>
bool is_semiprojection(const TripleArgsFiniteFunction &h) {
bool is_equaled_x = true;
bool is_equaled_y = true;
bool is_equaled_z = true;
for (CellType x = 0; x < CUR_BASE; ++x)
for (CellType y = 0; y < CUR_BASE; ++y)
for (CellType z = 0; z < CUR_BASE; ++z) {
// если все различны, то не рассматриваем
if ( x != y && x != z && y != z )
continue;
is_equaled_x = is_equaled_x && (h(x,y,z) == x);
is_equaled_y = is_equaled_y && (h(x,y,z) == y);
is_equaled_z = is_equaled_z && (h(x,y,z) == z);
}
return is_equaled_x || is_equaled_y || is_equaled_z;
}
bool is_passed_rosenberg(const FiniteFunction<CUR_BASE> &f) {
auto h_1 = [f](const CellType x, const CellType y, CellType z) -> CellType {
return f( f(x,y), f(x,z) );
};
if ( !is_one_arg_func(h_1) ) {
if ( is_projection(h_1) || is_semiprojection(h_1) )
return false;
}
auto h_2 = [f](const CellType x, const CellType y, CellType z) -> CellType {
return f( f(x, y), f(z, x) );
};
if ( !is_one_arg_func(h_2) ) {
if ( is_projection(h_2) || is_semiprojection(h_2) )
return false;
}
auto h_3 = [f](const CellType x, const CellType y, CellType z) -> CellType {
return f( f(x,y), f(y,z) );
};
if ( !is_one_arg_func(h_3) ) {
if ( is_projection(h_3) || is_semiprojection(h_3) )
return false;
}
auto h_4 = [f](const CellType x, const CellType y, CellType z) -> CellType {
return f( f(x,y), f(z,y) );
};
if ( !is_one_arg_func(h_4) ) {
if ( is_projection(h_4) || is_semiprojection(h_4) )
return false;
}
return true;
}
template <CellType BASE>
vector<FiniteFunction<BASE>> get_funcs() {
size_t count = 0;
map<pair<size_t, size_t>, int> rules;
for (int i = 0; i < BASE; ++i)
rules[make_pair<size_t, size_t>(i,i)] = i;
FixedIniter<BASE> initer(rules);
//vector<array<int, BASE>> permutations;
//get_permutations<BASE>(permutations);
vector<FiniteFunction<BASE>> funcs;
set< FiniteFunction<BASE> > permutated_funcs;
do {
++count;
auto cur_func = FiniteFunction<BASE>(initer);
//if ( permutated_funcs.find(cur_func) != permutated_funcs.end() )
// continue;
// если закомментить, то 17 классов, вместо 20
//for (auto&& permutation: permutations)
// permutated_funcs.insert(cur_func.apply_permutation(permutation));
funcs.push_back(cur_func);
} while (initer.set_next());
cout << "Total " << count << " functions" << endl;
funcs.erase(
remove_if(
funcs.begin(),
funcs.end(),
[](const FiniteFunction<BASE> & f) {
return !is_passed_rosenberg(f);
}
),
funcs.end()
);
cout << "After Rosenberg " << funcs.size() << " functions" << endl;
//if ( permutated_funcs.size() != count )
// throw "Permutation's logic error!";
return funcs;
}
template <CellType BASE>
set<FiniteFunction<BASE>> generate_function_class(FiniteFunction<BASE> base_function) {
auto FiniteFunctionHasher = [](const FiniteFunction<BASE> &f) -> uint32_t {
return f.get_hash();
};
unordered_set<
FiniteFunction<CUR_BASE>,
decltype(FiniteFunctionHasher)
> func_class(1024, FiniteFunctionHasher);
func_class.insert(base_function);
//cout << "start with " << base_function << " ";
size_t last_size = 0; // размер в прошлой итерации
while (true) {
unordered_set<
FiniteFunction<CUR_BASE>,
decltype(FiniteFunctionHasher)
> new_funcs(1024, FiniteFunctionHasher);
for (const auto& f_main: func_class) {
new_funcs.insert(f_main.reversed());
//new_funcs.insert(f_main.equaled());
for (const auto& f_applied: func_class) {
//FiniteFunction<BASE> f_left_1, f_left_2;
//tie(f_left_1, f_left_2) = f_main.apply_to_first(f_applied);
FiniteFunction<BASE> f_left;
f_left = f_main.apply_to_first_partial(f_applied);
new_funcs.insert(f_left);
}
}
if ( new_funcs.size() == last_size ) {
break;
}
func_class.insert(new_funcs.begin(), new_funcs.end());
last_size = new_funcs.size();
}
return set<FiniteFunction<BASE>>(func_class.begin(), func_class.end());
}
// Возвращает новый функциональный класс, размером не сильно больше, чем max_size
// Вторым результатом возвращает true, если вычисления закончились успешно
// и false, если прервались по достижению max_size
template <CellType BASE>
pair<vector<FiniteFunction<BASE>>, bool> extend_function_class(
const vector<FiniteFunction<BASE>>& base_class,
size_t max_size
) {
if ( base_class.size() >= max_size ) {
return make_pair(
vector<FiniteFunction<BASE>>(base_class),
false
);
}
auto FiniteFunctionHasher = [](const FiniteFunction<BASE> &f) -> uint32_t {
return f.get_hash();
};
unordered_set<
FiniteFunction<CUR_BASE>,
decltype(FiniteFunctionHasher)
> func_class(1024, FiniteFunctionHasher);
for (auto&& base_function: base_class)
func_class.insert(base_function);
bool is_finished = false;
size_t last_size = 0; // размер в прошлой итерации
while (true) {
unordered_set<
FiniteFunction<CUR_BASE>,
decltype(FiniteFunctionHasher)
> new_funcs(1024, FiniteFunctionHasher);
for (const auto& f_main: func_class) {
new_funcs.insert(f_main.reversed());
for (const auto& f_applied: func_class) {
FiniteFunction<BASE> f_left;
f_left = f_main.apply_to_first_partial(f_applied);
new_funcs.insert(f_left);
}
}
new_funcs.erase(identical_x);
new_funcs.erase(identical_y);
if ( new_funcs.size() == last_size ) {
is_finished = true;
break;
}
func_class.insert(new_funcs.begin(), new_funcs.end());
last_size = new_funcs.size();
// слишком много насчитали -- выходим
if ( func_class.size() > max_size ) {
break;
}
}
vector<FiniteFunction<BASE>> res(func_class.begin(), func_class.end());
res.shrink_to_fit();
return make_pair(
res,
is_finished
);
}
template <class Iterable>
bool is_bad_class(
const Iterable& func_class,
const set<FiniteFunction<CUR_BASE>>& bad_funcs
) {
for (auto&& func: func_class)
if ( bad_funcs.find(func) != bad_funcs.end() )
return true;
return false;
}
size_t total_possible_functions;
atomic<long> completed_tasks;
atomic<long> tasks_to_extend; // количество тасков, которые не обработаны
list< vector<FiniteFunction<CUR_BASE>> > shared_function_classes;
mutex shared_functions_mutex;
deque<FunctionTask> task_list;
mutex task_mutex;
vector<FunctionTask> processed_task_list;
mutex processed_task_mutex;
set<FiniteFunction<CUR_BASE>> bad_functions;
atomic<int> current_max_coeff;
void do_work() {
std::chrono::milliseconds SLEEP_TIME(10);
while ( true ) {
FunctionTask task;
task_mutex.lock();
if ( task_list.begin() != task_list.end() ) {
task = task_list.front();
task_list.pop_front();
task_mutex.unlock();
} else {
task_mutex.unlock();
if ( completed_tasks < total_possible_functions ) {
// Не все таски, подождём, пока добавят ещё
std::this_thread::sleep_for(SLEEP_TIME);
continue;
} else {
cout << "thread " << this_thread::get_id() << ": "
<< " finished" << endl;
break;
}
}
tie(task.current, task.is_finished) = extend_function_class(
task.current,
get_math_coeff(current_max_coeff)
);
processed_task_mutex.lock();
processed_task_list.push_back(task);
processed_task_mutex.unlock();
--tasks_to_extend;
}
}
void process_task_lists() {
cout << "processing starts " << endl;
std::chrono::milliseconds SLEEP_TIME(10);
vector<FunctionTask> local_processed_tasks;
while ( completed_tasks < total_possible_functions ) {
if ( tasks_to_extend ) {
// подождём, пока не закончатся таски в очереди
std::this_thread::sleep_for(SLEEP_TIME);
continue;
}
task_mutex.lock();
if ( task_list.size() != 0 )
cout << "IMPOSSIBLE task_list.size!!" << endl;
task_list.clear();
task_list.shrink_to_fit();
task_mutex.unlock();
// опустошим выполненные таски
processed_task_mutex.lock();
for (auto && task: processed_task_list)
local_processed_tasks.push_back(task);
processed_task_list.clear();
processed_task_list.shrink_to_fit();
processed_task_mutex.unlock();
cout << "sorting finished of "<< local_processed_tasks.size() << endl;
size_t total_funcs = 0;
for (auto&& task: local_processed_tasks)
total_funcs += task.current.size();
cout << "estimated size: "
<< sizeof(FunctionTask) * total_funcs / 1024 / 1024 << " MB" << endl;
// Обеспечим увеличение размеров, чтобы не было проблем со включением одного
// в другое
sort(
local_processed_tasks.begin(),
local_processed_tasks.end(),
[](const FunctionTask& a, const FunctionTask& b) {
return a.current.size() < b.current.size();
}
);
// сохраним, чем должен был равняться максимальный размер
auto last_math_coeff = get_math_coeff(current_max_coeff);
// а для всех новых увеличим его
++current_max_coeff;
for ( auto&& task: local_processed_tasks ) {
// если в каких-то больше, чем положено, то не трогаем их
if ( task.current.size() > last_math_coeff)
break;
if ( !task.is_finished ) {
if ( is_bad_class(task.current, bad_functions) ) {
//cout << "bad class" << endl;
++completed_tasks;
if ( print_progress(completed_tasks, total_possible_functions) ) {
append_classes(shared_function_classes);
shared_function_classes.clear();
}
} else {
task_mutex.lock();
++tasks_to_extend;
task_list.push_back(task);
task_mutex.unlock();
}
} else {
//cout << "task finished, appending" << endl;
++completed_tasks;
if ( print_progress(completed_tasks, total_possible_functions) ) {
append_classes(shared_function_classes);
shared_function_classes.clear();
}
sort(task.current.begin(), task.current.end());
auto func_class = task.current;
bool is_need_append = true;
vector<decltype(shared_function_classes)::iterator> functions_to_remove;
is_need_append = !is_bad_class(task.current, bad_functions);
/*for (auto it = shared_function_classes.begin(); it != shared_function_classes.end(); ++it) {
if ( func_class.size() < it->size() ) {
if (includes(
it->begin(),
it->end(),
func_class.begin(),
func_class.end()
)
) {
cout << "includes!!" << endl;
// новый класс функций часть уже существующего
functions_to_remove.push_back(it);
}
} else {
if (includes(
func_class.begin(),
func_class.end(),
it->begin(),
it->end()
)
) {
// новый класс функций надмножество существующего
is_need_append = false;
break;
}
}
}*/
// Делаем плохими ВСЕ функции без учёта того, порждают ли они
// минимальный класс. Очень опасно! Должны гарантировать, что
// классы меньше быть не могут, потому что мы их всех уже
// перебрали
// иначе ДОЛЖНО быть в if ( is_need_append )
for (auto&& func: func_class)
bad_functions.insert(func);
if ( is_need_append ) {
shared_function_classes.push_back(func_class);
}
for (auto&& to_remove: functions_to_remove) {
shared_function_classes.erase(to_remove);
cout << "Removing class from shared_function_classes" << endl;
}
}
}
local_processed_tasks.erase(
remove_if(
local_processed_tasks.begin(),
local_processed_tasks.end(),
[last_math_coeff](const FunctionTask& task) {
return task.current.size() <= last_math_coeff;
}),
local_processed_tasks.end()
);
local_processed_tasks.shrink_to_fit();
// Поспим, чтобы не работать слишком часто
std::this_thread::sleep_for(SLEEP_TIME);
}
}
int main() {
tie(identical_x, identical_y) = get_identicals<CUR_BASE>();
cout << "sizeof FiniteFunction<" << (int)CUR_BASE << "> = "
<< sizeof(FiniteFunction<CUR_BASE>) << endl;
cout << "sizeof FunctionTask = "<< sizeof(FunctionTask) << endl;
auto FiniteFunctionHasher = [](const FiniteFunction<CUR_BASE> &f) -> uint32_t {
return f.get_hash();
};
auto THREADS_COUNT = max(static_cast<int>(thread::hardware_concurrency()), 2);
cout << "Using " << THREADS_COUNT << " threads" << endl;
auto funcs = get_funcs<CUR_BASE>();
cout << "Removing permutations " << funcs.size() << " functions" << endl;
/*vector<array<int, 3>> permutations;
get_permutations<3>(permutations);
string function_to_str("000 112 212");
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[1]) << endl;
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[2]) << endl;
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[3]) << endl;
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[4]) << endl;
cout << FiniteFunction<3>(function_to_str).apply_permutation(permutations[5]) << endl;
*/
//cout << "022_210_002 -> " << FiniteFunction<3>(string("022 210 002")) << endl;
list< set<FiniteFunction<CUR_BASE>> > function_classes;
unordered_set<
FiniteFunction<CUR_BASE>,
decltype(FiniteFunctionHasher)
> allowed_functions(funcs.begin(), funcs.end(), 128, FiniteFunctionHasher);
cout << identical_x.get_hash() << endl;
cout << identical_y.get_hash() << endl;
allowed_functions.erase(identical_x);
allowed_functions.erase(identical_y);
completed_tasks = 0;
current_max_coeff = 1;
for (auto&& func: allowed_functions) {
FunctionTask task;
task.current = {func};
task.is_finished = false;
task_list.push_back(task);
}
cout << "Total funcs in list " << task_list.size() << " functions" << endl;
total_possible_functions = task_list.size();
tasks_to_extend = task_list.size();
vector< thread > thread_pool;
thread task_processer(process_task_lists);
// удалим старый контент
std::ofstream f_out("classes.txt");
f_out.close();
for (int i = 0; i < THREADS_COUNT - 1; ++i)
thread_pool.push_back(thread(do_work));
for (auto&& t: thread_pool)
t.join();
task_processer.join();
cout << "Shared " << shared_function_classes.size() << " functions!" << endl;
// перегоняем список с классами в массив с классами
vector< vector<FiniteFunction<CUR_BASE>> > vector_classes(
shared_function_classes.begin(),
shared_function_classes.end()
);
/*// фильтруем перестановки
vector<array<int, CUR_BASE>> permutations;
get_permutations<CUR_BASE>(permutations);
map<size_t, vector<vector<set<FiniteFunction<CUR_BASE>>>::iterator> > classes_per_size;
for (auto it = vector_classes.begin(); it != vector_classes.end(); ++it) {
classes_per_size[it->size()].push_back(it);
}
set<vector<set<FiniteFunction<CUR_BASE>>>::iterator> classes_to_remove;
for (auto&& class_fixed_size: classes_per_size) {
typedef pair<FiniteFunction<CUR_BASE>,vector<set<FiniteFunction<CUR_BASE>>>::iterator> sorting_arr_type;
cout << class_fixed_size.first << " -> " <<
class_fixed_size.second.size() << endl;
vector< sorting_arr_type > all_class_elements;
for (auto &&cur_class_it: class_fixed_size.second) {
for (auto&& cur_fn: (*cur_class_it)) {
if ( (cur_fn == identical_x) || (cur_fn == identical_y) )
continue;
for (auto&& permutation: permutations)
all_class_elements.push_back(make_pair(
cur_fn.apply_permutation(permutation),
cur_class_it
));
}
}
cout << all_class_elements.size() << endl;
// сортируем массив
sort(all_class_elements.begin(),all_class_elements.end());
for (auto&& el: all_class_elements)
cout << el.first << "|";
// выбираем, кого удалить
for (size_t i = 0; i < all_class_elements.size(); ++i) {
decltype(classes_to_remove) cur_vals_to_remove;
size_t j;
for (
j = i;
(j < all_class_elements.size()) &&
(all_class_elements.at(i).first == all_class_elements.at(j).first);
++j
) {
cout << all_class_elements.at(i).first << "<->" <<
all_class_elements.at(j).first << " " <<
(all_class_elements.at(i).second == all_class_elements.at(j).second )
<<endl;
cur_vals_to_remove.insert(all_class_elements.at(j).second);
}
for (
auto it = next(cur_vals_to_remove.begin());
it != cur_vals_to_remove.end();
++it
) {
classes_to_remove.insert(*it);
}
cout << "size " << cur_vals_to_remove.size() << endl;
i = j;
}
}
cout << "classes_to_remove.size() = " <<classes_to_remove.size() << endl;
// удаляем
{
decltype(vector_classes) new_vector_classes;
for (auto it = vector_classes.begin(); it != vector_classes.end(); ++it)
if ( classes_to_remove.find(it) == classes_to_remove.end() )
new_vector_classes.push_back(*it);
swap(new_vector_classes, vector_classes);
}*/
append_classes(vector_classes);
return 0;
}