OrcaSlicer latest
OrcaSlicer
Loading...
Searching...
No Matches
Slic3r::KMediods Class Reference

#include </home/runner/work/OrcaSlicer/OrcaSlicer/src/libslic3r/FilamentGroup.hpp>

Collaboration diagram for Slic3r::KMediods:

Public Member Functions

 KMediods (const int k, const int elem_count, const std::shared_ptr< FlushDistanceEvaluator > &evaluator, int default_group_id=0)
void set_max_cluster_size (const std::vector< int > &group_size)
void set_cluster_group_size (const std::vector< std::pair< std::set< int >, int > > &cluster_group_size)
void set_placable_limits (const std::unordered_map< int, std::vector< int > > &placable_limits)
void set_unplacable_limits (const std::unordered_map< int, std::vector< int > > &unplacable_limits)
void set_memory_threshold (double threshold)
MemoryedGroupHeap get_memoryed_groups () const
void do_clustering (const FilamentGroupContext &context, const ClusteringBudget &budget)
std::vector< int > get_cluster_labels () const

Protected Types

using MemoryedGroupHeap = FilamentGroupUtils::MemoryedGroupHeap
using MemoryedGroup = FilamentGroupUtils::MemoryedGroup

Protected Member Functions

bool have_enough_size (const std::vector< int > &cluster_size, const std::vector< std::pair< std::set< int >, int > > &cluster_group_size, int elem_count)
int calc_cost (const std::vector< int > &clusters, const std::vector< int > &cluster_centers, int cluster_id=-1)
std::vector< int > init_cluster_center (const std::unordered_map< int, std::vector< int > > &placeable_limits, const std::unordered_map< int, std::vector< int > > &unplaceable_limits, const std::vector< int > &cluster_size, const std::vector< std::pair< std::set< int >, int > > &cluster_group_size, int seed)
std::vector< int > assign_cluster_label (const std::vector< int > &center, const std::unordered_map< int, std::vector< int > > &placeable_limits, const std::unordered_map< int, std::vector< int > > &unplaceable_limits, const std::vector< int > &group_size, const std::vector< std::pair< std::set< int >, int > > &cluster_group_size)

Protected Attributes

MemoryedGroupHeap memoryed_groups
std::shared_ptr< FlushDistanceEvaluator > m_evaluator
std::unordered_map< int, std::vector< int > > m_unplaceable_limits
std::unordered_map< int, std::vector< int > > m_placeable_limits
std::vector< int > m_max_cluster_size
std::vector< int > m_cluster_labels
std::vector< std::pair< std::set< int >, int > > m_cluster_group_size
std::vector< int > m_nozzle_to_extruder
int m_k
int m_elem_count
int m_default_group_id { 0 }
double memory_threshold { 0 }

Detailed Description

Member Typedef Documentation

◆ MemoryedGroup

◆ MemoryedGroupHeap

Constructor & Destructor Documentation

◆ KMediods()

Slic3r::KMediods::KMediods ( const int k,
const int elem_count,
const std::shared_ptr< FlushDistanceEvaluator > & evaluator,
int default_group_id = 0 )
inline
222 {
223 m_k = k;
224 m_evaluator = evaluator;
225 m_max_cluster_size = std::vector<int>(k, DEFAULT_CLUSTER_SIZE);
226 m_elem_count = elem_count;
227 m_default_group_id = default_group_id;
228 }
static const int DEFAULT_CLUSTER_SIZE
Definition FilamentGroup.hpp:21
std::vector< int > m_max_cluster_size
Definition FilamentGroup.hpp:261
int m_default_group_id
Definition FilamentGroup.hpp:269
int m_elem_count
Definition FilamentGroup.hpp:268
std::shared_ptr< FlushDistanceEvaluator > m_evaluator
Definition FilamentGroup.hpp:258
int m_k
Definition FilamentGroup.hpp:267

References DEFAULT_CLUSTER_SIZE, m_default_group_id, m_elem_count, m_evaluator, m_k, and m_max_cluster_size.

Member Function Documentation

◆ assign_cluster_label()

std::vector< int > Slic3r::KMediods::assign_cluster_label ( const std::vector< int > & center,
const std::unordered_map< int, std::vector< int > > & placeable_limits,
const std::unordered_map< int, std::vector< int > > & unplaceable_limits,
const std::vector< int > & group_size,
const std::vector< std::pair< std::set< int >, int > > & cluster_group_size )
protected
737 {
738 std::vector<int> labels(m_elem_count, -1);
739 std::vector<int> l_nodes(m_elem_count);
740 std::vector<int> r_nodes(m_k);
741 std::iota(l_nodes.begin(), l_nodes.end(), 0);
742 std::iota(r_nodes.begin(), r_nodes.end(), 0);
743
744 std::vector<std::vector<float>> distance_matrix(m_elem_count, std::vector<float>(m_k));
745 for (int i = 0; i < m_elem_count; ++i) {
746 for (int j = 0; j < m_k; ++j) {
747 if (center[j] == -1)
748 distance_matrix[i][j] = static_cast<float>(MaxFlowGraph::MCMF_MAX_EDGE_COST);
749 else
750 distance_matrix[i][j] = m_evaluator->get_distance(i, center[j], m_nozzle_to_extruder[j]);
751 }
752 }
753
754 // only consider the size limit if the group can contain all of the filaments
755 std::vector<int> r_nodes_capacity = {};
756 std::vector<std::pair<std::set<int>, int>> r_nodes_group_capacity = {};
757 if (have_enough_size(cluster_size, cluster_group_size, m_elem_count)) {
758 r_nodes_capacity = cluster_size;
759 r_nodes_group_capacity = cluster_group_size;
760 }
761 else {
762 // TODO: throw exception here?
763 // adjust group size to elem count if the group cannot contain all of the filaments
764 r_nodes_capacity = std::vector<int>(m_k, m_elem_count);
765 }
766 std::vector<int> l_nodes_capacity(l_nodes.size(),1);
767 //for (size_t idx = 0; idx < center.size(); ++idx)
768 // if (center[idx] != -1)
769 // l_nodes_capacity[center[idx]] = 0;
770
771
772 // Each group can receive up to m_elem_count materials at most, so the flow from r_nodes to sink is adjusted to m_elem_count.
773 MinFlushFlowSolver M(distance_matrix, l_nodes, r_nodes, placeable_limits, unplaceable_limits, l_nodes_capacity, r_nodes_capacity, r_nodes_group_capacity);
774 auto ret = M.solve();
775
776 for (size_t idx = 0; idx < ret.size(); ++idx) {
777 if (ret[idx] != MaxFlowGraph::INVALID_ID) {
778 labels[l_nodes[idx]] = r_nodes[ret[idx]];
779 }
780 }
781
782 for (size_t idx = 0; idx < center.size(); ++idx)
783 if (center[idx] != -1)
784 assert(labels[center[idx]] == idx);
785
786 //for (size_t idx = 0; idx < center.size(); ++idx) {
787 // if (center[idx] != -1) {
788 // labels[center[idx]] = idx;
789 // }
790 //}
791
792 // If there are materials that have not been grouped in the last step, assign them to a valid group.
793 for (size_t idx = 0; idx < labels.size(); ++idx) {
794 if (labels[idx] == -1) {
795 int fallback = m_default_group_id;
796 auto it = unplaceable_limits.find(static_cast<int>(idx));
797 if (it != unplaceable_limits.end()) {
798 for (int nid = 0; nid < m_k; ++nid) {
799 if (std::find(it->second.begin(), it->second.end(), nid) == it->second.end()) {
800 fallback = nid;
801 break;
802 }
803 }
804 }
805 labels[idx] = fallback;
806 }
807 }
808
809 return labels;
810 }
std::vector< int > m_nozzle_to_extruder
Definition FilamentGroup.hpp:264
bool have_enough_size(const std::vector< int > &cluster_size, const std::vector< std::pair< std::set< int >, int > > &cluster_group_size, int elem_count)
Definition FilamentGroup.cpp:675
constexpr int MCMF_MAX_EDGE_COST
Definition ToolOrderUtils.hpp:23
const int INVALID_ID
Definition ToolOrderUtils.hpp:21
void center(ExPolygonsWithIds &e)
Definition Emboss.cpp:1382

References Slic3r::center(), have_enough_size(), Slic3r::MaxFlowGraph::INVALID_ID, m_default_group_id, m_elem_count, m_evaluator, m_k, m_nozzle_to_extruder, Slic3r::MaxFlowGraph::MCMF_MAX_EDGE_COST, and Slic3r::MinFlushFlowSolver::solve().

Referenced by do_clustering().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ calc_cost()

int Slic3r::KMediods::calc_cost ( const std::vector< int > & clusters,
const std::vector< int > & cluster_centers,
int cluster_id = -1 )
protected
645 {
646 assert(m_evaluator);
647 int total_cost = 0;
648
649 std::vector<std::pair<int, double>> nozzle_cost(m_k,{0,0.0});
650 std::vector<int> nozzle_filaments(m_k, 0);
651 for (int i = 0; i < m_elem_count; ++i) {
652 if (cluster_id != -1 && cluster_labels[i] != cluster_id)
653 continue;
654 if (cluster_centers[cluster_labels[i]] == -1)
655 continue;
656
657 nozzle_filaments[cluster_labels[i]]++;
658 for (int j = i + 1; j < m_elem_count; ++j) {
659 int nozzle_i = cluster_labels[i];
660 int nozzle_j = cluster_labels[j];
661 if (nozzle_i == nozzle_j) {
662 nozzle_cost[nozzle_i].first++;
663 nozzle_cost[nozzle_j].second += m_evaluator->get_distance(i, j, m_nozzle_to_extruder[nozzle_i]);
664 }
665 }
666 }
667 for (size_t i = 0; i < nozzle_cost.size(); ++i) {
668 if (nozzle_filaments[i] > 0 && nozzle_cost[i].second > 0)
669 total_cost += nozzle_cost[i].second / nozzle_cost[i].first * (nozzle_filaments[i] - 1);
670 }
671
672 return total_cost;
673 }

References m_elem_count, m_evaluator, m_k, and m_nozzle_to_extruder.

◆ do_clustering()

void Slic3r::KMediods::do_clustering ( const FilamentGroupContext & context,
const ClusteringBudget & budget )
819 {
820 FlushTimeMachine T;
821 T.time_machine_start();
822
823 const std::vector<unsigned int> used_filaments = collect_sorted_used_filaments(context.model_info.layer_filaments);
824
825 auto build_full_map = [&](const std::vector<int>& labels) -> std::vector<int> {
826 std::vector<int> full_map(context.group_info.total_filament_num, m_default_group_id);
827 for (int i = 0; i < (int)labels.size(); ++i)
828 full_map[used_filaments[i]] = labels[i];
829 return full_map;
830 };
831
832 auto evaluate_labels = [&](const std::vector<int>& labels) -> double {
833 auto full_map = build_full_map(labels);
834 return full_evaluate(used_filaments, full_map, context);
835 };
836
837 std::vector<int> best_cluster_centers = std::vector<int>(m_k, 0);
838 std::vector<int> best_cluster_labels = std::vector<int>(m_elem_count, m_default_group_id);
839 double best_cluster_cost = std::numeric_limits<double>::max();
840 int retry_count = 0;
841
842 // Run at least one restart; otherwise every filament would stay in the default group.
843 const int retry = std::max(1, budget.max_restarts);
844 auto within_budget = [&]() { return budget.timeout_ms <= 0 || T.time_machine_end() < budget.timeout_ms; };
845
846 while (retry_count < retry && within_budget()) {
847 std::vector<int> curr_cluster_centers = init_cluster_center(m_placeable_limits, m_unplaceable_limits, m_max_cluster_size, m_cluster_group_size, retry_count);
848 std::vector<int> curr_cluster_labels = assign_cluster_label(curr_cluster_centers, m_placeable_limits, m_unplaceable_limits, m_max_cluster_size, m_cluster_group_size);
849 double curr_cluster_cost = evaluate_labels(curr_cluster_labels);
850
851 MemoryedGroup g(curr_cluster_labels, curr_cluster_cost, 1);
853
854 bool mediods_changed = true;
855 while (mediods_changed && within_budget()) {
856 mediods_changed = false;
857 double best_swap_cost = curr_cluster_cost;
858 int best_swap_cluster = -1;
859 int best_swap_elem = -1;
860
861 for (size_t cluster_id = 0; cluster_id < m_k; ++cluster_id) {
862 if (curr_cluster_centers[cluster_id] == -1) continue;
863 for (int elem = 0; elem < m_elem_count; ++elem) {
864 if (std::find(curr_cluster_centers.begin(), curr_cluster_centers.end(), elem) != curr_cluster_centers.end() ||
865 std::find(m_unplaceable_limits[cluster_id].begin(), m_unplaceable_limits[cluster_id].end(), elem) != m_unplaceable_limits[cluster_id].end())
866 continue;
867 std::vector<int> tmp_centers = curr_cluster_centers;
868 tmp_centers[cluster_id] = elem;
870 double tmp_cost = evaluate_labels(tmp_labels);
871
872 if (tmp_cost < best_swap_cost) {
873 best_swap_cost = tmp_cost;
874 best_swap_cluster = cluster_id;
875 best_swap_elem = elem;
876 mediods_changed = true;
877 }
878 }
879 }
880
881 if (mediods_changed) {
882 curr_cluster_centers[best_swap_cluster] = best_swap_elem;
884 curr_cluster_cost = evaluate_labels(curr_cluster_labels);
885
886 MemoryedGroup g(curr_cluster_labels, curr_cluster_cost, 1);
888 }
889 }
890
891 if (curr_cluster_cost < best_cluster_cost) {
892 best_cluster_centers = curr_cluster_centers;
893 best_cluster_cost = curr_cluster_cost;
894 best_cluster_labels = curr_cluster_labels;
895 }
896 retry_count += 1;
897 }
898 m_cluster_labels = best_cluster_labels;
899 }
std::vector< int > init_cluster_center(const std::unordered_map< int, std::vector< int > > &placeable_limits, const std::unordered_map< int, std::vector< int > > &unplaceable_limits, const std::vector< int > &cluster_size, const std::vector< std::pair< std::set< int >, int > > &cluster_group_size, int seed)
Definition FilamentGroup.cpp:694
std::vector< int > assign_cluster_label(const std::vector< int > &center, const std::unordered_map< int, std::vector< int > > &placeable_limits, const std::unordered_map< int, std::vector< int > > &unplaceable_limits, const std::vector< int > &group_size, const std::vector< std::pair< std::set< int >, int > > &cluster_group_size)
Definition FilamentGroup.cpp:736
std::vector< std::pair< std::set< int >, int > > m_cluster_group_size
Definition FilamentGroup.hpp:263
MemoryedGroupHeap memoryed_groups
Definition FilamentGroup.hpp:257
std::unordered_map< int, std::vector< int > > m_unplaceable_limits
Definition FilamentGroup.hpp:259
FilamentGroupUtils::MemoryedGroup MemoryedGroup
Definition FilamentGroup.hpp:220
double memory_threshold
Definition FilamentGroup.hpp:270
std::unordered_map< int, std::vector< int > > m_placeable_limits
Definition FilamentGroup.hpp:260
std::vector< int > m_cluster_labels
Definition FilamentGroup.hpp:262
void update_memoryed_groups(const MemoryedGroup &item, const double gap_threshold, MemoryedGroupHeap &groups)
Definition FilamentGroup.cpp:518
static double full_evaluate(const std::vector< unsigned int > &used_filaments, const std::vector< int > &filament_nozzle_map, const FilamentGroupContext &ctx, std::optional< std::function< bool(int, std::vector< int > &)> > get_custom_seq=std::nullopt, int *out_flush=nullptr)
Definition FilamentGroup.cpp:114
std::vector< unsigned int > collect_sorted_used_filaments(const std::vector< std::vector< unsigned int > > &layer_filaments)
Definition FilamentGroup.cpp:563

References assign_cluster_label(), Slic3r::collect_sorted_used_filaments(), Slic3r::full_evaluate(), Slic3r::FilamentGroupContext::group_info, init_cluster_center(), Slic3r::FilamentGroupContext::ModelInfo::layer_filaments, m_cluster_group_size, m_cluster_labels, m_default_group_id, m_elem_count, m_k, m_max_cluster_size, m_placeable_limits, m_unplaceable_limits, Slic3r::ClusteringBudget::max_restarts, memory_threshold, memoryed_groups, Slic3r::FilamentGroupContext::model_info, Slic3r::FilamentGroupUtils::FlushTimeMachine::time_machine_start(), Slic3r::ClusteringBudget::timeout_ms, Slic3r::FilamentGroupContext::GroupInfo::total_filament_num, and Slic3r::FilamentGroupUtils::update_memoryed_groups().

Referenced by Slic3r::FilamentGroup::calc_group_by_kmedoids().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_cluster_labels()

std::vector< int > Slic3r::KMediods::get_cluster_labels ( ) const
inline
244{ return m_cluster_labels; }

References m_cluster_labels.

Referenced by Slic3r::FilamentGroup::calc_group_by_kmedoids().

Here is the caller graph for this function:

◆ get_memoryed_groups()

MemoryedGroupHeap Slic3r::KMediods::get_memoryed_groups ( ) const
inline
241{ return memoryed_groups; }

References memoryed_groups.

Referenced by Slic3r::FilamentGroup::calc_group_by_kmedoids().

Here is the caller graph for this function:

◆ have_enough_size()

bool Slic3r::KMediods::have_enough_size ( const std::vector< int > & cluster_size,
const std::vector< std::pair< std::set< int >, int > > & cluster_group_size,
int elem_count )
protected
676 {
677 bool have_enough_size = true;
678 std::optional<int>cluster_sum;
679 std::optional<int>cluster_group_sum;
680
681 if (!cluster_size.empty())
682 cluster_sum = std::accumulate(cluster_size.begin(), cluster_size.end(), 0);
683 if (!cluster_group_size.empty())
684 cluster_group_sum = std::accumulate(cluster_group_size.begin(), cluster_group_size.end(), 0, [](int a, const std::pair<std::set<int>, int>& p) {return a + p.second; });
685 if (cluster_sum.has_value())
686 have_enough_size &= (cluster_sum >= elem_count);
687 if (cluster_group_sum.has_value())
688 have_enough_size &= (cluster_group_sum >= elem_count);
689 return have_enough_size;
690 }

References have_enough_size().

Referenced by assign_cluster_label(), and have_enough_size().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ init_cluster_center()

std::vector< int > Slic3r::KMediods::init_cluster_center ( const std::unordered_map< int, std::vector< int > > & placeable_limits,
const std::unordered_map< int, std::vector< int > > & unplaceable_limits,
const std::vector< int > & cluster_size,
const std::vector< std::pair< std::set< int >, int > > & cluster_group_size,
int seed )
protected
695 {
696 // max flow network
697 std::vector<int> l_nodes(m_elem_count); // represent the filament idx, to be shuffled
698 std::vector<int> r_nodes(m_k); // represent the group idx
699 std::iota(l_nodes.begin(), l_nodes.end(), 0);
700 std::iota(r_nodes.begin(), r_nodes.end(), 0);
701
702 std::unordered_map<int, std::vector<int>> shuffled_placeable_limits;
703 std::unordered_map<int, std::vector<int>> shuffled_unplaceable_limits;
704 // shuffle the filaments and transfer placeable,unplaceable limits
705 {
706 std::mt19937 rng(seed);
707 std::shuffle(l_nodes.begin(), l_nodes.end(), rng);
708
709 std::unordered_map<int, int>idx_transfer;
710 for (size_t idx = 0; idx < l_nodes.size(); ++idx){
711 int new_idx = std::find(l_nodes.begin(),l_nodes.end(), idx) - l_nodes.begin();
712 idx_transfer[idx] = new_idx;
713 }
714 for (auto& elem : placeable_limits)
715 shuffled_placeable_limits[idx_transfer[elem.first]] = elem.second;
716 for (auto& elem : unplaceable_limits)
717 shuffled_unplaceable_limits[idx_transfer[elem.first]] = elem.second;
718 }
719
720
721 MaxFlowSolver M(l_nodes, r_nodes, shuffled_placeable_limits, shuffled_unplaceable_limits);
722 auto ret = M.solve();
723
724 // A remaining -1 means some filaments cannot be placed under the limit. We ignore the -1 here since we
725 // are deciding the cluster center; the -1 can be handled in later steps.
726 std::vector<int> cluster_center(m_k, -1);
727 for (size_t idx = 0; idx < ret.size(); ++idx) {
728 if (ret[idx] != -1) {
729 cluster_center[ret[idx]] = l_nodes[idx];
730 }
731 }
732
733 return cluster_center;
734 }

References m_elem_count, m_k, and Slic3r::MaxFlowSolver::solve().

Referenced by do_clustering().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ set_cluster_group_size()

void Slic3r::KMediods::set_cluster_group_size ( const std::vector< std::pair< std::set< int >, int > > & cluster_group_size)
636 {
637 m_cluster_group_size = cluster_group_size;
638 m_nozzle_to_extruder.resize(m_k, 0);
639 for (int i = 0; i < m_cluster_group_size.size(); i++) {
640 for (auto nozzle_id : m_cluster_group_size[i].first) m_nozzle_to_extruder[nozzle_id] = i;
641 }
642 }

References m_cluster_group_size, m_k, and m_nozzle_to_extruder.

Referenced by Slic3r::FilamentGroup::calc_group_by_kmedoids().

Here is the caller graph for this function:

◆ set_max_cluster_size()

void Slic3r::KMediods::set_max_cluster_size ( const std::vector< int > & group_size)
inline
230{ m_max_cluster_size = group_size; }

References m_max_cluster_size.

◆ set_memory_threshold()

void Slic3r::KMediods::set_memory_threshold ( double threshold)
inline
240{ memory_threshold = threshold; }

References memory_threshold.

Referenced by Slic3r::FilamentGroup::calc_group_by_kmedoids().

Here is the caller graph for this function:

◆ set_placable_limits()

void Slic3r::KMediods::set_placable_limits ( const std::unordered_map< int, std::vector< int > > & placable_limits)
inline
235{ m_placeable_limits = placable_limits; }

References m_placeable_limits.

◆ set_unplacable_limits()

void Slic3r::KMediods::set_unplacable_limits ( const std::unordered_map< int, std::vector< int > > & unplacable_limits)
inline
238{ m_unplaceable_limits = unplacable_limits; }

References m_unplaceable_limits.

Referenced by Slic3r::FilamentGroup::calc_group_by_kmedoids().

Here is the caller graph for this function:

Member Data Documentation

◆ m_cluster_group_size

std::vector<std::pair<std::set<int>,int> > Slic3r::KMediods::m_cluster_group_size
protected

◆ m_cluster_labels

std::vector<int> Slic3r::KMediods::m_cluster_labels
protected

◆ m_default_group_id

int Slic3r::KMediods::m_default_group_id { 0 }
protected
269{ 0 };

Referenced by KMediods(), assign_cluster_label(), and do_clustering().

◆ m_elem_count

int Slic3r::KMediods::m_elem_count
protected

◆ m_evaluator

std::shared_ptr<FlushDistanceEvaluator> Slic3r::KMediods::m_evaluator
protected

◆ m_k

◆ m_max_cluster_size

std::vector<int> Slic3r::KMediods::m_max_cluster_size
protected

◆ m_nozzle_to_extruder

std::vector<int> Slic3r::KMediods::m_nozzle_to_extruder
protected

◆ m_placeable_limits

std::unordered_map<int, std::vector<int> > Slic3r::KMediods::m_placeable_limits
protected

◆ m_unplaceable_limits

std::unordered_map<int, std::vector<int> > Slic3r::KMediods::m_unplaceable_limits
protected

◆ memory_threshold

double Slic3r::KMediods::memory_threshold { 0 }
protected
270{ 0 };

Referenced by do_clustering(), and set_memory_threshold().

◆ memoryed_groups

MemoryedGroupHeap Slic3r::KMediods::memoryed_groups
protected

The documentation for this class was generated from the following files: