diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/.gitignore b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/.gitignore new file mode 100644 index 0000000..ad694bd --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/.gitignore @@ -0,0 +1,25 @@ +.rebar3 +_build +_checkouts +_vendor +.eunit +*.o +*.beam +*.plt +*.swp +*.swo +.erlang.cookie +ebin +log +erl_crash.dump +.rebar +logs +.idea +*.iml +rebar3.crashdump +*~ + +# NOTE: For the experiment +*.csv +*.pdf +__pycache__ diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/LICENSE.md b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/LICENSE.md new file mode 100644 index 0000000..904f187 --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/LICENSE.md @@ -0,0 +1,186 @@ +# Apache License +Version 2.0, January 2004 + +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +## 1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +## 2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +## 3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +## 4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +1. You must give any other recipients of the Work or Derivative Works a copy of + this License; and + +2. You must cause any modified files to carry prominent notices stating that + You changed the files; and + +3. You must retain, in the Source form of any Derivative Works that You + distribute, all copyright, patent, trademark, and attribution notices from + the Source form of the Work, excluding those notices that do not pertain to + any part of the Derivative Works; and + +4. If the Work includes a "NOTICE" text file as part of its distribution, then + any Derivative Works that You distribute must include a readable copy of the + attribution notices contained within such NOTICE file, excluding those + notices that do not pertain to any part of the Derivative Works, in at least + one of the following places: within a NOTICE text file distributed as part + of the Derivative Works; within the Source form or documentation, if + provided along with the Derivative Works; or, within a display generated by + the Derivative Works, if and wherever such third-party notices normally + appear. The contents of the NOTICE file are for informational purposes only + and do not modify the License. You may add Your own attribution notices + within Derivative Works that You distribute, alongside or as an addendum to + the NOTICE text from the Work, provided that such additional attribution + notices cannot be construed as modifying the License. + +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +## 5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +## 6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +## 7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, NON- +INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +## 8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +## 9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +Copyright 2024, ajfAfg <56056962+ajfAfg@users.noreply.github.com>. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/README.md b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/README.md new file mode 100644 index 0000000..46e8296 --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/README.md @@ -0,0 +1,14 @@ +measure_execution_time_of_function_that_solve_optimal_supervision_tree +===== + +An escript + +Build +----- + + $ rebar3 escriptize + +Run +--- + + $ _build/default/bin/measure_execution_time_of_function_that_solve_optimal_supervision_tree diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/benchmark b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/benchmark new file mode 100755 index 0000000..db942d6 --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/benchmark @@ -0,0 +1,6 @@ +#!/bin/sh + +rebar3 escriptize >/dev/null 2>&1 + +_build/default/bin/measure_execution_time_of_function_that_solve_optimal_supervision_tree \ + >execution_time_of_function_that_solve_optimal_supervision_tree.csv diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/generate_figure.py b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/generate_figure.py new file mode 100644 index 0000000..a5d3766 --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/generate_figure.py @@ -0,0 +1,36 @@ +import matplotlib.pyplot as plt +import numpy as np + +results = np.loadtxt( + "execution_time_of_function_that_solve_optimal_supervision_tree.csv", + delimiter=",", + skiprows=1, +) + +ratios = results[:, 1] / results[:, 0] +results_per_edge = [results[ratios == ratio] for ratio in np.unique(ratios)] + +for result in results_per_edge: + # Scatter plot + x = result[:, 0].astype(int) + y = result[:, 2] + plt.rcParams["font.size"] = 22 + plt.scatter(x, y, s=100, c="pink", alpha=0.5, edgecolors="red") + plt.xticks(list(x), list(map(str, x))) + plt.xlabel("The number of gen_server (no)", labelpad=15) + plt.ylabel("execution time (s)", labelpad=20) + plt.tight_layout() + + # Line chart + x = np.unique(result[:, 0].astype(int)) + y = [ + np.median(matrix[:, 2]) + for matrix in [result[result[:, 0] == vertex_num] for vertex_num in x] + ] + plt.plot(x, y, label="Median") + plt.legend(loc="upper left") + + # Generate a graph + ratio = int((result[:, 1] / result[:, 0])[0]) + plt.savefig(f"execution_time_when_edge_num_is_{ratio}_times_vertex_num.pdf") + plt.clf() diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/rebar.config b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/rebar.config new file mode 100644 index 0000000..f8f3d1f --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/rebar.config @@ -0,0 +1,24 @@ +{erl_opts, [no_debug_info]}. + +{deps, [bean]}. + +{escript_incl_apps, + [measure_execution_time_of_function_that_solve_optimal_supervision_tree]}. + +{escript_main_app, + measure_execution_time_of_function_that_solve_optimal_supervision_tree}. + +{escript_name, measure_execution_time_of_function_that_solve_optimal_supervision_tree}. + +{escript_emu_args, "%%! +sbtu +A1\n"}. + +%% the plugin itself +{project_plugins, [rebar3_proper]}. + +%% Profiles +{profiles, + [{test, + [{deps, + %% hex + [{proper, "1.4.0"}]}, + {erl_opts, [debug_info]}]}]}. diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/rebar.lock b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/rebar.lock new file mode 100644 index 0000000..92a0942 --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/rebar.lock @@ -0,0 +1,40 @@ +{"1.2.0", +[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.12.2">>},2}, + {<<"certifi">>,{pkg,<<"certifi">>,<<"2.11.0">>},2}, + {<<"cf">>,{pkg,<<"cf">>,<<"0.3.1">>},2}, + {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.5.1">>},2}, + {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"1.4.0">>},2}, + {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.5.0">>},2}, + {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},2}, + {<<"providers">>,{pkg,<<"providers">>,<<"1.9.0">>},1}, + {<<"rebar">>, + {git_subdir,"https://github.com/erlang/rebar3.git", + {ref,"0a432f2f60cd32b8ff07d644a506dba15eb6f04a"}, + "apps/rebar"}, + 1}, + {<<"relx">>,{pkg,<<"relx">>,<<"4.8.0">>},2}, + {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},2}]}. +[ +{pkg_hash,[ + {<<"bbmustache">>, <<"0CABDCE0DB9FE6D3318131174B9F2B351328A4C0AFBEB3E6E99BB0E02E9B621D">>}, + {<<"certifi">>, <<"5ADFE37CEB8569D019F836944AEAF27F8AC391DACAF3707F570C155B7E03AAA8">>}, + {<<"cf">>, <<"5CB902239476E141EA70A740340233782D363A31EEA8AD37049561542E6CD641">>}, + {<<"cth_readable">>, <<"F511EFCFDE04A48B014A9197FFF1B4C4860E4E35CDB8E2F3AE3C4178E20299B1">>}, + {<<"erlware_commons">>, <<"F9EE38412E1413944BE78736DDB9BDB4C0664ABA5133563F4F3B359A8ED0AD52">>}, + {<<"eunit_formatters">>, <<"6A9133943D36A465D804C1C5B6E6839030434B8879C5600D7DDB5B3BAD4CCB59">>}, + {<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>}, + {<<"providers">>, <<"46F6645B0C677B1029E02B013BFD69092A2232854DAF359F2378FA42AC0BEC0D">>}, + {<<"relx">>, <<"A95AD845C5F010875E7AF2B27BA07472B3D92D872AD52AC5CBF282C91338EAC6">>}, + {<<"ssl_verify_fun">>, <<"CF344F5692C82D2CD7554F5EC8FD961548D4FD09E7D22F5B62482E5AEAEBD4B0">>}]}, +{pkg_hash_ext,[ + {<<"bbmustache">>, <<"688B33A4D5CC2D51F575ADF0B3683FC40A38314A2F150906EDCFC77F5B577B3B">>}, + {<<"certifi">>, <<"9E37E0542EC3FABAA19A0734B3900DC095797FAC48C40A2A9741D8AD5E3C9BB7">>}, + {<<"cf">>, <<"315E8D447D3A4B02BCDBFA397AD03BBB988A6E0AA6F44D3ADD0F4E3C3BF97672">>}, + {<<"cth_readable">>, <<"686541A22EFE6CA5A41A047B39516C2DD28FB3CADE5F24A2F19145B3967F9D80">>}, + {<<"erlware_commons">>, <<"185ECF5CF43BAB3A013DDB3614CE7BBA7F6C7A827904E64E57DA54FCDFDCE2E6">>}, + {<<"eunit_formatters">>, <<"D6C8BA213424944E6E05BBC097C32001CDD0ABE3925D02454F229B20D68763C9">>}, + {<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>}, + {<<"providers">>, <<"D287E874406A1505608642B0A3DB5B68D6ADA3F2AB001AEC87E7F4D7C79FC017">>}, + {<<"relx">>, <<"C98E3D4351C30219F252B99CA21D84E847FBA7032876990911206FE6F75398DA">>}, + {<<"ssl_verify_fun">>, <<"BDB0D2471F453C88FF3908E7686F86F9BE327D065CC1EC16FA4540197EA04680">>}]} +]. diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/dependency_graph_generator.erl b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/dependency_graph_generator.erl new file mode 100644 index 0000000..61cf48f --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/dependency_graph_generator.erl @@ -0,0 +1,14 @@ +-module(dependency_graph_generator). + +-export([generate_randomly/2]). + +-spec generate_randomly(non_neg_integer(), non_neg_integer()) -> dependency_graph:t(). +generate_randomly(VertexNum, EdgeNum) + when 0 =< VertexNum andalso 0 =< EdgeNum andalso EdgeNum =< VertexNum * (VertexNum - 1) -> + Vertices = [list_to_atom(integer_to_list(X)) || X <- lists:seq(1, VertexNum)], + MaximumValidEdges = [{V1, V2} || V1 <- Vertices, V2 <- Vertices, V1 =/= V2], + Edges = + lists:nthtail(length(MaximumValidEdges) - EdgeNum, my_lists:shuffle(MaximumValidEdges)), + lists:foldl(fun({V1, V2}, Acc) -> maps:update_with(V1, fun(Vs) -> [V2 | Vs] end, Acc) end, + maps:from_keys(Vertices, []), + Edges). diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/measure_execution_time_of_function_that_solve_optimal_supervision_tree.app.src b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/measure_execution_time_of_function_that_solve_optimal_supervision_tree.app.src new file mode 100644 index 0000000..e73ddb0 --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/measure_execution_time_of_function_that_solve_optimal_supervision_tree.app.src @@ -0,0 +1,10 @@ +{application, + measure_execution_time_of_function_that_solve_optimal_supervision_tree, + [{description, "An escript"}, + {vsn, "0.1.0"}, + {registered, []}, + {applications, [kernel, stdlib, bean]}, + {env, []}, + {modules, []}, + {licenses, ["Apache-2.0"]}, + {links, []}]}. diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/measure_execution_time_of_function_that_solve_optimal_supervision_tree.erl b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/measure_execution_time_of_function_that_solve_optimal_supervision_tree.erl new file mode 100644 index 0000000..dab70ee --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/measure_execution_time_of_function_that_solve_optimal_supervision_tree.erl @@ -0,0 +1,53 @@ +-module(measure_execution_time_of_function_that_solve_optimal_supervision_tree). + +%% API exports +-export([main/1]). + +%%==================================================================== +%% API functions +%%==================================================================== + +%% escript Entry point +main(_Args) -> + io:format("vertex_num(no), edge_num(no), execution_time(s)~n"), + + lists:foreach(fun({VertexNum, EdgeNum}) -> + lists:foreach(fun(_) -> + Graph = + dependency_graph_generator:generate_randomly(VertexNum, + EdgeNum), + + StartTime = erlang:monotonic_time(), + _ = optimum_supervision_tree_solver:solve(Graph, + fun all_local_minimum_vertex_splitters_solver:solve_in_polynomial_time_without_correctness/1), + StopTime = erlang:monotonic_time(), + + ExecutionTimeStr = + begin + ExecutionTime = StopTime - StartTime, + Second = + erlang:convert_time_unit(ExecutionTime, + native, + second), + Millisecond = + erlang:convert_time_unit(ExecutionTime, + native, + millisecond), + io_lib:format("~B.~3..0B~n", + [Second, + Millisecond + - erlang:convert_time_unit(Second, + second, + millisecond)]) + end, + io:format("~p,~p,~s", [VertexNum, EdgeNum, ExecutionTimeStr]) + end, + lists:seq(1, parameters:trial_num())) + end, + parameters:vertex_num_and_edge_num_list()), + + erlang:halt(0). + +%%==================================================================== +%% Internal functions +%%==================================================================== diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/parameters.erl b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/parameters.erl new file mode 100644 index 0000000..d9f1fc8 --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/src/parameters.erl @@ -0,0 +1,10 @@ +-module(parameters). + +-export([vertex_num_and_edge_num_list/0, trial_num/0]). + +vertex_num_and_edge_num_list() -> + [{VertexNum, EdgeNum} + || VertexNum <- lists:seq(10, 80, 10), + EdgeNum <- lists:map(fun(X) -> VertexNum * X end, [1, 2, 3])]. + +trial_num() -> 20. diff --git a/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/test/prop_dependency_graph_generator.erl b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/test/prop_dependency_graph_generator.erl new file mode 100644 index 0000000..f90a5a8 --- /dev/null +++ b/benchmarks/measure_execution_time_of_function_that_solve_optimal_supervision_tree/test/prop_dependency_graph_generator.erl @@ -0,0 +1,40 @@ +-module(prop_dependency_graph_generator). + +-compile(export_all). + +-include_lib("proper/include/proper.hrl"). + +vertex_num_and_edge_num() -> + ?SUCHTHAT({VertexNum, EdgeNum}, + {pos_integer(), pos_integer()}, + EdgeNum =< VertexNum * (VertexNum - 1)). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% dependency_graph_generator:generate_randomly/2 %%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +prop_generate_randomly1(doc) -> + "The number of the vertices of the return value equals the argument `VertexNum`". + +prop_generate_randomly1() -> + ?FORALL({VertexNum, EdgeNum}, + vertex_num_and_edge_num(), + begin + VertexNum + =:= length(maps:keys( + dependency_graph_generator:generate_randomly(VertexNum, EdgeNum))) + end). + +prop_generate_randomly2(doc) -> + "The number of the edges of the return value equals the argument `EdgeNum`". + +prop_generate_randomly2() -> + ?FORALL({VertexNum, EdgeNum}, + vertex_num_and_edge_num(), + begin + EdgeNum + =:= lists:sum( + lists:map(fun erlang:length/1, + maps:values( + dependency_graph_generator:generate_randomly(VertexNum, + EdgeNum)))) + end).