@@ -718,7 +718,7 @@ class PathOperator : public IntVarLocalSearchOperator {
718718 // / Only returns a valid value if path variables are taken into account.
719719 int64_t Path (int64_t node) const {
720720 if constexpr (ignore_path_vars) return 0LL ;
721- return Value (node + number_of_nexts_ );
721+ return Value (PathIndex ( node) );
722722 }
723723
724724 // / Number of next variables.
@@ -802,6 +802,20 @@ class PathOperator : public IntVarLocalSearchOperator {
802802 virtual void SetNextBaseToIncrement (int64_t base_index) {
803803 next_base_to_increment_ = base_index;
804804 }
805+ // / Returns true if the node can be moved to the given path.
806+ // / If paths are ignored, always returns true.
807+ bool IsCompatibleWithPath (int64_t node, int64_t path) const {
808+ if constexpr (ignore_path_vars) return true ;
809+ return this ->Var (PathIndex (node))->Contains (path);
810+ }
811+ bool CheckPathCompatibility (absl::Span<const int64_t > path,
812+ int path_id) const {
813+ for (int64_t node : path) {
814+ if (!IsCompatibleWithPath (node, path_id)) return false ;
815+ }
816+ return true ;
817+ }
818+
805819 // / Indicates if alternatives should be considered when iterating over base
806820 // / nodes.
807821 virtual bool ConsiderAlternatives (int64_t ) const { return false ; }
@@ -823,7 +837,7 @@ class PathOperator : public IntVarLocalSearchOperator {
823837
824838 int64_t OldPath (int64_t node) const {
825839 if constexpr (ignore_path_vars) return 0LL ;
826- return OldValue (node + number_of_nexts_ );
840+ return OldValue (PathIndex ( node) );
827841 }
828842
829843 int CurrentNodePathStart (int64_t node) const {
@@ -834,26 +848,47 @@ class PathOperator : public IntVarLocalSearchOperator {
834848
835849 // / Moves the chain starting after the node before_chain and ending at the
836850 // / node chain_end after the node destination
837- bool MoveChain (int64_t before_chain, int64_t chain_end, int64_t destination) {
838- if (destination == before_chain || destination == chain_end) return false ;
851+ struct ChangeResult {
852+ bool has_change;
853+ bool is_feasible;
854+ };
855+ ChangeResult MoveChainWithCheck (int64_t before_chain, int64_t chain_end,
856+ int64_t destination,
857+ bool check_path_compatibility) {
858+ if (destination == before_chain || destination == chain_end) {
859+ return {.has_change = false , .is_feasible = true };
860+ }
839861 DCHECK (CheckChainValidity (before_chain, chain_end, destination) &&
840862 !IsPathEnd (chain_end) && !IsPathEnd (destination));
841863 const int64_t destination_path = Path (destination);
842864 const int64_t after_chain = Next (chain_end);
843865 SetNext (chain_end, Next (destination), destination_path);
866+ bool is_path_compatible =
867+ !check_path_compatibility ||
868+ this ->IsCompatibleWithPath (chain_end, destination_path);
844869 if constexpr (!ignore_path_vars) {
845870 int current = destination;
846871 int next = Next (before_chain);
847872 while (current != chain_end) {
848873 SetNext (current, next, destination_path);
874+ if (check_path_compatibility && is_path_compatible &&
875+ !this ->IsCompatibleWithPath (next, destination_path)) {
876+ is_path_compatible = false ;
877+ // TODO(user): Investigate if we could break here (making sure the
878+ // path inconsistencies don't break the operator).
879+ }
849880 current = next;
850881 next = Next (next);
851882 }
852883 } else {
853884 SetNext (destination, Next (before_chain), destination_path);
854885 }
855886 SetNext (before_chain, after_chain, Path (before_chain));
856- return true ;
887+ return {.has_change = true , .is_feasible = is_path_compatible};
888+ }
889+ bool MoveChain (int64_t before_chain, int64_t chain_end, int64_t destination) {
890+ return MoveChainWithCheck (before_chain, chain_end, destination, false )
891+ .has_change ;
857892 }
858893
859894 // / Reverses the chain starting after before_chain and ending before
@@ -896,15 +931,28 @@ class PathOperator : public IntVarLocalSearchOperator {
896931 }
897932
898933 // / Swaps the nodes node1 and node2.
899- bool SwapNodes (int64_t node1, int64_t node2) {
934+ ChangeResult SwapNodesWithCheck (int64_t node1, int64_t node2,
935+ bool check_path_compatibility) {
900936 if (IsPathEnd (node1) || IsPathEnd (node2) || IsPathStart (node1) ||
901937 IsPathStart (node2)) {
902- return false ;
938+ return {. has_change = false , . is_feasible = true } ;
903939 }
904- if (node1 == node2) return false ;
940+ if (node1 == node2) return {. has_change = false , . is_feasible = true } ;
905941 const int64_t prev_node1 = Prev (node1);
906- const bool ok = MoveChain (prev_node1, node1, Prev (node2));
907- return MoveChain (Prev (node2), node2, prev_node1) || ok;
942+ if constexpr (!ignore_path_vars) {
943+ const auto [has_change1, is_feasible1] = MoveChainWithCheck (
944+ prev_node1, node1, Prev (node2), check_path_compatibility);
945+ const auto [has_change2, is_feasible2] = MoveChainWithCheck (
946+ Prev (node2), node2, prev_node1, check_path_compatibility);
947+ return {.has_change = has_change1 || has_change2,
948+ .is_feasible = is_feasible1 && is_feasible2};
949+ }
950+ bool ok = MoveChain (prev_node1, node1, Prev (node2));
951+ ok = MoveChain (Prev (node2), node2, prev_node1) || ok;
952+ return {.has_change = ok, .is_feasible = true };
953+ }
954+ bool SwapNodes (int64_t node1, int64_t node2) {
955+ return SwapNodesWithCheck (node1, node2, false ).has_change ;
908956 }
909957
910958 // / Insert the inactive node after destination.
@@ -1085,6 +1133,8 @@ class PathOperator : public IntVarLocalSearchOperator {
10851133 return node >= 0 ? node : default_value;
10861134 }
10871135
1136+ int64_t PathIndex (int64_t node) const { return node + number_of_nexts_; }
1137+
10881138 void OnStart () override {
10891139 optimal_paths_enabled_ = false ;
10901140 if (!iterators_initialized_) {
0 commit comments