Is it possible to draw those 521 connections with no overlap? - graphviz
Consider the following graphviz file - is it possible to draw a graph for it? I experimented with sfdp, K, repulsiveforce, overlap, but I couldn't find any decent setup:
digraph {
"setup_dirs_fds" -> "snprintf"
"setup_dirs_fds" -> "fdopen"
"setup_dirs_fds" -> "mkdir"
"setup_dirs_fds" -> "__errno_location"
"setup_dirs_fds" -> "strerror"
"setup_dirs_fds" -> "DFL_ck_alloc"
"setup_dirs_fds" -> "DFL_ck_free"
"setup_dirs_fds" -> "exit"
"setup_dirs_fds" -> "printf"
"setup_dirs_fds" -> "fprintf"
"setup_dirs_fds" -> "fflush"
"setup_dirs_fds" -> "maybe_delete_out_dir"
"setup_dirs_fds" -> "open"
"setup_dirs_fds" -> "flock"
"afl_main" -> "setup_dirs_fds"
"afl_main" -> "sscanf"
"afl_main" -> "write_bitmap"
"afl_main" -> "get_qemu_argv"
"afl_main" -> "perform_dry_run"
"afl_main" -> "check_cpu_governor"
"afl_main" -> "fclose"
"afl_main" -> "get_cur_time"
"afl_main" -> "check_if_tty"
"afl_main" -> "destroy_queue"
"afl_main" -> "setup_signal_handlers"
"afl_main" -> "sleep"
"afl_main" -> "find_start_position"
"afl_main" -> "printf"
"afl_main" -> "check_crash_handling"
"afl_main" -> "get_core_count"
"afl_main" -> "fflush"
"afl_main" -> "DFL_ck_alloc"
"afl_main" -> "fix_up_sync"
"afl_main" -> "sync_fuzzers"
"afl_main" -> "write_stats_file"
"afl_main" -> "save_cmdline"
"afl_main" -> "detect_file_args"
"afl_main" -> "show_init_stats"
"afl_main" -> "access"
"afl_main" -> "setup_post"
"afl_main" -> "DFL_ck_free"
"afl_main" -> "exit"
"afl_main" -> "usage"
"afl_main" -> "getopt"
"afl_main" -> "setup_stdio_file"
"afl_main" -> "read_bitmap"
"afl_main" -> "destroy_extras"
"afl_main" -> "read_testcases"
"afl_main" -> "pivot_inputs"
"afl_main" -> "fix_up_banner"
"afl_main" -> "getenv"
"afl_main" -> "setup_shm"
"afl_main" -> "cull_queue"
"afl_main" -> "save_auto"
"afl_main" -> "show_stats"
"afl_main" -> "find_timeout"
"afl_main" -> "load_extras"
"afl_main" -> "load_auto"
"afl_main" -> "check_binary"
"afl_main" -> "check_asan_opts"
"afl_main" -> "fuzz_one"
"afl_main" -> "init_G"
"afl_main" -> "strcmp"
"write_bitmap" -> "snprintf"
"write_bitmap" -> "__errno_location"
"write_bitmap" -> "strerror"
"write_bitmap" -> "DFL_ck_free"
"write_bitmap" -> "exit"
"write_bitmap" -> "printf"
"write_bitmap" -> "close"
"write_bitmap" -> "fflush"
"write_bitmap" -> "open"
"write_bitmap" -> "DFL_ck_alloc"
"get_qemu_argv" -> "snprintf"
"get_qemu_argv" -> "memcpy"
"get_qemu_argv" -> "getenv"
"get_qemu_argv" -> "access"
"get_qemu_argv" -> "DFL_ck_free"
"get_qemu_argv" -> "exit"
"get_qemu_argv" -> "printf"
"get_qemu_argv" -> "strrchr"
"get_qemu_argv" -> "DFL_ck_strdup"
"get_qemu_argv" -> "DFL_ck_alloc"
"perform_dry_run" -> "calibrate_case"
"perform_dry_run" -> "DFL_ck_alloc_nozero"
"perform_dry_run" -> "check_map_coverage"
"perform_dry_run" -> "__errno_location"
"perform_dry_run" -> "strerror"
"perform_dry_run" -> "getenv"
"perform_dry_run" -> "DFL_ck_free"
"perform_dry_run" -> "exit"
"perform_dry_run" -> "DMS"
"perform_dry_run" -> "printf"
"perform_dry_run" -> "close"
"perform_dry_run" -> "strrchr"
"perform_dry_run" -> "fflush"
"perform_dry_run" -> "open"
"check_cpu_governor" -> "strncmp"
"check_cpu_governor" -> "fscanf"
"check_cpu_governor" -> "fclose"
"check_cpu_governor" -> "__errno_location"
"check_cpu_governor" -> "strerror"
"check_cpu_governor" -> "getenv"
"check_cpu_governor" -> "exit"
"check_cpu_governor" -> "printf"
"check_cpu_governor" -> "fgets"
"check_cpu_governor" -> "fflush"
"check_cpu_governor" -> "fopen"
"nuke_resume_dir" -> "snprintf"
"nuke_resume_dir" -> "__errno_location"
"nuke_resume_dir" -> "DFL_ck_free"
"nuke_resume_dir" -> "exit"
"nuke_resume_dir" -> "delete_files"
"nuke_resume_dir" -> "printf"
"nuke_resume_dir" -> "DFL_ck_alloc"
"nuke_resume_dir" -> "rmdir"
"maybe_update_plot_file" -> "fflush"
"maybe_update_plot_file" -> "fprintf"
"maybe_update_plot_file" -> "get_cur_time"
"check_if_tty" -> "ioctl"
"check_if_tty" -> "__errno_location"
"check_if_tty" -> "printf"
"destroy_queue" -> "DFL_ck_free"
"setup_signal_handlers" -> "sigemptyset"
"setup_signal_handlers" -> "sigaction"
"save_if_interesting" -> "calibrate_case"
"save_if_interesting" -> "snprintf"
"save_if_interesting" -> "get_cur_time"
"save_if_interesting" -> "open"
"save_if_interesting" -> "has_new_bits"
"save_if_interesting" -> "add_to_queue"
"save_if_interesting" -> "__errno_location"
"save_if_interesting" -> "hash32"
"save_if_interesting" -> "write_crash_readme"
"save_if_interesting" -> "strerror"
"save_if_interesting" -> "simplify_trace"
"save_if_interesting" -> "DFL_ck_free"
"save_if_interesting" -> "exit"
"save_if_interesting" -> "printf"
"save_if_interesting" -> "close"
"save_if_interesting" -> "fflush"
"save_if_interesting" -> "describe_op"
"save_if_interesting" -> "DFL_ck_alloc"
"find_start_position" -> "strstr"
"find_start_position" -> "snprintf"
"find_start_position" -> "DFL_ck_free"
"find_start_position" -> "exit"
"find_start_position" -> "printf"
"find_start_position" -> "close"
"find_start_position" -> "atoi"
"find_start_position" -> "open"
"find_start_position" -> "DFL_ck_alloc"
"check_crash_handling" -> "close"
"check_crash_handling" -> "open"
"check_crash_handling" -> "printf"
"load_auto" -> "snprintf"
"load_auto" -> "maybe_add_auto"
"load_auto" -> "__errno_location"
"load_auto" -> "strerror"
"load_auto" -> "DFL_ck_free"
"load_auto" -> "exit"
"load_auto" -> "printf"
"load_auto" -> "close"
"load_auto" -> "fflush"
"load_auto" -> "open"
"load_auto" -> "DFL_ck_alloc"
"link_or_copy" -> "__errno_location"
"link_or_copy" -> "strerror"
"link_or_copy" -> "DFL_ck_free"
"link_or_copy" -> "link"
"link_or_copy" -> "printf"
"link_or_copy" -> "close"
"link_or_copy" -> "exit"
"link_or_copy" -> "fflush"
"link_or_copy" -> "open"
"link_or_copy" -> "DFL_ck_alloc"
"DFL_ck_alloc" -> "memset"
"DFL_ck_alloc" -> "DFL_ck_alloc_nozero"
"read_testcases" -> "strstr"
"read_testcases" -> "snprintf"
"read_testcases" -> "lstat"
"read_testcases" -> "add_to_queue"
"read_testcases" -> "__errno_location"
"read_testcases" -> "strerror"
"read_testcases" -> "free"
"read_testcases" -> "access"
"read_testcases" -> "DFL_ck_free"
"read_testcases" -> "exit"
"read_testcases" -> "DMS"
"read_testcases" -> "printf"
"read_testcases" -> "fflush"
"read_testcases" -> "DFL_ck_alloc"
"read_testcases" -> "scandir"
"mark_as_redundant" -> "snprintf"
"mark_as_redundant" -> "__errno_location"
"mark_as_redundant" -> "strerror"
"mark_as_redundant" -> "DFL_ck_free"
"mark_as_redundant" -> "exit"
"mark_as_redundant" -> "printf"
"mark_as_redundant" -> "close"
"mark_as_redundant" -> "strrchr"
"mark_as_redundant" -> "fflush"
"mark_as_redundant" -> "unlink"
"mark_as_redundant" -> "open"
"mark_as_redundant" -> "DFL_ck_alloc"
"DFL_ck_realloc" -> "realloc"
"DFL_ck_realloc" -> "DFL_ck_free"
"DFL_ck_realloc" -> "abort"
"DFL_ck_realloc" -> "memset"
"DFL_ck_realloc" -> "printf"
"load_extras_file" -> "strchr"
"load_extras_file" -> "DMS"
"load_extras_file" -> "tolower"
"load_extras_file" -> "DFL_ck_realloc_block"
"load_extras_file" -> "fclose"
"load_extras_file" -> "__errno_location"
"load_extras_file" -> "strerror"
"load_extras_file" -> "__ctype_b_loc"
"load_extras_file" -> "atoi"
"load_extras_file" -> "exit"
"load_extras_file" -> "printf"
"load_extras_file" -> "fgets"
"load_extras_file" -> "fflush"
"load_extras_file" -> "fopen"
"load_extras_file" -> "strlen"
"load_extras_file" -> "DFL_ck_alloc"
"sync_fuzzers" -> "show_stats"
"sync_fuzzers" -> "fstat"
"sync_fuzzers" -> "sscanf"
"sync_fuzzers" -> "snprintf"
"sync_fuzzers" -> "save_if_interesting"
"sync_fuzzers" -> "write_to_testcase"
"sync_fuzzers" -> "__errno_location"
"sync_fuzzers" -> "strerror"
"sync_fuzzers" -> "readdir"
"sync_fuzzers" -> "DFL_ck_alloc"
"sync_fuzzers" -> "closedir"
"sync_fuzzers" -> "opendir"
"sync_fuzzers" -> "DFL_ck_free"
"sync_fuzzers" -> "exit"
"sync_fuzzers" -> "sprintf"
"sync_fuzzers" -> "printf"
"sync_fuzzers" -> "close"
"sync_fuzzers" -> "fflush"
"sync_fuzzers" -> "run_target"
"sync_fuzzers" -> "open"
"sync_fuzzers" -> "strcmp"
"write_stats_file" -> "snprintf"
"write_stats_file" -> "fdopen"
"write_stats_file" -> "fclose"
"write_stats_file" -> "__errno_location"
"write_stats_file" -> "strerror"
"write_stats_file" -> "get_cur_time"
"write_stats_file" -> "DFL_ck_free"
"write_stats_file" -> "exit"
"write_stats_file" -> "getpid"
"write_stats_file" -> "printf"
"write_stats_file" -> "fprintf"
"write_stats_file" -> "fflush"
"write_stats_file" -> "open"
"write_stats_file" -> "DFL_ck_alloc"
"check_binary" -> "strchr"
"check_binary" -> "strncmp"
"check_binary" -> "stat"
"check_binary" -> "snprintf"
"check_binary" -> "memcmp"
"check_binary" -> "__errno_location"
"check_binary" -> "strerror"
"check_binary" -> "memcpy"
"check_binary" -> "getenv"
"check_binary" -> "DFL_ck_free"
"check_binary" -> "exit"
"check_binary" -> "printf"
"check_binary" -> "close"
"check_binary" -> "fflush"
"check_binary" -> "DFL_ck_strdup"
"check_binary" -> "open"
"check_binary" -> "DFL_ck_alloc"
"detect_file_args" -> "strstr"
"detect_file_args" -> "snprintf"
"detect_file_args" -> "__errno_location"
"detect_file_args" -> "strerror"
"detect_file_args" -> "free"
"detect_file_args" -> "DFL_ck_free"
"detect_file_args" -> "exit"
"detect_file_args" -> "printf"
"detect_file_args" -> "fflush"
"detect_file_args" -> "DFL_ck_alloc"
"show_init_stats" -> "DMS"
"show_init_stats" -> "DI"
"show_init_stats" -> "printf"
"check_term_size" -> "ioctl"
"mark_as_det_done" -> "snprintf"
"mark_as_det_done" -> "__errno_location"
"mark_as_det_done" -> "strerror"
"mark_as_det_done" -> "DFL_ck_free"
"mark_as_det_done" -> "exit"
"mark_as_det_done" -> "printf"
"mark_as_det_done" -> "close"
"mark_as_det_done" -> "strrchr"
"mark_as_det_done" -> "fflush"
"mark_as_det_done" -> "open"
"mark_as_det_done" -> "DFL_ck_alloc"
"setup_post" -> ""
"setup_post" -> "dlsym"
"setup_post" -> "getenv"
"setup_post" -> "exit"
"setup_post" -> "dlerror"
"setup_post" -> "printf"
"setup_post" -> "dlopen"
"DFL_ck_free" -> "abort"
"DFL_ck_free" -> "free"
"DFL_ck_free" -> "printf"
"add_to_queue" -> "DFL_ck_alloc"
"add_to_queue" -> "get_cur_time"
"usage" -> "exit"
"usage" -> "printf"
"setup_stdio_file" -> "snprintf"
"setup_stdio_file" -> "__errno_location"
"setup_stdio_file" -> "strerror"
"setup_stdio_file" -> "DFL_ck_free"
"setup_stdio_file" -> "exit"
"setup_stdio_file" -> "printf"
"setup_stdio_file" -> "fflush"
"setup_stdio_file" -> "unlink"
"setup_stdio_file" -> "open"
"setup_stdio_file" -> "DFL_ck_alloc"
"describe_op" -> "strlen"
"describe_op" -> "sprintf"
"describe_op" -> "strcat"
"read_bitmap" -> "__errno_location"
"read_bitmap" -> "strerror"
"read_bitmap" -> "exit"
"read_bitmap" -> "printf"
"read_bitmap" -> "close"
"read_bitmap" -> "fflush"
"read_bitmap" -> "open"
"destroy_extras" -> "DFL_ck_free"
"fix_up_sync" -> "snprintf"
"fix_up_sync" -> "exit"
"fix_up_sync" -> "printf"
"fix_up_sync" -> "__ctype_b_loc"
"fix_up_sync" -> "strlen"
"fix_up_sync" -> "DFL_ck_alloc"
"save_cmdline" -> "strlen"
"save_cmdline" -> "DFL_ck_alloc"
"save_cmdline" -> "memcpy"
"DFL_ck_memdup_str" -> "malloc"
"DFL_ck_memdup_str" -> "abort"
"DFL_ck_memdup_str" -> "memcpy"
"DFL_ck_memdup_str" -> "printf"
"pivot_inputs" -> "strncmp"
"pivot_inputs" -> "strchr"
"pivot_inputs" -> "sscanf"
"pivot_inputs" -> "snprintf"
"pivot_inputs" -> "nuke_resume_dir"
"pivot_inputs" -> "mark_as_det_done"
"pivot_inputs" -> "strstr"
"pivot_inputs" -> "DFL_ck_free"
"pivot_inputs" -> "exit"
"pivot_inputs" -> "printf"
"pivot_inputs" -> "strrchr"
"pivot_inputs" -> "link_or_copy"
"pivot_inputs" -> "DFL_ck_alloc"
"fix_up_banner" -> "strrchr"
"fix_up_banner" -> "strlen"
"fix_up_banner" -> "DFL_ck_alloc"
"fix_up_banner" -> "sprintf"
"find_timeout" -> "strstr"
"find_timeout" -> "snprintf"
"find_timeout" -> "DFL_ck_free"
"find_timeout" -> "exit"
"find_timeout" -> "printf"
"find_timeout" -> "close"
"find_timeout" -> "atoi"
"find_timeout" -> "open"
"find_timeout" -> "DFL_ck_alloc"
"cull_queue" -> "mark_as_redundant"
"cull_queue" -> "memset"
"save_auto" -> "snprintf"
"save_auto" -> "__errno_location"
"save_auto" -> "strerror"
"save_auto" -> "DFL_ck_free"
"save_auto" -> "exit"
"save_auto" -> "printf"
"save_auto" -> "close"
"save_auto" -> "fflush"
"save_auto" -> "open"
"save_auto" -> "DFL_ck_alloc"
"show_stats" -> "get_runnable_processes"
"show_stats" -> "count_bits"
"show_stats" -> "memset"
"show_stats" -> "write_bitmap"
"show_stats" -> "write_stats_file"
"show_stats" -> "DI"
"show_stats" -> "DF"
"show_stats" -> "printf"
"show_stats" -> "check_term_size"
"show_stats" -> "maybe_update_plot_file"
"show_stats" -> "get_cur_time"
"show_stats" -> "strcat"
"show_stats" -> "DTD"
"show_stats" -> "strcpy"
"show_stats" -> "sprintf"
"show_stats" -> "count_non_255_bytes"
"show_stats" -> "save_auto"
"show_stats" -> "getenv"
"show_stats" -> "strlen"
"show_stats" -> "fflush"
"DFL_ck_alloc_nozero" -> "malloc"
"DFL_ck_alloc_nozero" -> "abort"
"DFL_ck_alloc_nozero" -> "printf"
"DFL_ck_realloc_block" -> "DFL_ck_realloc"
"DFL_ck_realloc_block" -> "abort"
"DFL_ck_realloc_block" -> "printf"
"write_crash_readme" -> "DMS"
"write_crash_readme" -> "snprintf"
"write_crash_readme" -> "fdopen"
"write_crash_readme" -> "fprintf"
"write_crash_readme" -> "fclose"
"write_crash_readme" -> "DFL_ck_free"
"write_crash_readme" -> "exit"
"write_crash_readme" -> "printf"
"write_crash_readme" -> "close"
"write_crash_readme" -> "open"
"write_crash_readme" -> "DFL_ck_alloc"
"check_map_coverage" -> "count_bytes"
"check_map_coverage" -> "printf"
"write_to_testcase" -> "lseek"
"write_to_testcase" -> "__errno_location"
"write_to_testcase" -> "strerror"
"write_to_testcase" -> "exit"
"write_to_testcase" -> "printf"
"write_to_testcase" -> "close"
"write_to_testcase" -> "ftruncate"
"write_to_testcase" -> "fflush"
"write_to_testcase" -> "unlink"
"write_to_testcase" -> "open"
"handle_timeout" -> "kill"
"load_extras" -> "strchr"
"load_extras" -> "load_extras_file"
"load_extras" -> "DMS"
"load_extras" -> "DFL_ck_realloc_block"
"load_extras" -> "snprintf"
"load_extras" -> "lstat"
"load_extras" -> "__errno_location"
"load_extras" -> "strerror"
"load_extras" -> "readdir"
"load_extras" -> "access"
"load_extras" -> "closedir"
"load_extras" -> "opendir"
"load_extras" -> "atoi"
"load_extras" -> "exit"
"load_extras" -> "printf"
"load_extras" -> "close"
"load_extras" -> "DFL_ck_free"
"load_extras" -> "fflush"
"load_extras" -> "open"
"load_extras" -> "DFL_ck_alloc"
"DFL_ck_strdup" -> "malloc"
"DFL_ck_strdup" -> "abort"
"DFL_ck_strdup" -> "strlen"
"DFL_ck_strdup" -> "memcpy"
"DFL_ck_strdup" -> "printf"
"DFL_ck_memdup" -> "malloc"
"DFL_ck_memdup" -> "abort"
"DFL_ck_memdup" -> "memcpy"
"DFL_ck_memdup" -> "printf"
"check_asan_opts" -> "exit"
"check_asan_opts" -> "printf"
"check_asan_opts" -> "strstr"
"check_asan_opts" -> "getenv"
"maybe_delete_out_dir" -> "rename"
"maybe_delete_out_dir" -> "unlink"
"maybe_delete_out_dir" -> "time"
"maybe_delete_out_dir" -> "snprintf"
"maybe_delete_out_dir" -> "flock"
"maybe_delete_out_dir" -> "fclose"
"maybe_delete_out_dir" -> "fscanf"
"maybe_delete_out_dir" -> "__errno_location"
"maybe_delete_out_dir" -> "strerror"
"maybe_delete_out_dir" -> "localtime"
"maybe_delete_out_dir" -> "DFL_ck_free"
"maybe_delete_out_dir" -> "exit"
"maybe_delete_out_dir" -> "delete_files"
"maybe_delete_out_dir" -> "printf"
"maybe_delete_out_dir" -> "fflush"
"maybe_delete_out_dir" -> "fopen"
"maybe_delete_out_dir" -> "open"
"maybe_delete_out_dir" -> "DFL_ck_alloc"
"maybe_delete_out_dir" -> "rmdir"
"handle_stop_sig" -> "kill"
"mark_as_variable" -> "snprintf"
"mark_as_variable" -> "__errno_location"
"mark_as_variable" -> "strerror"
"mark_as_variable" -> "symlink"
"mark_as_variable" -> "DFL_ck_free"
"mark_as_variable" -> "exit"
"mark_as_variable" -> "printf"
"mark_as_variable" -> "close"
"mark_as_variable" -> "strrchr"
"mark_as_variable" -> "fflush"
"mark_as_variable" -> "open"
"mark_as_variable" -> "DFL_ck_alloc"
"delete_files" -> "strncmp"
"delete_files" -> "snprintf"
"delete_files" -> "__errno_location"
"delete_files" -> "strerror"
"delete_files" -> "readdir"
"delete_files" -> "closedir"
"delete_files" -> "opendir"
"delete_files" -> "DFL_ck_free"
"delete_files" -> "exit"
"delete_files" -> "rmdir"
"delete_files" -> "printf"
"delete_files" -> "fflush"
"delete_files" -> "unlink"
"delete_files" -> "strlen"
"delete_files" -> "DFL_ck_alloc"
}
I wouldn't mind spending a lot of RAM / time calculating this result, I'd just like it to look nice.
Without knowing anything about the desired structure or layout of this graph, it's difficult to know what you would consider to be nice output. But with large graph with few ranks like this one, using the graph attribute rankdir=LR can improve the output significantly. You could also try experimenting with (cluster) subgraphs and the splines attribute.
Related
Order of nodes in graphviz
I am working on this graph: digraph "example.gsn.yaml" { ## Elements "G1" [shape="box", label=<<B>G1</B><BR align="left"/>Goal 1>]; "G2" [shape="box", label=<<B>G2</B><BR align="left"/>Goal 2>]; "G4" [shape="box", label=<<B>G4</B><BR align="left"/>Goal 4>]; "G3" [shape="box", label=<<B>G3</B><BR align="left"/>Goal 3>]; "C1" [shape="box", style="rounded", label=<<B>C1</B><BR align="left"/>Context 1>]; "S1" [shape="parallelogram", label=<<B>S1</B><BR align="left"/>Strategy 1>]; "A1" [shape="oval", label=<<B>A1</B><BR align="left"/>Argument 1>]; "A1":e -> "A1":e [headlabel=< <B>A</B> >, labeldistance=2.5, penwidth=0, arrowhead=none]; "Sn1" [shape="circle", label=<<B>Sn1</B><BR align="left"/>Solution 1>]; "Sn2" [shape="circle", label=<<B>Sn2</B><BR align="left"/>Solution 2>]; "J1" [shape="oval", label=<<B>J1</B><BR align="left"/>Justification 1>]; "J1":e -> "J1":e [headlabel=< <B>J</B> >, labeldistance=2.5, penwidth=0, arrowhead=none]; ## Relations "G1" -> "G2"; "G1" -> "G3"; "G1" -> "G4"; "G2" -> "S1"; "G4" -> "Sn2"; "G3" -> "Sn1"; "G3" -> "C1" [arrowhead=empty]; {rank = same; "G3"; "C1"; } "S1" -> "Sn1"; "S1" -> "J1" [arrowhead=empty]; "S1" -> "A1" [arrowhead=empty]; {rank = same; "A1"; "S1"; } {rank = same; "S1"; "J1"; } } I would like to minimize crossings. My preferred layout would be that A1, S1 and J1 are on the same rank in a left-to-right order. However I cannot find out how to do this. Can anybody help, please? Thanks in advance.
Many "obvious" solutions did not work (ordering, weight, invisible edges). Putting A1, J1, and S1 inside a cluster does the trick: digraph "example.gsn.yaml" { ## Elements "G1" [shape="box", label=<<B>G1</B><BR align="left"/>Goal 1>]; "G2" [shape="box", label=<<B>G2</B><BR align="left"/>Goal 2>]; "G4" [shape="box", label=<<B>G4</B><BR align="left"/>Goal 4>]; "G3" [shape="box", label=<<B>G3</B><BR align="left"/>Goal 3>]; "C1" [shape="box", style="rounded", label=<<B>C1</B><BR align="left"/>Context 1>]; "S1" [shape="parallelogram", label=<<B>S1</B><BR align="left"/>Strategy 1>]; "A1" [shape="oval", label=<<B>A1</B><BR align="left"/>Argument 1>]; "A1":e -> "A1":e [headlabel=< <B>A</B> >, labeldistance=2.5, penwidth=0, arrowhead=none]; "Sn1" [shape="circle", label=<<B>Sn1</B><BR align="left"/>Solution 1>]; "Sn2" [shape="circle", label=<<B>Sn2</B><BR align="left"/>Solution 2>]; "J1" [shape="oval", label=<<B>J1</B><BR align="left"/>Justification 1>]; "J1":e -> "J1":e [headlabel=< <B>J</B> >, labeldistance=2.5, penwidth=0, arrowhead=none]; subgraph clusterg1{ graph[peripheries=0] // no box around the cluster {rank = same; node[group=g1] edge[style=invis] "A1" -> "S1" -> "J1"} } ## Relations "G1" -> "G2"; "G1" -> "G3"; "G1" -> "G4"; "G2" -> "S1"; "G4" -> "Sn2"; "G3" -> "Sn1"; "G3" -> "C1" [arrowhead=empty]; {rank = same; "G3"; "C1"; } "S1" -> "Sn1"; "S1" -> "J1" [arrowhead=empty]; "A1" -> "S1" [arrowhead=empty dir=back]; } p.s. is the A1->S1 arrow correct?
How do I get individual clusters on 3 different levels?
Here's the dot file I have: digraph G { rankdir=TB; ranksep=1; subgraph cluster_application { subgraph cluster_module_core { init_a -> service_a; init_a -> service_b; init_a -> service_c; init_a -> service_d; } subgraph cluster_module_a { init_d -> service_c_1; init_d -> service_d_1; } subgraph cluster_module_b { init_b -> service_a_1; init_b -> service_b_1; } subgraph cluster_module_db { init_c -> db_service; db_service -> db; } } main -> init_a; main -> init_b; main -> init_c; main -> init_d; service_a -> service_a_1; service_b -> service_b_1; service_c -> service_c_1; service_d -> service_d_1; service_a_1 -> db_service; service_b_1 -> db_service; service_c_1 -> db_service; service_d_1 -> db_service; } How do I get a visual that would look like this: Main | | +------------+ | core | +------------+ / / \ \ / / \ \ +-----------+ +-----------+ | Module A | | Module B | +-----------+ +-----------+ \ \ / / \ \ / / +-------------+ | Module DB | +-------------+ So we can clearly see that ModuleA and ModuleB act as middlewares? I tried groupping them in clusters but I still get cluster to overlap on vertical axis instead of them being clearly on different levels. I don't mind if lines cross boxes as it's not possible otherwise.
Once you are not satisfied with how graphviz positioned nodes on the graph, you are entering an ugly world of invisible edges, constraint-falses and weights. I've added constraint=false attribute for your main -> init_c edge and added few invisible edges (I temporary marked them red for clarity). If you want to further adjust the position of nodes and clusters, you can play with weight attribute of different edges. digraph G { rankdir=TB; ranksep=1; subgraph cluster_application { subgraph cluster_module_core { init_a -> service_a; init_a -> service_b; init_a -> service_c; init_a -> service_d; } subgraph cluster_module_a { init_d -> service_c_1; init_d -> service_d_1; } subgraph cluster_module_b { init_b -> service_a_1; init_b -> service_b_1; } subgraph cluster_module_db { init_c -> db_service; db_service -> db; } } main -> init_a; main -> init_b; main -> init_c [constraint=false] main -> init_d; service_a -> service_a_1; service_b -> service_b_1; service_c -> service_c_1; service_d -> service_d_1; service_a_1 -> db_service; service_b_1 -> db_service; service_c_1 -> db_service; service_d_1 -> db_service; service_d -> init_d [color="red"] #[style=invis] service_d -> init_b [color="red"] #[style=invis] service_d_1 -> init_c [color="red"] #[style=invis] service_b_1 -> init_c [color="red"] #[style=invis] } Result:
Graphviz hierarchically display tree of subgraphs
I have a program that autogenerates a graphviz .dot file, the contents of which are a tree of subgraphs. When I observe how this file is rendered, the tree seems to be 'flattened', poorly displaying the hierarchical structure. I would like to know how to correct this issue, and properly render this tree. In the past, I have used the fdp layout program to render this graph. It was able to show the hierarchical structure of the tree, but the contents of the nodes, which are subgraphs, were not very readable. I want to use the dot layout program since it does a better job layering the nodes of the subgraphs, and is said to be ideal for hierarchical graphs. Please see the image below for a sample graph that shows the poorly rendered tree. The code that generates this figure is: digraph G { compound=true subgraph cluster_649 {label = EPISODE649count3 subgraph cluster_STATE650 { label = State_1; EPISODE649HEIGHT643[label="HEIGHT",shape=oval,color=blue]; EPISODE649LEN642[label="LEN",shape=oval,color=blue]; EPISODE649Y641[label="Y",shape=oval,color=blue]; EPISODE649X640[label="X",shape=oval,color=blue]; EPISODE649BLOCK639[label="BLOCK",shape=oval,color=blue]; EPISODE649HEIGHT638[label="HEIGHT",shape=oval,color=blue]; EPISODE649LEN637[label="LEN",shape=oval,color=blue]; EPISODE649Y636[label="Y",shape=oval,color=blue]; EPISODE649X635[label="X",shape=oval,color=blue]; EPISODE649TRIANGLE634[label="TRIANGLE",shape=oval,color=blue]; EPISODE649ON645[label="ON",shape=oval,color=blue]; EPISODE649ON555[label="ON",shape=oval,color=blue]; EPISODE649CLEAR647[label="CLEAR",shape=oval,color=blue]; EPISODE649CLEAR557[label="CLEAR",shape=oval,color=blue]; EPISODE649CLEAR558[label="CLEAR",shape=oval,color=blue]; EPISODE649ON556[label="ON",shape=oval,color=blue]; EPISODE649BLOCK545[label="BLOCK",shape=oval,color=blue]; EPISODE649X546[label="X",shape=oval,color=blue]; EPISODE649Y547[label="Y",shape=oval,color=blue]; EPISODE649LEN548[label="LEN",shape=oval,color=blue]; EPISODE649HEIGHT549[label="HEIGHT",shape=oval,color=blue]; EPISODE649BLOCK550[label="BLOCK",shape=oval,color=blue]; EPISODE649X551[label="X",shape=oval,color=blue]; EPISODE649Y552[label="Y",shape=oval,color=blue]; EPISODE649LEN553[label="LEN",shape=oval,color=blue]; EPISODE649HEIGHT554[label="HEIGHT",shape=oval,color=blue]; EPISODE649BLOCK639 -> EPISODE649HEIGHT643; EPISODE649BLOCK639 -> EPISODE649LEN642; EPISODE649BLOCK639 -> EPISODE649Y641; EPISODE649BLOCK639 -> EPISODE649X640; EPISODE649TRIANGLE634 -> EPISODE649HEIGHT638; EPISODE649TRIANGLE634 -> EPISODE649LEN637; EPISODE649TRIANGLE634 -> EPISODE649Y636; EPISODE649TRIANGLE634 -> EPISODE649X635; EPISODE649ON645 -> EPISODE649HEIGHT643; EPISODE649ON645 -> EPISODE649LEN642; EPISODE649ON645 -> EPISODE649Y641; EPISODE649ON645 -> EPISODE649X640; EPISODE649ON645 -> EPISODE649BLOCK639; EPISODE649ON645 -> EPISODE649HEIGHT638; EPISODE649ON645 -> EPISODE649LEN637; EPISODE649ON645 -> EPISODE649Y636; EPISODE649ON645 -> EPISODE649X635; EPISODE649ON645 -> EPISODE649TRIANGLE634; EPISODE649ON555 -> EPISODE649ON645; EPISODE649ON555 -> EPISODE649ON556; EPISODE649CLEAR647 -> EPISODE649HEIGHT638; EPISODE649CLEAR647 -> EPISODE649LEN637; EPISODE649CLEAR647 -> EPISODE649Y636; EPISODE649CLEAR647 -> EPISODE649X635; EPISODE649CLEAR647 -> EPISODE649TRIANGLE634; EPISODE649CLEAR557 -> EPISODE649CLEAR647; EPISODE649CLEAR557 -> EPISODE649CLEAR558; EPISODE649CLEAR558 -> EPISODE649BLOCK545; EPISODE649CLEAR558 -> EPISODE649X546; EPISODE649CLEAR558 -> EPISODE649Y547; EPISODE649CLEAR558 -> EPISODE649LEN548; EPISODE649CLEAR558 -> EPISODE649HEIGHT549; EPISODE649ON556 -> EPISODE649BLOCK545; EPISODE649ON556 -> EPISODE649X546; EPISODE649ON556 -> EPISODE649Y547; EPISODE649ON556 -> EPISODE649LEN548; EPISODE649ON556 -> EPISODE649HEIGHT549; EPISODE649ON556 -> EPISODE649BLOCK550; EPISODE649ON556 -> EPISODE649X551; EPISODE649ON556 -> EPISODE649Y552; EPISODE649ON556 -> EPISODE649LEN553; EPISODE649ON556 -> EPISODE649HEIGHT554; EPISODE649BLOCK545 -> EPISODE649X546; EPISODE649BLOCK545 -> EPISODE649Y547; EPISODE649BLOCK545 -> EPISODE649LEN548; EPISODE649BLOCK545 -> EPISODE649HEIGHT549; EPISODE649BLOCK550 -> EPISODE649X551; EPISODE649BLOCK550 -> EPISODE649Y552; EPISODE649BLOCK550 -> EPISODE649LEN553; EPISODE649BLOCK550 -> EPISODE649HEIGHT554; } 649[shape=point style=invis] } subgraph cluster_559 {label = EPISODE559count1 subgraph cluster_STATE651 { label = State_1; EPISODE559CLEAR557[label="CLEAR",shape=oval,color=blue]; EPISODE559CLEAR558[label="CLEAR",shape=oval,color=blue]; EPISODE559ON555[label="ON",shape=oval,color=blue]; EPISODE559ON556[label="ON",shape=oval,color=blue]; EPISODE559BLOCK545[label="BLOCK",shape=oval,color=blue]; EPISODE559X546[label="X",shape=oval,color=blue]; EPISODE559Y547[label="Y",shape=oval,color=blue]; EPISODE559LEN548[label="LEN",shape=oval,color=blue]; EPISODE559HEIGHT549[label="HEIGHT",shape=oval,color=blue]; EPISODE559BLOCK550[label="BLOCK",shape=oval,color=blue]; EPISODE559X551[label="X",shape=oval,color=blue]; EPISODE559Y552[label="Y",shape=oval,color=blue]; EPISODE559LEN553[label="LEN",shape=oval,color=blue]; EPISODE559HEIGHT554[label="HEIGHT",shape=oval,color=blue]; EPISODE559CLEAR557 -> EPISODE559CLEAR558; EPISODE559CLEAR558 -> EPISODE559BLOCK545; EPISODE559CLEAR558 -> EPISODE559X546; EPISODE559CLEAR558 -> EPISODE559Y547; EPISODE559CLEAR558 -> EPISODE559LEN548; EPISODE559CLEAR558 -> EPISODE559HEIGHT549; EPISODE559ON555 -> EPISODE559ON556; EPISODE559ON556 -> EPISODE559BLOCK545; EPISODE559ON556 -> EPISODE559X546; EPISODE559ON556 -> EPISODE559Y547; EPISODE559ON556 -> EPISODE559LEN548; EPISODE559ON556 -> EPISODE559HEIGHT549; EPISODE559ON556 -> EPISODE559BLOCK550; EPISODE559ON556 -> EPISODE559X551; EPISODE559ON556 -> EPISODE559Y552; EPISODE559ON556 -> EPISODE559LEN553; EPISODE559ON556 -> EPISODE559HEIGHT554; EPISODE559BLOCK545 -> EPISODE559X546; EPISODE559BLOCK545 -> EPISODE559Y547; EPISODE559BLOCK545 -> EPISODE559LEN548; EPISODE559BLOCK545 -> EPISODE559HEIGHT549; EPISODE559BLOCK550 -> EPISODE559X551; EPISODE559BLOCK550 -> EPISODE559Y552; EPISODE559BLOCK550 -> EPISODE559LEN553; EPISODE559BLOCK550 -> EPISODE559HEIGHT554; } 559[shape=point style=invis] } 649 -> 559 [ltail=cluster_649 lhead=cluster_559]; subgraph cluster_625 {label = EPISODE625count1 subgraph cluster_STATE652 { label = State_1; EPISODE625CLEAR623[label="CLEAR",shape=oval,color=blue]; EPISODE625CLEAR624[label="CLEAR",shape=oval,color=blue]; EPISODE625ON621[label="ON",shape=oval,color=blue]; EPISODE625ON622[label="ON",shape=oval,color=blue]; EPISODE625BLOCK611[label="BLOCK",shape=oval,color=blue]; EPISODE625X612[label="X",shape=oval,color=blue]; EPISODE625Y613[label="Y",shape=oval,color=blue]; EPISODE625LEN614[label="LEN",shape=oval,color=blue]; EPISODE625HEIGHT615[label="HEIGHT",shape=oval,color=blue]; EPISODE625BLOCK616[label="BLOCK",shape=oval,color=blue]; EPISODE625X617[label="X",shape=oval,color=blue]; EPISODE625Y618[label="Y",shape=oval,color=blue]; EPISODE625LEN619[label="LEN",shape=oval,color=blue]; EPISODE625HEIGHT620[label="HEIGHT",shape=oval,color=blue]; EPISODE625CLEAR623 -> EPISODE625CLEAR624; EPISODE625CLEAR624 -> EPISODE625BLOCK611; EPISODE625CLEAR624 -> EPISODE625X612; EPISODE625CLEAR624 -> EPISODE625Y613; EPISODE625CLEAR624 -> EPISODE625LEN614; EPISODE625CLEAR624 -> EPISODE625HEIGHT615; EPISODE625ON621 -> EPISODE625ON622; EPISODE625ON622 -> EPISODE625BLOCK611; EPISODE625ON622 -> EPISODE625X612; EPISODE625ON622 -> EPISODE625Y613; EPISODE625ON622 -> EPISODE625LEN614; EPISODE625ON622 -> EPISODE625HEIGHT615; EPISODE625ON622 -> EPISODE625BLOCK616; EPISODE625ON622 -> EPISODE625X617; EPISODE625ON622 -> EPISODE625Y618; EPISODE625ON622 -> EPISODE625LEN619; EPISODE625ON622 -> EPISODE625HEIGHT620; EPISODE625BLOCK611 -> EPISODE625X612; EPISODE625BLOCK611 -> EPISODE625Y613; EPISODE625BLOCK611 -> EPISODE625LEN614; EPISODE625BLOCK611 -> EPISODE625HEIGHT615; EPISODE625BLOCK616 -> EPISODE625X617; EPISODE625BLOCK616 -> EPISODE625Y618; EPISODE625BLOCK616 -> EPISODE625LEN619; EPISODE625BLOCK616 -> EPISODE625HEIGHT620; } 625[shape=point style=invis] } 649 -> 625 [ltail=cluster_649 lhead=cluster_625]; subgraph cluster_648 {label = EPISODE648count1 subgraph cluster_STATE653 { label = State_1; EPISODE648CLEAR646[label="CLEAR",shape=oval,color=blue]; EPISODE648CLEAR647[label="CLEAR",shape=oval,color=blue]; EPISODE648ON644[label="ON",shape=oval,color=blue]; EPISODE648ON645[label="ON",shape=oval,color=blue]; EPISODE648TRIANGLE634[label="TRIANGLE",shape=oval,color=blue]; EPISODE648X635[label="X",shape=oval,color=blue]; EPISODE648Y636[label="Y",shape=oval,color=blue]; EPISODE648LEN637[label="LEN",shape=oval,color=blue]; EPISODE648HEIGHT638[label="HEIGHT",shape=oval,color=blue]; EPISODE648BLOCK639[label="BLOCK",shape=oval,color=blue]; EPISODE648X640[label="X",shape=oval,color=blue]; EPISODE648Y641[label="Y",shape=oval,color=blue]; EPISODE648LEN642[label="LEN",shape=oval,color=blue]; EPISODE648HEIGHT643[label="HEIGHT",shape=oval,color=blue]; EPISODE648CLEAR646 -> EPISODE648CLEAR647; EPISODE648CLEAR647 -> EPISODE648TRIANGLE634; EPISODE648CLEAR647 -> EPISODE648X635; EPISODE648CLEAR647 -> EPISODE648Y636; EPISODE648CLEAR647 -> EPISODE648LEN637; EPISODE648CLEAR647 -> EPISODE648HEIGHT638; EPISODE648ON644 -> EPISODE648ON645; EPISODE648ON645 -> EPISODE648TRIANGLE634; EPISODE648ON645 -> EPISODE648X635; EPISODE648ON645 -> EPISODE648Y636; EPISODE648ON645 -> EPISODE648LEN637; EPISODE648ON645 -> EPISODE648HEIGHT638; EPISODE648ON645 -> EPISODE648BLOCK639; EPISODE648ON645 -> EPISODE648X640; EPISODE648ON645 -> EPISODE648Y641; EPISODE648ON645 -> EPISODE648LEN642; EPISODE648ON645 -> EPISODE648HEIGHT643; EPISODE648TRIANGLE634 -> EPISODE648X635; EPISODE648TRIANGLE634 -> EPISODE648Y636; EPISODE648TRIANGLE634 -> EPISODE648LEN637; EPISODE648TRIANGLE634 -> EPISODE648HEIGHT638; EPISODE648BLOCK639 -> EPISODE648X640; EPISODE648BLOCK639 -> EPISODE648Y641; EPISODE648BLOCK639 -> EPISODE648LEN642; EPISODE648BLOCK639 -> EPISODE648HEIGHT643; } 648[shape=point style=invis] } 649 -> 648 [ltail=cluster_649 lhead=cluster_648]; } Observe that in order to draw edges from cluster to cluster, I've created invisible nodes in each cluster, like: 625[shape=point style=invis] and connected two invisible nodes if there is a parent/child relation between the two clusters, like: 649 -> 625 [ltail=cluster_649 lhead=cluster_625]; Thanks for your help!
Your invisible nodes were a good idea, but you got caught by dot's (unwritten?) rule that all nodes should be on the same rank unless there is a command the "forces" a new rank. If you change "invisible" to "dotted", you will see what happened to those nodes. I tried to find a straightforward way to get dot to play nicely with those extra nodes, but failed. Eventually I used some of the existing nodes to accomplish what I assume is the goal. I left these edges visible so you could easily see what I changed. I also added peripheries and margin attributes. A cut-down version of the program: digraph G { compound = true // a dummy cluster to pad the left side subgraph cluster_dummy{ label="" ; peripheries=0;node[style=invis]; {rank=same _a; _b } } peripheries=0 subgraph cluster_649 { label = EPISODE649count3 subgraph cluster_STATE650 { graph [ margin=30] label = State_1; // unmodified nodes go here } } subgraph cluster_559 { label = EPISODE559count1 subgraph cluster_STATE651 { graph [ margin=30] label = State_1; // unmodified nodes go here } } subgraph cluster_625 { label = EPISODE625count1 subgraph cluster_STATE652 { graph [ margin=30] label = State_1; // unmodified nodes go here } } subgraph cluster_648 { label = EPISODE648count1 subgraph cluster_STATE653 { graph [ margin=30] label = State_1; // unmodified nodes go here } } EPISODE649HEIGHT638 -> EPISODE559ON555 [weight=0 style=dotted] EPISODE649HEIGHT638 -> EPISODE625ON621 [weight=0 style=dotted] EPISODE649HEIGHT638 -> EPISODE648ON644 [weight=0 style=dotted] }
Edge crossing each other
is there a way to eliminate the crossing of edges? I tried many ways, nothing helped. digraph graphname { graph [splines=ortho,]; node [shape=box,]; l; l_a [shape=diamond,label="",height=0.20,width=0.20]; l_a_s [shape=point]; l_a_i [shape=point]; l_a_ii [shape=point]; l_a -> l_a_s; {rank=same; a -> l_a -> l} {rank=same; l_a_i -> l_a_s -> l_a_ii} l_a_i -> i; l_a_ii -> ii; l_c [shape=diamond,label="",height=0.20,width=0.20]; l_c_s [shape=point]; l_c_t [shape=point]; l_c_n [shape=point]; l_c -> l_c_s; {rank=same; l -> l_c -> c} {rank=same; l_c_t -> l_c_s -> l_c_n} l_c_t -> t; l_c_n -> n; } some more details:
All you need to do is to reorganize your node/edge definitions a little bit, no additional edges needed. for example: digraph graphname { graph [splines=ortho,]; node [shape=box,]; a; l_a [shape=diamond,label="",height=0.20,width=0.20]; l; l_c [shape=diamond,label="",height=0.20,width=0.20]; c; {rank=same; a -> l_a -> l -> l_c -> c} l_a_s [shape=point]; l_c_s [shape=point]; l_a -> l_a_s; l_c -> l_c_s; l_a_i [shape=point]; l_a_ii [shape=point]; l_c_t [shape=point]; l_c_n [shape=point]; {rank=same; l_a_i -> l_a_s -> l_a_ii; l_c_t -> l_c_s -> l_c_n;} l_a_i -> i; l_a_ii -> ii; l_c_t -> t; l_c_n -> n; }
Adding this invisible edge solves your problem: l_a_i -> l_c_s [constraint=false style=invis] I can't prove mathematically why this works, but in such situation it usually helps to play with order in which nodes/edges are defined, or try adding invisible adges to nudge the layout in the direction you need
DOT: How to place clusters at same level without ranking equally the nodes in those clusters?
I have below DOT code. It describes five subgraphs (clusters). I am happy with the rendering of the two lower clusters and also with the relative position of the clusters (three on the top at the same level and two at the bottom in the respective order). However, I would like that the nods in the upper three clusters are ranked according to the internal relationships of their nodes, similar to what I get in the lower two clusters (As you see, the nodes in the upper clusters are rendered all in a single row despite the presence of hidden edges between some of them). I understand that the reason they are not is the rank=same; command. However, if I remove that I am losing the positioning of the clusters. I tried a couple of variants using different combinations of the commands and additional ones such as clusterrank=local;, but the result below stayed the closest. Also subgraph cluster ranking in dot did not get me what I want. digraph G { splines=line; size=1; ranksep=2; newrank=true; rankdir=BT subgraph cluster_z { label="Z"; rank=same; "ZSF" [fillcolor = red]; "ZTA" [fillcolor = red]; "ZSS" [fillcolor = red]; "ZIN" [fillcolor = red]; "ZOW" [fillcolor = red]; "ZNT" [fillcolor = red]; "ZSS" [fillcolor = red]; "ZCE" [fillcolor = red]; "ZAY" [fillcolor = red]; "ZNT" [fillcolor = red]; "ZTA" [fillcolor = red]; "ZTA" [fillcolor = red]; "ZST" [fillcolor = red]; "ZTO" [fillcolor = red]; "ZON" [fillcolor = red]; "ZPP" [fillcolor = red]; "TNT" [fillcolor = red]; "TCE" [fillcolor = red]; "TNT" [fillcolor = red]; "ZNT" -> "ZTA" [style=invis] "ZTA" -> "ZSF" [style=invis] "ZSF" -> "ZNT" [style=invis] "ZIN" -> "ZTA" [style=invis] "ZTA" -> "ZON" [style=invis] "ZON" -> "ZTA" [style=invis] "ZTA" -> "ZSF" [style=invis] "ZSF" -> "ZNT" [style=invis] "ZNT" -> "ZIN" [style=invis] "ZPP" -> "ZTA" [style=invis] "ZTA" -> "ZSF" [style=invis] "ZSF" -> "ZPP" [style=invis] "ZSF" -> "ZTA" [style=invis] "ZTA" -> "ZSF" [style=invis] "ZOW" -> "ZSS" [style=invis] "ZSS" -> "ZOW" [style=invis] "ZAY" -> "ZCE" [style=invis] "ZCE" -> "ZTA" [style=invis] "ZTA" -> "ZAY" [style=invis] "ZSF" -> "ZTA" [style=invis] "ZAY" -> "ZTA" [style=invis] "ZTA" -> "ZSF" [style=invis] "ZAY" -> "ZTA" [style=invis] "ZTA" -> "ZTA" [style=invis] "ZTA" -> "ZCE" [style=invis] "ZCE" -> "ZTA" [style=invis] "ZTA" -> "ZSF" [style=invis] "ZSF" -> "ZAY" [style=invis] } subgraph cluster_y { rank=same; label="Y"; "YCY" [fillcolor = blue]; "YES" [fillcolor = blue]; } subgraph cluster_w { rank=same; label="W"; "WER" [fillcolor = green]; "WRT" [fillcolor = green]; } subgraph cluster_o { label="O"; "OOL" [fillcolor = white]; "OOL" [fillcolor = white]; "OIT" [fillcolor = white]; "ONT" [fillcolor = white]; "OGE" [fillcolor = white]; "OTA" [fillcolor = white]; "OTA" [fillcolor = white]; "OTS" [fillcolor = white]; "OTS" [fillcolor = white]; "OCE" [fillcolor = white]; "ORT" [fillcolor = white]; "ORT" [fillcolor = white]; "OON" [fillcolor = white]; "OCT" [fillcolor = white]; "OOL" [fillcolor = white]; "OTO" [fillcolor = white]; "OPE" [fillcolor = white]; "OPY" [fillcolor = white]; "OIT" -> "ORT" [style=invis] "ORT" -> "OON" [style=invis] "OON" -> "OPE" [style=invis] "OPE" -> "OIT" [style=invis] "OON" -> "OPE" [style=invis] "OPE" -> "OON" [style=invis] "OOL" -> "OOL" [style=invis] "OOL" -> "OOL" [style=invis] "OCE" -> "OON" [style=invis] "OON" -> "OCE" [style=invis] "OTA" -> "OON" [style=invis] "OON" -> "OTA" [style=invis] "OIT" -> "OTA" [style=invis] "OTA" -> "ORT" [style=invis] "ORT" -> "ORT" [style=invis] "ORT" -> "OON" [style=invis] "OON" -> "OIT" [style=invis] "OIT" -> "OTA" [style=invis] "OTA" -> "OTS" [style=invis] "OTS" -> "OON" [style=invis] "OON" -> "OPE" [style=invis] "OPE" -> "OIT" [style=invis] } subgraph cluster_e { label="E"; "EUT" [fillcolor = grey]; "EON" [fillcolor = grey]; "ERT" [fillcolor = grey]; "ERT" [fillcolor = grey]; "EST" [fillcolor = grey]; "EON" [fillcolor = grey]; "EER" [fillcolor = grey]; "ERE" [fillcolor = grey]; "ETO" [fillcolor = grey]; } "OIT" -> "ZSF"; "ORT" -> "ZSF"; "OON" -> "ZSF"; "OPE" -> "ZSF"; "EON" -> "ZSF"; "EER" -> "ZSF"; "OON" -> "ZTA"; "OPE" -> "ZTA"; "EER" -> "ZTA"; "OOL" -> "ZSS"; "OOL" -> "ZSS"; "OGE" -> "ZIN"; "EON" -> "ZIN"; "OOL" -> "ZOW"; "OTS" -> "ZNT"; "OCT" -> "ZSS"; "OCE" -> "ZCE"; "OON" -> "ZCE"; "OTA" -> "ZAY"; "OON" -> "ZAY"; "OIT" -> "ZNT"; "EON" -> "ZNT"; "OIT" -> "ZTA"; "OTA" -> "ZTA"; "ORT" -> "ZTA"; "ORT" -> "ZTA"; "OON" -> "ZTA"; "EON" -> "ZTA"; "ERE" -> "ZTA"; "OIT" -> "ZTA"; "OTA" -> "ZTA"; "OTS" -> "ZTA"; "OON" -> "ZTA"; "OPE" -> "ZTA"; "EON" -> "ZTA"; "ERE" -> "ZTA"; "OOL" -> "ZST"; "OTO" -> "ZTO"; "ONT" -> "ZON"; "EON" -> "ZON"; "OPY" -> "ZPP"; "EER" -> "ZPP"; { rank=same; "ZSF"; "YCY"; "WER" } { rank=same; "OPY"; "EER"} }
I removed all the duplicate lines, invisible edges, and colour attributes. I then gave it some colour to make visible what I've done. The source is like this: digraph G { newrank=true; rankdir=BT; ranksep=2; splines=line; subgraph cluster_z { label="Z"; "TCE"; "TNT"; "ZAY"; "ZCE"; "ZIN"; "ZNT"; "ZON"; "ZOW"; "ZPP"; "ZSF"; "ZSS"; "ZST"; "ZTA"; "ZTO"; } subgraph cluster_y { rank=same; label="Y"; "YCY"; "YES"; } subgraph cluster_w { rank=same; label="W"; "WER"; "WRT"; } subgraph cluster_o { label="O"; "OCE"; "OCT"; "OGE"; "OIT"; "ONT"; "OOL"; "OON"; "OPE"; "OPY"; "ORT"; "OTA"; "OTO"; "OTS"; } subgraph cluster_e { label="E"; "EUT"; "ERT"; "EST"; "EON"; "EER"; "ERE"; "ETO"; } /* All the edges */ "EER" -> "ZPP"; "EER" -> "ZSF"; "EER" -> "ZTA"; "EON" -> "ZIN"; "EON" -> "ZNT"; "EON" -> "ZON"; "EON" -> "ZSF"; "EON" -> "ZTA"; "ERE" -> "ZTA"; "OCE" -> "ZCE"; "OCT" -> "ZSS"; "OGE" -> "ZIN"; "OIT" -> "ZNT"; "OIT" -> "ZSF"; "OIT" -> "ZTA"; "ONT" -> "ZON"; "OOL" -> "ZOW"; "OOL" -> "ZSS"; "OOL" -> "ZST"; "OON" -> "ZAY"; "OON" -> "ZCE"; "OON" -> "ZSF"; "OON" -> "ZTA"; "OPE" -> "ZSF"; "OPE" -> "ZTA"; "OPY" -> "ZPP"; "ORT" -> "ZSF"; "ORT" -> "ZTA"; "OTA" -> "ZAY"; "OTA" -> "ZTA"; "OTO" -> "ZTO"; "OTS" -> "ZNT"; "OTS" -> "ZTA"; /* 15 nodes from cluster_z and cluster_y and cluster_w */ { rank = same; "TCE" [style = filled; color = green;] "TNT" [style = filled; color = green;] "WER" [style = filled; color = green;] "WRT" [style = filled; color = green;] "YCY" [style = filled; color = green;] "YES" [style = filled; color = green;] "ZAY" [style = filled; color = green;] "ZCE" [style = filled; color = green;] "ZIN" [style = filled; color = green;] "ZON" [style = filled; color = green;] "ZOW" [style = filled; color = green;] "ZPP" [style = filled; color = green;] "ZSS" [style = filled; color = green;] "ZST" [style = filled; color = green;] "ZTO" [style = filled; color = green;] } /* 10 nodes from cluster_o and cluster_e */ { rank = same; "OCE" [style = filled; color = yellow;] "OCT" [style = filled; color = yellow;] "OGE" [style = filled; color = yellow;] "ONT" [style = filled; color = yellow;] "OOL" [style = filled; color = yellow;] "OPY" [style = filled; color = yellow;] "OTO" [style = filled; color = yellow;] "EON" [style = filled; color = yellow;] "EER" [style = filled; color = yellow;] "ERE" [style = filled; color = yellow;] } /* Gives cluster_z 2 rows of nodes. */ "ZSF" -> "ZTO" [color = red]; /* Gives cluster_o 4 rows of nodes. */ "OIT" -> "OON" -> "OPE" -> "ORT" [color = red]; } while the image looks like this: so you can fiddle with it as you like.