@@ -998,8 +998,10 @@ struct chan_flow
998
998
};
999
999
1000
1000
/* Search in the network a path of positive flow until we reach a node with
1001
- * positive balance. */
1002
- static u32 find_positive_balance (
1001
+ * positive balance (returns a node idx with positive balance)
1002
+ * or we discover a cycle (returns a node idx with 0 balance).
1003
+ * */
1004
+ static u32 find_path_or_cycle (
1003
1005
const struct gossmap * gossmap ,
1004
1006
const struct chan_flow * chan_flow ,
1005
1007
const u32 start_idx ,
@@ -1009,42 +1011,34 @@ static u32 find_positive_balance(
1009
1011
int * prev_dir ,
1010
1012
u32 * prev_idx )
1011
1013
{
1014
+ const size_t max_num_nodes = gossmap_max_node_idx (gossmap );
1015
+ bitmap * visited =
1016
+ tal_arrz (tmpctx , bitmap , BITMAP_NWORDS (max_num_nodes ));
1012
1017
u32 final_idx = start_idx ;
1018
+ bitmap_set_bit (visited , start_idx );
1013
1019
1014
- /* TODO(eduardo)
1015
- * This is guaranteed to halt if there are no directed flow cycles.
1016
- * There souldn't be any. In fact if cost is strickly
1017
- * positive, then flow cycles do not exist at all in the
1018
- * MCF solution. But if cost is allowed to be zero for
1019
- * some arcs, then we might have flow cyles in the final
1020
- * solution. We must somehow ensure that the MCF
1021
- * algorithm does not come up with spurious flow cycles. */
1022
- while (balance [final_idx ]<=0 )
1023
- {
1024
- // printf("%s: node = %d\n",__PRETTY_FUNCTION__,final_idx);
1025
- u32 updated_idx = INVALID_INDEX ;
1026
- struct gossmap_node * cur
1027
- = gossmap_node_byidx (gossmap ,final_idx );
1020
+ /* It is guaranteed to halt, because we either find a node with
1021
+ * balance[]>0 or we hit a node twice and we stop. */
1022
+ while (balance [final_idx ] <= 0 ) {
1023
+ u32 updated_idx = INVALID_INDEX ;
1024
+ struct gossmap_node * cur =
1025
+ gossmap_node_byidx (gossmap , final_idx );
1028
1026
1029
- for (size_t i = 0 ;i < cur -> num_chans ;++ i )
1030
- {
1027
+ for (size_t i = 0 ; i < cur -> num_chans ; ++ i ) {
1031
1028
int dir ;
1032
- const struct gossmap_chan * c
1033
- = gossmap_nth_chan (gossmap ,
1034
- cur ,i ,& dir );
1029
+ const struct gossmap_chan * c =
1030
+ gossmap_nth_chan (gossmap , cur , i , & dir );
1035
1031
1036
- if (!gossmap_chan_set (c , dir ))
1032
+ if (!gossmap_chan_set (c , dir ) || ! c -> half [ dir ]. enabled )
1037
1033
continue ;
1038
1034
1039
- const u32 c_idx = gossmap_chan_idx (gossmap ,c );
1040
-
1041
- // follow the flow
1042
- if (chan_flow [c_idx ].half [dir ]> 0 )
1043
- {
1044
- const struct gossmap_node * next
1045
- = gossmap_nth_node (gossmap ,c ,!dir );
1046
- u32 next_idx = gossmap_node_idx (gossmap ,next );
1035
+ const u32 c_idx = gossmap_chan_idx (gossmap , c );
1047
1036
1037
+ /* follow the flow */
1038
+ if (chan_flow [c_idx ].half [dir ] > 0 ) {
1039
+ const struct gossmap_node * next =
1040
+ gossmap_nth_node (gossmap , c , !dir );
1041
+ u32 next_idx = gossmap_node_idx (gossmap , next );
1048
1042
1049
1043
prev_dir [next_idx ] = dir ;
1050
1044
prev_chan [next_idx ] = c ;
@@ -1055,10 +1049,17 @@ static u32 find_positive_balance(
1055
1049
}
1056
1050
}
1057
1051
1058
- assert (updated_idx != INVALID_INDEX );
1059
- assert (updated_idx != final_idx );
1060
-
1052
+ assert (updated_idx != INVALID_INDEX );
1053
+ assert (updated_idx != final_idx );
1061
1054
final_idx = updated_idx ;
1055
+
1056
+ if (bitmap_test_bit (visited , updated_idx )) {
1057
+ /* We have seen this node before, we've found a cycle.
1058
+ */
1059
+ assert (balance [updated_idx ] <= 0 );
1060
+ break ;
1061
+ }
1062
+ bitmap_set_bit (visited , updated_idx );
1062
1063
}
1063
1064
return final_idx ;
1064
1065
}
@@ -1069,6 +1070,108 @@ struct list_data
1069
1070
struct flow * flow_path ;
1070
1071
};
1071
1072
1073
+ /* Given a path from a node with negative balance to a node with positive
1074
+ * balance, compute the bigest flow and substract it from the nodes balance and
1075
+ * the channels allocation. */
1076
+ static struct flow * substract_flow (const tal_t * ctx ,
1077
+ const struct gossmap * gossmap ,
1078
+ const u32 start_idx , const u32 final_idx ,
1079
+ s64 * balance , struct chan_flow * chan_flow ,
1080
+ const u32 * prev_idx , const int * prev_dir ,
1081
+ const struct gossmap_chan * const * prev_chan )
1082
+ {
1083
+ assert (balance [start_idx ] < 0 );
1084
+ assert (balance [final_idx ] > 0 );
1085
+ s64 delta = - balance [start_idx ];
1086
+ size_t length = 0 ;
1087
+ delta = MIN (delta , balance [final_idx ]);
1088
+
1089
+ /* We can only walk backwards, now get me the legth of the path and the
1090
+ * max flow we can send through this route. */
1091
+ for (u32 cur_idx = final_idx ; cur_idx != start_idx ;
1092
+ cur_idx = prev_idx [cur_idx ]) {
1093
+ assert (cur_idx != INVALID_INDEX );
1094
+ const int dir = prev_dir [cur_idx ];
1095
+ const struct gossmap_chan * const chan = prev_chan [cur_idx ];
1096
+
1097
+ /* we could optimize here by caching the idx of the channels in
1098
+ * the path, but the bottleneck of the algorithm is the MCF
1099
+ * computation not here. */
1100
+ const u32 chan_idx = gossmap_chan_idx (gossmap , chan );
1101
+
1102
+ delta = MIN (delta , chan_flow [chan_idx ].half [dir ]);
1103
+ length ++ ;
1104
+ }
1105
+
1106
+ struct flow * f = tal (ctx , struct flow );
1107
+ f -> path = tal_arr (f , const struct gossmap_chan * , length );
1108
+ f -> dirs = tal_arr (f , int , length );
1109
+
1110
+ /* Walk again and substract the flow value (delta). */
1111
+ assert (delta > 0 );
1112
+ balance [start_idx ] += delta ;
1113
+ balance [final_idx ] -= delta ;
1114
+ for (u32 cur_idx = final_idx ; cur_idx != start_idx ;
1115
+ cur_idx = prev_idx [cur_idx ]) {
1116
+ const int dir = prev_dir [cur_idx ];
1117
+ const struct gossmap_chan * const chan = prev_chan [cur_idx ];
1118
+ const u32 chan_idx = gossmap_chan_idx (gossmap , chan );
1119
+
1120
+ length -- ;
1121
+ /* f->path and f->dirs contain the channels in the path in the
1122
+ * correct order. */
1123
+ f -> path [length ] = chan ;
1124
+ f -> dirs [length ] = dir ;
1125
+
1126
+ chan_flow [chan_idx ].half [dir ] -= delta ;
1127
+ }
1128
+ f -> delivers = amount_msat (delta * 1000 );
1129
+ return f ;
1130
+ }
1131
+
1132
+ /* Substract a flow cycle from the channel allocation. */
1133
+ static void substract_cycle (const struct gossmap * gossmap , const u32 final_idx ,
1134
+ struct chan_flow * chan_flow , const u32 * prev_idx ,
1135
+ const int * prev_dir ,
1136
+ const struct gossmap_chan * const * prev_chan )
1137
+ {
1138
+ s64 delta = INFINITE ;
1139
+ u32 cur_idx ;
1140
+
1141
+ /* Compute greatest flow in this cycle. */
1142
+ for (cur_idx = final_idx ; cur_idx != INVALID_INDEX ;) {
1143
+ const int dir = prev_dir [cur_idx ];
1144
+ const struct gossmap_chan * const chan = prev_chan [cur_idx ];
1145
+ const u32 chan_idx = gossmap_chan_idx (gossmap , chan );
1146
+
1147
+ delta = MIN (delta , chan_flow [chan_idx ].half [dir ]);
1148
+
1149
+ cur_idx = prev_idx [cur_idx ];
1150
+ if (cur_idx == final_idx )
1151
+ /* we have come back full circle */
1152
+ break ;
1153
+ }
1154
+ assert (cur_idx == final_idx );
1155
+
1156
+ /* Walk again and substract the flow value (delta). */
1157
+ assert (delta < INFINITE );
1158
+ assert (delta > 0 );
1159
+
1160
+ for (cur_idx = final_idx ;cur_idx != INVALID_INDEX ;) {
1161
+ const int dir = prev_dir [cur_idx ];
1162
+ const struct gossmap_chan * const chan = prev_chan [cur_idx ];
1163
+ const u32 chan_idx = gossmap_chan_idx (gossmap , chan );
1164
+
1165
+ chan_flow [chan_idx ].half [dir ] -= delta ;
1166
+
1167
+ cur_idx = prev_idx [cur_idx ];
1168
+ if (cur_idx == final_idx )
1169
+ /* we have come back full circle */
1170
+ break ;
1171
+ }
1172
+ assert (cur_idx == final_idx );
1173
+ }
1174
+
1072
1175
/* Given a flow in the residual network, build a set of payment flows in the
1073
1176
* gossmap that corresponds to this flow. */
1074
1177
static struct flow * *
@@ -1092,6 +1195,9 @@ get_flow_paths(const tal_t *ctx,
1092
1195
int * prev_dir = tal_arr (tmpctx ,int ,max_num_nodes );
1093
1196
u32 * prev_idx = tal_arr (tmpctx ,u32 ,max_num_nodes );
1094
1197
1198
+ for (u32 node_idx = 0 ; node_idx < max_num_nodes ; node_idx ++ )
1199
+ prev_idx [node_idx ] = INVALID_INDEX ;
1200
+
1095
1201
// Convert the arc based residual network flow into a flow in the
1096
1202
// directed channel network.
1097
1203
// Compute balance on the nodes.
@@ -1117,75 +1223,34 @@ get_flow_paths(const tal_t *ctx,
1117
1223
1118
1224
}
1119
1225
1120
-
1121
1226
// Select all nodes with negative balance and find a flow that reaches a
1122
1227
// positive balance node.
1123
1228
for (u32 node_idx = 0 ;node_idx < max_num_nodes ;++ node_idx )
1124
1229
{
1125
- // for(size_t i=0;i<tal_count(prev_idx);++i)
1126
- // {
1127
- // prev_idx[i]=INVALID_INDEX;
1128
- // }
1129
1230
// this node has negative balance, flows leaves from here
1130
1231
while (balance [node_idx ]< 0 )
1131
1232
{
1132
- prev_chan [node_idx ]= NULL ;
1133
- u32 final_idx = find_positive_balance (
1233
+ prev_chan [node_idx ] = NULL ;
1234
+ u32 final_idx = find_path_or_cycle (
1134
1235
rq -> gossmap , chan_flow , node_idx , balance ,
1135
1236
prev_chan , prev_dir , prev_idx );
1136
1237
1137
- s64 delta = - balance [node_idx ];
1138
- int length = 0 ;
1139
- delta = MIN (delta ,balance [final_idx ]);
1140
-
1141
- // walk backwards, get me the length and the max flow we
1142
- // can send.
1143
- for (u32 cur_idx = final_idx ;
1144
- cur_idx != node_idx ;
1145
- cur_idx = prev_idx [cur_idx ])
1238
+ if (balance [final_idx ] > 0 )
1239
+ /* case 1. found a path */
1146
1240
{
1147
- assert (cur_idx != INVALID_INDEX );
1148
-
1149
- const int dir = prev_dir [cur_idx ];
1150
- const struct gossmap_chan * const c = prev_chan [cur_idx ];
1151
- const u32 c_idx = gossmap_chan_idx (rq -> gossmap ,c );
1152
-
1153
- delta = MIN (delta ,chan_flow [c_idx ].half [dir ]);
1154
- length ++ ;
1155
- }
1156
-
1157
- struct flow * fp = tal (flows ,struct flow );
1158
- fp -> path = tal_arr (fp ,const struct gossmap_chan * ,length );
1159
- fp -> dirs = tal_arr (fp ,int ,length );
1160
-
1161
- balance [node_idx ] += delta ;
1162
- balance [final_idx ]-= delta ;
1163
-
1164
- // walk backwards, substract flow
1165
- for (u32 cur_idx = final_idx ;
1166
- cur_idx != node_idx ;
1167
- cur_idx = prev_idx [cur_idx ])
1241
+ struct flow * fp = substract_flow (
1242
+ flows , rq -> gossmap , node_idx , final_idx ,
1243
+ balance , chan_flow , prev_idx , prev_dir ,
1244
+ prev_chan );
1245
+
1246
+ tal_arr_expand (& flows , fp );
1247
+ } else
1248
+ /* case 2. found a cycle */
1168
1249
{
1169
- assert (cur_idx != INVALID_INDEX );
1170
-
1171
- const int dir = prev_dir [cur_idx ];
1172
- const struct gossmap_chan * const c = prev_chan [cur_idx ];
1173
- const u32 c_idx = gossmap_chan_idx (rq -> gossmap ,c );
1174
-
1175
- length -- ;
1176
- fp -> path [length ]= c ;
1177
- fp -> dirs [length ]= dir ;
1178
- // notice: fp->path and fp->dirs have the path
1179
- // in the correct order.
1180
-
1181
- chan_flow [c_idx ].half [prev_dir [cur_idx ]]-= delta ;
1250
+ substract_cycle (rq -> gossmap , final_idx ,
1251
+ chan_flow , prev_idx , prev_dir ,
1252
+ prev_chan );
1182
1253
}
1183
-
1184
- assert (delta > 0 );
1185
- fp -> delivers = amount_msat (delta * 1000 );
1186
-
1187
- // add fp to flows
1188
- tal_arr_expand (& flows , fp );
1189
1254
}
1190
1255
}
1191
1256
return flows ;
0 commit comments