15#ifndef MCRL2_PBES_TOOLS_PBESCHAIN_H
16#define MCRL2_PBES_TOOLS_PBESCHAIN_H
18#include "mcrl2/core/identifier_string.h"
19#include "mcrl2/data/rewrite_strategy.h"
20#include "mcrl2/data/rewriter.h"
21#include "mcrl2/pbes/algorithms.h"
22#include "mcrl2/pbes/detail/instantiate_global_variables.h"
23#include "mcrl2/pbes/detail/iteration_builders.h"
24#include "mcrl2/pbes/detail/srf_transformations.h"
25#include "mcrl2/pbes/detail/stategraph_pbes.h"
26#include "mcrl2/pbes/io.h"
27#include "mcrl2/pbes/pbes_equation.h"
28#include "mcrl2/pbes/pbes_expression.h"
29#include "mcrl2/pbes/resolve_name_clashes.h"
30#include "mcrl2/pbes/rewrite.h"
31#include "mcrl2/pbes/srf_pbes.h"
32#include "mcrl2/pbes/unify_parameters.h"
33#include "mcrl2/utilities/logger.h"
44 data::rewrite_strategy rewrite_strategy =
data::rewrite_strategy::jitty;
65template<
template<
class>
class Builder>
103template<
template<
class>
class Builder>
120 data::application da = atermpp::down_cast<data::application>(x);
128 res = atermpp::down_cast<data::data_expression>(m_pbes_rewriter(atermpp::down_cast<pbes_expression>(res)));
138 result = atermpp::down_cast<T>(simpl_data(x));
142template<
template<
class>
class Builder>
183 data::data_expression_list pars = x.parameters();
184 for (
const data::variable& v: m_eq.variable().parameters())
186 data::data_expression par = pars.front();
188 if (par.sort() != v.sort())
196 pbes_expression p = pbes_rewrite(m_eq.formula(), m_pbes_rewriter, sigma);
197 std::set<propositional_variable_instantiation> set = find_propositional_variable_instantiations(p);
198 if (std::all_of(set.begin(),
200 [
this](
const propositional_variable_instantiation& v) {
return v.name() != m_eq.variable().name(); }))
203 mCRL2log(log::log_level_t::debug) <<
"Replaced in PBES equation for " << name <<
":\n" << x <<
" --> " << p <<
"\n";
208 mCRL2log(log::log_level_t::debug) <<
"No Replacement in PBES equation for " << name <<
":\n" << x <<
" --> " << p <<
"\n";
216template<
template<
class>
class Builder>
228 std::vector<detail::predicate_variable> predvars)
236 result = pbes_rewrite(pbes_expression(x), m_pbes_rewriter, predvars[i].sigma());
244template<
typename Container>
247 std::vector<propositional_variable_instantiation> result;
248 pbes_system::find_propositional_variable_instantiations(container, std::inserter(result, result.end()));
262 total_time += duration;
268 return std::chrono::duration<
double>(total_time).count();
284 gauss_elimination_direct_count++;
290 gauss_elimination_path_count++;
296 return std::chrono::steady_clock::now();
300 void end_measurement(
const std::string& label, std::chrono::time_point<std::chrono::steady_clock> measurement_start)
302 auto duration = std::chrono::duration_cast<std::chrono::duration<
double, std::milli>>(
303 std::chrono::steady_clock::now() - measurement_start);
304 measurements[label].add_measurement(duration);
311 = std::chrono::duration<
double>(std::chrono::high_resolution_clock::now() - start_time).count();
312 mCRL2log(log::log_level_t::verbose) <<
"Total time: " << total_elapsed <<
"s" << std::endl;
314 if (!measurements.empty())
317 std::size_t max_label_length = 0;
318 for (
const auto& [label, measurement]: measurements)
320 max_label_length = std::max(max_label_length, label.length() + 4);
324 for (
const auto& [label, measurement]: measurements)
326 mCRL2log(log::log_level_t::verbose) << std::left << std::setw(
static_cast<
int>(max_label_length)) << (label +
" ") <<
": " << std::right
327 << std::setw(2) << std::fixed << measurement.get_total_seconds() <<
"s ("
328 << measurement.call_count <<
" calls)" << std::endl;
332 mCRL2log(log::log_level_t::verbose) <<
"Gauss eliminations (path): " << gauss_elimination_path_count << std::endl;
333 mCRL2log(log::log_level_t::verbose) <<
"Gauss eliminations (direct): " << gauss_elimination_direct_count << std::endl;
338template<
typename Func>
341 auto start = timer.start_measurement();
342 if constexpr (std::is_same_v<
void,
decltype(func())>)
344 std::forward<Func>(func)();
345 timer.end_measurement(name, start);
349 auto result = std::forward<Func>(func)();
350 timer.end_measurement(name, start);
356 rewrite_if_builder<pbes_system::pbes_expression_builder>& if_substituter,
360 if_substituter.apply(res, res);
372 return !(options.pvi_pp_factor > 0.0) || ((
double)pp(phi).size() <= options.pvi_pp_factor * (
double)pp(cur_x).size());
383 mCRL2log(log::log_level_t::status) <<
"New number of pvi: " << initial_size <<
" --> " << current_size <<
"" << std::endl;
387 std::size_t initial_size,
388 substitute_propositional_variables_for_true_false_builder<pbes_system::pbes_expression_builder>& pvi_substituter,
389 rewrite_if_builder<pbes_system::pbes_expression_builder>& if_substituter,
399 mCRL2log(log::log_level_t::verbose) <<
"Starting substitution for equation " << equation.variable().name() <<
" with timeout "
400 << options.timeout <<
"s" << std::endl;
403 std::set<propositional_variable_instantiation> stable_set = {};
404 std::size_t previous_size = initial_size;
405 std::size_t current_size = initial_size;
409 std::vector<propositional_variable_instantiation> set
410 = get_propositional_variable_instantiations(equation.formula());
411 current_size = set.size();
413 std::set<std::string> parameterNames = {};
414 for (data::variable a: equation.variable().parameters())
416 parameterNames.insert(a.name());
418 for (
const propositional_variable_instantiation& x: set)
421 if (options.timeout > 0.0)
423 std::chrono::time_point current_time = std::chrono::high_resolution_clock::now();
424 double elapsed = std::chrono::duration<
double>(current_time - timer.start_time).count();
425 if (elapsed >= options.timeout)
428 mCRL2log(log::log_level_t::verbose) <<
"Timeout reached (" << options.timeout <<
"s) for equation "
429 << equation.variable().name() <<
" after " << elapsed <<
"s, stopping substitution"
434 if (equation.variable().name() != x.name())
438 if (
auto it = stable_set.find(x); it != stable_set.end())
443 std::set<propositional_variable_instantiation> path = {x};
445 propositional_variable_instantiation cur_x = x;
446 pbes_expression result = x;
448 mCRL2log(log::log_level_t::debug) <<
" - - - - - - - - - - - - - - -\n"
449 <<
"\n\nStart " << (cur_x) <<
"\n";
450 bool pvi_done =
false;
457 data::mutable_indexed_substitution sigma;
458 data::data_expression_list pars = cur_x.parameters();
459 for (
const data::variable& v: equation.variable().parameters())
461 data::data_expression par = pars.front();
463 if (par.sort() != v.sort())
465 throw mcrl2::runtime_error(
"PBES is not well-typed.");
470 pbes_expression phi = measure_time(timer,
472 [&]() {
return pbes_rewrite(equation.formula(), pbes_default_rewriter, sigma); });
474 std::vector<propositional_variable_instantiation> phi_vector = get_propositional_variable_instantiations(phi);
477 auto it = find(phi_vector.begin(), phi_vector.end(), cur_x);
478 if (it != phi_vector.end())
480 mCRL2log(log::log_level_t::debug) <<
"Need to replace this with true/false " << pp(cur_x) <<
"\n";
481 mCRL2log(log::log_level_t::debug) << phi <<
"\n";
482 mCRL2log(log::log_level_t::debug) << equation.formula() <<
"\n";
484 if (!options.disable_gauss_elimination || pp(cur_x.name()) ==
"X_true" || pp(cur_x.name()) ==
"X_false")
486 pvi_substituter.set_pvi(cur_x);
487 pvi_substituter.set_replacement(equation.symbol().is_nu() ? true_() : false_());
488 pvi_substituter.apply(phi, phi);
489 timer.increment_gauss_elimination_direct_count();
492 mCRL2log(log::log_level_t::debug) << phi <<
"\n";
494 mCRL2log(log::log_level_t::debug) <<
"- - - - - - - - - - - - - - - - - - - - \n";
498 phi = measure_time(timer,
"simplify_expr", [&]() {
return simplify_expr(phi, if_substituter, pbes_rewriter); });
500 phi_vector = get_propositional_variable_instantiations(phi);
501 std::size_t size = phi_vector.size();
502 if (options.count_unique_pvi)
504 size = std::set(phi_vector.begin(), phi_vector.end()).size();
510 bool condition_result = (size >= 1 && size <= options.max_number_pvi);
512 if (condition_result)
514 condition_result = measure_time(timer,
515 "is_avoiding_alternation",
517 return std::all_of(phi_vector.begin(), phi_vector.end(), [&](
const propositional_variable_instantiation& pvi) {
518 return is_avoiding_alternation(options, pvi, equation);
523 if (condition_result)
526 = measure_time(timer,
"is_not_too_big", [&]() {
return is_not_too_big(options, cur_x, phi); });
529 if (condition_result)
532 = measure_time(timer,
"is_quantifier_free", [&]() {
return is_quantifier_free(phi, options); });
535 if (condition_result)
537 std::set<propositional_variable_instantiation> phi_set(phi_vector.begin(), phi_vector.end());
538 bool all_in_path =
true;
539 measure_time(timer,
"successful_substitutions", [&]() {
return true; });
541 for (
const propositional_variable_instantiation& phi_x: phi_set)
543 mCRL2log(log::log_level_t::debug) <<
"Trying loop " << phi_x <<
" in path with \n";
544 for (
const propositional_variable_instantiation& itr: path)
546 mCRL2log(log::log_level_t::debug) << itr <<
"\n";
549 if (path.contains(phi_x))
552 mCRL2log(log::log_level_t::debug) <<
"Loop, seen " << phi_x <<
" in path after " << cur_x <<
" " << phi <<
"\n";
553 for (
const propositional_variable_instantiation& itr: path)
555 mCRL2log(log::log_level_t::debug) << itr <<
"\n";
557 if (!options.disable_gauss_elimination || pp(cur_x.name()) ==
"X_true" || pp(cur_x.name()) ==
"X_false")
559 pvi_substituter.set_pvi(phi_x);
560 pvi_substituter.set_replacement(equation.symbol().is_nu() ? true_() : false_());
561 pvi_substituter.apply(result, phi);
562 timer.increment_gauss_elimination_path_count();
571 if (all_in_path && !options.disable_gauss_elimination)
573 pvi_substituter.set_pvi(cur_x);
574 pvi_substituter.set_replacement(result);
575 pvi_substituter.apply(equation.formula(), equation.formula());
577 mCRL2log(log::log_level_t::debug) <<
"new_phi " << result <<
"\n";
581 else if (all_in_path)
583 mCRL2log(log::log_level_t::debug) <<
"Loop, but Gauss elimination is disabled; stop expanding " << cur_x <<
"\n";
584 stable_set.insert(x);
585 stable_set.insert(cur_x);
591 mCRL2log(log::log_level_t::debug) <<
"Replaced in PBES equation for " << cur_x <<
"\n-->\n"
593 << core::detail::print_list(phi_set) <<
"\n";
595 pvi_substituter.set_pvi(cur_x);
596 pvi_substituter.set_replacement(phi);
597 pvi_substituter.apply(equation.formula(), equation.formula());
599 std::set<propositional_variable_instantiation> phi_set_same_name;
600 std::copy_if(phi_set.begin(),
602 std::inserter(phi_set_same_name, phi_set_same_name.begin()),
603 [&](
const propositional_variable_instantiation& pvi)
604 {
return pvi.name() == equation.variable().name(); });
606 if (phi_set_same_name.size() == 0)
612 cur_x = *phi_set_same_name.begin();
616 else if (size == 0 && is_quantifier_free(phi, options))
618 pvi_substituter.set_pvi(cur_x);
619 pvi_substituter.set_replacement(phi);
620 pvi_substituter.apply(equation.formula(), equation.formula());
622 mCRL2log(log::log_level_t::debug) <<
"Replaced in PBES equation for " << cur_x <<
":\n" << x <<
" \n-->\n " << phi <<
"\n";
624 measure_time(timer,
"successful_substitutions", [&]() {
return true; });
628 mCRL2log(log::log_level_t::debug) <<
"Not simpler: " << cur_x <<
" \n--> size: " << phi_vector.size() <<
"\n " << phi
629 <<
" and size " << phi_vector.size() <<
"\n";
638 mCRL2log(log::log_level_t::debug) <<
"Nothing further to do\n";
641 else if (depth >= options.max_depth)
643 stable_set.insert(x);
644 stable_set.insert(cur_x);
650 std::vector<propositional_variable_instantiation> set
651 = get_propositional_variable_instantiations(equation.formula());
653 current_size = set.size();
654 log_number_pvi(initial_size, current_size);
657 if (current_size == 0 || (previous_size >= current_size + 10))
659 previous_size = current_size;
660 equation.formula() = measure_time(timer,
662 [&]() {
return simplify_expr(equation.formula(), if_substituter, pbes_rewriter); });
667 log_number_pvi(initial_size, current_size);
669 if (current_size == 0)
673 [&]() {
return simplify_expr(equation.formula(), if_substituter, pbes_rewriter); });
684 substitute_propositional_variables_builder<pbes_system::pbes_expression_builder>& substituter)
686 substituter.set_equation(by);
687 substituter.set_name(into.variable().name());
689 substituter.apply(p, into.formula());
700 for (detail::stategraph_equation& equation: stategraph.equations())
702 for (detail::predicate_variable& predvar: equation.predicate_variables())
704 predvar.simplify_guard();
710 std::vector<pbes_equation> eqn = {};
711 for (detail::stategraph_equation& eq: stategraph.equations())
713 substitute_propositional_variables_i_builder<pbes_system::pbes_expression_builder> substituter(
714 pbes_default_rewriter,
715 eq.predicate_variables());
717 pbes_expression new_formula;
718 substituter.apply(new_formula, eq.formula());
719 pbes_equation new_eq(
static_cast<
const pbes_equation&>(eq));
720 new_eq.formula() = new_formula;
721 eqn.push_back(new_eq);
725 pbes res(p.data(), p.global_variables(), eqn, p.initial_state());
731 auto result = pbes2srf(pbesspec,
true);
732 if (split_srf_conditions)
734 result = split_conditions(result, 1);
736 return (result).to_pbes();
743 data::
rewriter data_rewriter(p.data(), options.rewrite_strategy);
745#ifdef MCRL2_ENABLE_JITTYC
746 if (options.rewrite_strategy == data::rewriter::strategy::jitty_compiling
747 || options.rewrite_strategy == data::rewriter::strategy::jitty_compiling_prover)
749 data_default_rewriter = data_rewriter;
755 substitute_propositional_variables_builder<pbes_system::pbes_expression_builder> substituter(pbes_default_rewriter);
756 rewrite_if_builder<pbes_system::pbes_expression_builder> if_rewriter(pbes_default_rewriter);
757 substitute_propositional_variables_for_true_false_builder<pbes_system::pbes_expression_builder> pvi_substituter(
770 std::vector<std::size_t> initial_sizes = {};
771 for (pbes_equation& i: p.equations())
773 initial_sizes.push_back(get_propositional_variable_instantiations(i.formula()).size());
782 std::set<core::identifier_string> equations_to_remove;
784 for (std::size_t equation_index = p.equations().size(); equation_index > 0; --equation_index)
786 pbes_equation& i = p.equations()[equation_index - 1];
787 mCRL2log(log::log_level_t::verbose) <<
"Investigating the equation for " << i.variable().name() <<
"\n";
788 std::size_t original_i = equation_index - 1;
791 initial_sizes[original_i],
794 options.rewrite_only_substitution ? pbes_default_rewriter : pbes_rewriter,
795 pbes_default_rewriter,
798 std::set<propositional_variable_instantiation> pvi_set = find_propositional_variable_instantiations(i.formula());
801 if (pvi_set.size() > 0 && options.srf_factor > 0)
804 pbes result_srf_pbes = tosrf(p, options.srf_split_conditions);
805 pbes original_srf_pbes = tosrf(original_pbes, options.srf_split_conditions);
808 core::identifier_string original_variable_name = i.variable().name();
809 auto original_eq_it = std::find_if(original_pbes.equations().begin(),
810 original_pbes.equations().end(),
811 [&](
const pbes_equation& eq) {
return eq.variable().name() == original_variable_name; });
812 if (original_eq_it == original_pbes.equations().end())
814 const pbes_equation& original_eq = *original_eq_it;
816 auto result_srf_eq = std::find_if(result_srf_pbes.equations().rbegin(),
817 result_srf_pbes.equations().rend(),
818 [&](
const pbes_equation& eq) {
return eq.variable().name() == original_variable_name; });
819 if (result_srf_eq == result_srf_pbes.equations().rend())
822 auto original_srf_eq = std::find_if(original_srf_pbes.equations().rbegin(),
823 original_srf_pbes.equations().rend(),
824 [&](
const pbes_equation& eq) {
return eq.variable().name() == original_variable_name; });
825 if (original_srf_eq == original_srf_pbes.equations().rend())
828 std::size_t original_size = pp(original_srf_eq->formula()).size();
829 std::size_t new_size = pp(result_srf_eq->formula()).size();
830 mCRL2log(log::log_level_t::verbose) <<
"Original size: " << original_size <<
" New size: " << new_size <<
"\n";
831 if (options.srf_factor * (
double)original_size <= (
double)new_size)
833 log_number_pvi(initial_sizes[original_i], initial_sizes[original_i]);
834 i.formula() = original_eq.formula();
835 pvi_set = find_propositional_variable_instantiations(i.formula());
839 mCRL2log(log::log_level_t::verbose) <<
"How many unique PVI are left? " << pvi_set.size() <<
"\n";
842 if (pvi_set.size() == 0 && options.back_substitution)
844 if (options.rewrite_only_substitution)
846 i.formula() = simplify_expr(i.formula(), if_rewriter, pbes_rewriter);
848 for (std::size_t j = 0; j < equation_index; ++j)
850 substitute(p.equations()[j], i, substituter);
852 equations_to_remove.insert(i.variable().name());
858 std::set<core::identifier_string> removable_equations;
859 for (std::size_t i = 0; i < p.equations().size(); ++i)
861 const core::identifier_string& name = p.equations()[i].variable().name();
862 if (!equations_to_remove.contains(name))
867 bool referenced = (p.initial_state().name() == name);
868 for (std::size_t j = 0; j < p.equations().size() && !referenced; ++j)
874 const std::set<propositional_variable_instantiation> pvis
875 = find_propositional_variable_instantiations(p.equations()[j].formula());
876 referenced = std::any_of(pvis.begin(),
878 [&](
const propositional_variable_instantiation& pvi) {
return pvi.name() == name; });
882 removable_equations.insert(name);
886 p.equations().erase(std::remove_if(p.equations().begin(),
888 [&](
const pbes_equation& equation)
889 {
return removable_equations.contains(equation.variable().name()); }),
890 p.equations().end());
896 const std::string& output_filename,
897 const utilities::file_format& input_format,
898 const utilities::file_format& output_format,
902 load_pbes(p, input_filename, input_format);
905 backward_substituter
.run(p
, options
);
906 save_pbes(p, output_filename, output_format);
aterm_string & operator=(const aterm_string &t) noexcept=default
sort_expression sort() const
Returns the sort of the data expression.
const core::identifier_string & name() const
Generic substitution function.
Rewriter that operates on data expressions.
const sort_expression & target_sort() const
Returns the target sort of this expression.
void compute_source_target_copy()
stategraph_pbes(const pbes &p, const data::rewriter &rewr)
Constructor.
propositional_variable & variable()
Returns the pbes variable of the equation.
pbes_expression & formula()
Returns the predicate formula on the right hand side of the equation.
const propositional_variable & variable() const
Returns the pbes variable of the equation.
pbes_expression & operator=(const pbes_expression &) noexcept=default
parameterized boolean equation system
\brief A propositional variable instantiation
const core::identifier_string & name() const
propositional_variable_instantiation & operator=(const propositional_variable_instantiation &) noexcept=default
const core::identifier_string & name() const
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
data_expression not_(const data_expression &x)
bool is_application(const data_expression &t)
Returns true if the term t is an application.
data_expression or_(const data_expression &x, const data_expression &y)
data_expression and_(const data_expression &x, const data_expression &y)
bool is_function_symbol(const atermpp::aterm &x)
Returns true if the term t is a function symbol.
void normalize(pbes &x)
The function normalize brings (embedded) pbes expressions into positive normal form,...
The main namespace for the PBES library.
void log_number_pvi(std::size_t &initial_size, std::size_t ¤t_size)
auto measure_time(timing_tracker &timer, const std::string &name, Func &&func)
Generic decorator to wrap any function call with timing measurements.
pbes fill_pvi(pbes &p, data::rewriter data_rewriter)
bool is_avoiding_alternation(const pbeschain_options &options, const propositional_variable_instantiation &new_x, const pbes_equation &eq)
void pbeschain(const std::string &input_filename, const std::string &output_filename, const utilities::file_format &input_format, const utilities::file_format &output_format, pbeschain_options options)
std::vector< propositional_variable_instantiation > get_propositional_variable_instantiations(Container const &container)
Returns all data variables that occur in a range of expressions.
pbes_expression simplify_expr(pbes_expression &phi, rewrite_if_builder< pbes_system::pbes_expression_builder > &if_substituter, simplify_data_rewriter< data::rewriter > &pbes_rewriter)
pbes tosrf(pbes_system::pbes pbesspec, bool split_srf_conditions=false)
bool is_not_too_big(pbeschain_options &options, propositional_variable_instantiation &cur_x, pbes_expression &phi)
bool is_quantifier_free(pbes_expression &phi, pbeschain_options options)
std::set< data::variable > find_free_variables(const pbes_system::pbes_expression &x)
void self_substitute(pbes_equation &equation, std::size_t initial_size, substitute_propositional_variables_for_true_false_builder< pbes_system::pbes_expression_builder > &pvi_substituter, rewrite_if_builder< pbes_system::pbes_expression_builder > &if_substituter, simplify_data_rewriter< data::rewriter > &pbes_rewriter, simplify_data_rewriter< data::rewriter > &pbes_default_rewriter, pbeschain_options options)
void substitute(pbes_equation &into, const pbes_equation &by, substitute_propositional_variables_builder< pbes_system::pbes_expression_builder > &substituter)
bool srf_split_conditions
bool disable_gauss_elimination
bool rewrite_only_substitution
std::size_t max_number_pvi
void run(pbes &p, pbeschain_options options)
simplify_data_rewriter< data::rewriter > m_pbes_rewriter
data::data_expression simpl_data(const data::data_expression &x)
rewrite_if_builder(simplify_data_rewriter< data::rewriter > &r)
void apply(T &result, const data::data_expression &x)
A rewriter that simplifies boolean expressions in a term, and rewrites data expressions using DataRew...
void set_name(const core::identifier_string &s)
substitute_propositional_variables_builder(simplify_data_rewriter< data::rewriter > &r)
simplify_quantifiers_data_rewriter< data::rewriter > m_pbes_rewriter
void apply(T &result, const propositional_variable_instantiation &x)
core::identifier_string name
void set_equation(const pbes_equation &eq)
substitute_propositional_variables_for_true_false_builder(simplify_data_rewriter< data::rewriter > &r)
void set_replacement(const pbes_expression x)
pbes_expression m_replacement
simplify_data_rewriter< data::rewriter > m_pbes_rewriter
void apply(T &result, const propositional_variable_instantiation &x)
void set_pvi(const propositional_variable_instantiation x)
propositional_variable_instantiation m_pvi
std::vector< detail::predicate_variable > predvars
substitute_propositional_variables_i_builder(simplify_data_rewriter< data::rewriter > &r, std::vector< detail::predicate_variable > predvars)
simplify_data_rewriter< data::rewriter > m_pbes_rewriter
void apply(T &result, const propositional_variable_instantiation &x)
void add_measurement(std::chrono::duration< double, std::milli > duration)
double get_total_seconds() const
std::chrono::duration< double, std::milli > total_time
Helper class to track multiple time measurements and call counts.
std::size_t gauss_elimination_path_count
void log_measurements() const
Log all measurements.
std::size_t gauss_elimination_direct_count
void increment_gauss_elimination_path_count()
Increment the counter of path Gauss eliminations (replacing a pvi in a loop with true/false)
std::map< std::string, measurement > measurements
void increment_gauss_elimination_direct_count()
Increment the counter of direct Gauss eliminations (replacing a reoccurring pvi with true/false)
std::chrono::time_point< std::chrono::high_resolution_clock > start_time
std::chrono::time_point< std::chrono::steady_clock > start_measurement()
Start timing an operation (returns the start time for use with end_measurement)
void end_measurement(const std::string &label, std::chrono::time_point< std::chrono::steady_clock > measurement_start)
End timing an operation and record it.