Skip to content

Commit

Permalink
Merge pull request #27 from ajfAfg/fix-solve_in_exp_time_with_c-orrec…
Browse files Browse the repository at this point in the history
…tness-output-result-to-satisfy-all

fix solve in exp time with c orrectness output result to satisfy all
  • Loading branch information
ajfAfg authored Jan 3, 2024
2 parents 4b0d555 + 933fbe7 commit 8739dde
Show file tree
Hide file tree
Showing 5 changed files with 135 additions and 18 deletions.
51 changes: 38 additions & 13 deletions src/core/all_local_minimum_vertex_splitters_solver.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
-type take_all_local_minimum_vertex_splitters() ::
fun((dependency_digraph:t()) -> [[dependency_digraph:vertex()]]).

%% ===================================================================
%% Public API
%% ===================================================================
% NOTE:
% The argument graph is assumed to be a connected DAG.
% To reduce computation time, do not check whether `ConnectedDAG` is a connected DAG.
Expand Down Expand Up @@ -39,31 +42,38 @@ solve_in_exp_time_with_correctness(ConnectedDAG) ->
digraph:vertices(ConnectedDAG)),
dependency_digraph:is_vertex_splitter(ConnectedDAG, Vertices)],
Candidates2 =
lists:usort(
maps:values(
begin
MinimumVertexSplittersForEveryExitVertex =
lists:foldl(fun(VertexSplitter, Acc) ->
lists:foldl(fun(ExitVertex, Acc2) ->
maps:update_with(ExitVertex,
fun(VertexSplitter2) ->
case length(VertexSplitter)
< length(VertexSplitter2)
fun(VertexSplitters) ->
case
compare(length(VertexSplitter),
length(hd(VertexSplitters)))
of
true -> VertexSplitter;
false -> VertexSplitter2
less -> [VertexSplitter];
greater -> VertexSplitters;
equal ->
[VertexSplitter
| VertexSplitters]
end
end,
Acc2)
end,
Acc,
[V
|| V <- VertexSplitter,
digraph:out_degree(ConnectedDAG, V) =:= 0])
is_exit_vertex(ConnectedDAG, V)])
end,
maps:from_keys([V
|| V <- digraph:vertices(ConnectedDAG),
digraph:out_degree(ConnectedDAG, V) =:= 0],
digraph:vertices(ConnectedDAG)),
Candidates))),
maps:from_keys(exit_vertices(ConnectedDAG),
[digraph:vertices(ConnectedDAG)]),
Candidates),
lists:usort(
lists:map(fun lists:sort/1,
my_lists:flatten(
maps:values(MinimumVertexSplittersForEveryExitVertex))))
end,
[S1
|| S1 <- Candidates2,
not
Expand All @@ -72,3 +82,18 @@ solve_in_exp_time_with_correctness(ConnectedDAG) ->
sets:from_list(S2), sets:from_list(S1))
end,
lists:delete(S1, Candidates2))].

%% ===================================================================
%% Private API
%% ===================================================================
-spec compare(term(), term()) -> less | greater | equal.
compare(X, Y) when X < Y -> less;
compare(X, Y) when X > Y -> greater;
compare(_, _) -> equal.

-spec exit_vertices(digraph:graph()) -> [digraph:vertex()].
exit_vertices(Digraph) ->
[V || V <- digraph:vertices(Digraph), is_exit_vertex(Digraph, V)].

-spec is_exit_vertex(digraph:graph(), digraph:vertex()) -> boolean().
is_exit_vertex(Digraph, Vertex) -> digraph:out_degree(Digraph, Vertex) =:= 0.
6 changes: 5 additions & 1 deletion src/core/my_lists.erl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
-module(my_lists).

-export([shuffle/1, sublist_randomly/1, power/1]).
-export([shuffle/1, sublist_randomly/1, power/1, flatten/1]).

-spec shuffle(list()) -> list().
shuffle(List) -> [X || {_, X} <- lists:sort([{rand:uniform(), Y} || Y <- List])].
Expand All @@ -16,3 +16,7 @@ power(List) -> power_([], List, []).
power_(Subset, [], Family) -> [Subset | Family];
power_(Subset, [Head | Rest], Family) ->
power_(Subset, Rest, power_([Head | Subset], Rest, Family)).

% NOTE: Unlike `lists:flatten/1`, this function removes only one level of the nested list.
-spec flatten([list()]) -> list().
flatten(ListList) -> [X || List <- ListList, X <- List].
66 changes: 62 additions & 4 deletions test/all_local_minimum_vertex_splitters_solver_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ solve_in_polynomial_time_without_correctness_test_() ->

solve_in_exp_time_with_correctness_test_() ->
{inparallel,
[{"Critical test cases",
[{"Satisfy all",
fun() ->
G1 = my_digraph:create(
lists:seq(1, 6), [{1, 3}, {1, 4}, {2, 4}, {2, 5}, {3, 6}, {4, 6}, {5, 6}]),
Expand All @@ -51,10 +51,68 @@ solve_in_exp_time_with_correctness_test_() ->
lists:map(fun lists:sort/1,
all_local_minimum_vertex_splitters_solver:solve_in_exp_time_with_correctness(G2)))),
G3 = my_digraph:create(
lists:seq(1, 3), [{1, 2}, {2, 3}]),
lists:seq(1, 12),
[{1, 4},
{1, 6},
{2, 1},
{2, 6},
{3, 1},
{5, 1},
{6, 7},
{8, 6},
{8, 10},
{9, 8},
{11, 10},
{12, 9}]),
?assertEqual(lists:sort(
lists:map(fun lists:sort/1, [[1, 2, 3]])),
lists:map(fun lists:sort/1, [[6, 7], [10]])),
lists:sort(
lists:map(fun lists:sort/1,
all_local_minimum_vertex_splitters_solver:solve_in_exp_time_with_correctness(G3)))),
G4 = my_digraph:create(
lists:seq(1, 18),
[{1, 2},
{2, 3},
{2, 4},
{4, 5},
{5, 6},
{7, 3},
{8, 9},
{9, 6},
{10, 1},
{10, 11},
{10, 12},
{13, 14},
{14, 12},
{14, 15},
{15, 11},
{15, 16},
{15, 17},
{18, 17}]),
?assertEqual(lists:sort(
lists:map(fun lists:sort/1, [[3], [6], [11, 12], [17]])),
lists:sort(
lists:map(fun lists:sort/1,
all_local_minimum_vertex_splitters_solver:solve_in_exp_time_with_correctness(G4)))),
G5 = my_digraph:create(
lists:seq(1, 14),
[{1, 2},
{1, 5},
{1, 7},
{2, 3},
{4, 2},
{6, 5},
{7, 8},
{9, 10},
{10, 11},
{10, 12},
{12, 13},
{13, 8},
{13, 14},
{14, 8}]),
?assertEqual(lists:sort(
lists:map(fun lists:sort/1, [[2, 3], [5], [8]])),
lists:sort(
lists:map(fun lists:sort/1,
all_local_minimum_vertex_splitters_solver:solve_in_exp_time_with_correctness(G3))))
all_local_minimum_vertex_splitters_solver:solve_in_exp_time_with_correctness(G5))))
end}]}.
16 changes: 16 additions & 0 deletions test/prop_all_local_minimum_vertex_splitters_solver.erl
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,19 @@ prop_solve_in_exp_time_with_correctness3() ->
end,
all_local_minimum_vertex_splitters_solver:solve_in_exp_time_with_correctness(ConnectedDAG))
end).

prop_solve_in_exp_time_with_correctness4(doc) ->
"The return value equals the vertices of the argument graph if only one entrance vertex".

prop_solve_in_exp_time_with_correctness4() ->
?FORALL(ConnectedDAG,
dependency_connected_dag(),
begin
?IMPLIES(length([V
|| V <- digraph:vertices(ConnectedDAG),
digraph:in_degree(ConnectedDAG, V) =:= 0])
=:= 1,
lists:sort(
digraph:vertices(ConnectedDAG))
=:= lists:sort(hd(all_local_minimum_vertex_splitters_solver:solve_in_exp_time_with_correctness(ConnectedDAG))))
end).
14 changes: 14 additions & 0 deletions test/prop_my_lists.erl
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,17 @@ prop_power2() ->
?SUCHTHAT(L, list(random_type()), length(L) =< 15),
lists:all(fun(List2) -> lists:sort(List2 ++ List -- List2) =:= lists:sort(List) end,
my_lists:power(List))).

%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% my_lists:flatten/1 %%%
%%%%%%%%%%%%%%%%%%%%%%%%%%
% NOTE:
% It is easy to give a different implementation,
% so I test by "Wording the specification differently".
prop_flatten1(doc) -> "Wording the specification differently".

prop_flatten1() ->
?FORALL(ListList,
list(list(random_type())),
lists:foldl(fun(List, Acc) -> List ++ Acc end, [], lists:reverse(ListList))
=:= my_lists:flatten(ListList)).

0 comments on commit 8739dde

Please sign in to comment.