Last active
April 27, 2019 01:33
-
-
Save maelvls/755c16db4681e3a671c1 to your computer and use it in GitHub Desktop.
DIMACS Maximum Flow to .dot (GraphViz) converts from DIMACS to DOT format. I did this script to visualize the results from the `pseudo-flow` algorithm written by Hochbaum (http://lpsolve.sourceforge.net/5.5/DIMACS_maxf.htm). Short link: https://git.io/vdhsz
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
# vim:fenc=utf-8 | |
# | |
# Copyright 2015 Mael Valais <mael.valais@univ-tlse3.fr> | |
# | |
# Distributed under terms of the MIT license. | |
# | |
__descr__ = """ | |
This script is a parser from DIMACS to DOT that helps you transform | |
the max-flow DIMACS files into DOT files. | |
""" | |
# | |
# Terminology: | |
# - 'problem.max' = the problem written in DIMACS | |
# - 'results' = the results of ./pseudo, the max-flow solver | |
# | |
# Example 1 (display problem.max only): | |
# | |
# ./dimacs_to_dot.py problem.max | dot -Tpng > problem.png | |
# | |
# Example 2 (using the result of ./pseudo): | |
# | |
# cat problem.max | pseudo \\ | |
# | ./dimacs_to_dot.py problem.max -rstdin | dot -Tpng > problem.png | |
# | |
# NOTES: | |
# 1) the `cn 2 NAME` are comment lines, but are used in this script to give | |
# names to the vertices. | |
# 2) `./pseudo` is the name of the max-flow solver. You can get the source | |
# and compile it here: http://lpsolve.sourceforge.net/5.5/DIMACS_maxf.htm | |
# | |
# Content of problem.max: | |
# | |
# p max 10 15 | |
# n 1 s | |
# n 10 t | |
# cn 2 A | |
# cn 3 B | |
# cn 4 C | |
# cn 5 D | |
# cn 6 E | |
# cn 7 Mac | |
# cn 8 Linux | |
# cn 9 Windows | |
# a 1 7 2 | |
# a 1 8 2 | |
# a 1 9 3 | |
# a 7 2 1 | |
# a 7 4 1 | |
# a 7 6 1 | |
# a 7 3 1 | |
# a 8 3 1 | |
# a 8 5 1 | |
# a 9 5 1 | |
# a 2 10 1 | |
# a 3 10 1 | |
# a 4 10 1 | |
# a 5 10 1 | |
# a 6 10 1 | |
import re | |
import sys | |
import argparse | |
# Parses the DIMACS file and produces `edges` and `vertices` | |
# In: the DIMACS input file already open | |
# Out: [vertices, edges] with | |
# edges = [[vertex1, vertex2],...] | |
# vertices = [vertex1, vertex2...] | |
def parse_dimacs(dimacs_file): | |
edges = [] | |
vertices = [] | |
for line in dimacs_file: | |
vals = [i for i in re.findall("(\d+|\w+)",line)] | |
if vals: | |
if vals[0] in ["cn","n"]: | |
vertices += [vals[1:]] | |
if vals[0] == "a": | |
edges += [vals[1:]] | |
return [vertices, edges] | |
# Parses a result file that has the form | |
# Form: `starting_char vertex1 vertex2 flow_value` | |
# Ex: `a 4 1 90` | |
# Call: parse_result("file.result",'a',[[1,2],...] | |
# In: starting_char is the character that introduces each | |
# new result line | |
# In/out: edges with the form [[1,2],...] | |
# In: result_file_name is the input file already open | |
# and add a column into edge | |
def parse_result(result_file, starting_char, edges): | |
for line in result_file: | |
vals = [i for i in re.findall("(\d+|\w+)",line)] | |
if vals and vals[0] == starting_char: | |
vals = vals[1:] | |
i = next(i for i in range(len(edges)) if \ | |
[vals[0],vals[1]]==[edges[i][0],edges[i][1]]) | |
edges[i] += [vals[2]] | |
return edges | |
# Creates the dot file and write it into the output_file | |
def print_dot_file(edges, vertices, output_file): | |
print('digraph a \n') | |
print('{ \n') | |
print('\tgraph [rankdir=LR];') | |
for v in vertices: | |
print('\t%s [label=%s];' % (v[0], v[1])) | |
for e in edges: | |
if len(e) == 3: | |
print('\t%s -> %s [label="(%s)" fontsize=11];' % (e[0], e[1], e[2])) | |
if len(e) == 4: | |
print('\t%s -> %s [label=<(%s)> fontsize=11 xlabel=<<font color=\'red\'>%s</font>>];' % (e[0],e[1],e[2],e[3])) | |
print('}') | |
parser = argparse.ArgumentParser(description=__descr__,add_help=True) | |
parser.add_argument("dimacs_file", nargs='?',\ | |
type=argparse.FileType('r'), default=sys.stdin,\ | |
help='the input DIMACS file') | |
parser.add_argument("-o", nargs='?',\ | |
type=argparse.FileType('w'), default=sys.stdout, \ | |
help='the output file in DOT format', metavar='output') | |
parser.add_argument("-r", required=False, nargs=1, \ | |
type=argparse.FileType('r'),\ | |
help='the optional result file given by your solver (see -c)',\ | |
metavar='result_file') | |
parser.add_argument("-rstdin", required=False, action="store_true", default=False,\ | |
help='use stdin as the input for the result file') | |
parser.add_argument("-c",nargs=1,\ | |
help='use a different starting char for the result file',\ | |
metavar='starting_char', default='a') | |
args = parser.parse_args() | |
# 0. Checking args | |
# Exclusive or: either stdin OR a file name | |
result_has_been_given = False | |
if (args.rstdin == True or args.r != None): | |
result_has_been_given = True | |
if not (args.rstdin == True) ^ (args.r != None): | |
print("error: can't have both -rstdin and -r",file=sys.stderr) | |
sys.exit(1) | |
elif args.rstdin == True: | |
# We must check that sys.stdin is not already used | |
# by dimacs_file | |
if args.dimacs_file != sys.stdin: | |
res_file = sys.stdin | |
else: | |
print("error: you must give an explicit dimacs file name \ | |
when using -rstdin",file=sys.stderr) | |
sys.exit(1) | |
else: | |
res_file = open(args.r,'r') | |
# 1. Parse the dimacs file | |
[vertices,edges] = parse_dimacs(args.dimacs_file) | |
# 2. Parse the result file if given | |
if result_has_been_given: | |
edges = parse_result(res_file, args.c, edges) | |
# 3. Produce the dot file | |
print_dot_file(edges,vertices,args.o) | |
With the file example.max
:
p max 10 15
n 1 s
n 10 t
cn 2 A
cn 3 B
cn 4 C
cn 5 D
cn 6 E
cn 7 Mac
cn 8 Linux
cn 9 Windows
a 1 7 2
a 1 8 2
a 1 9 3
a 7 2 1
a 7 4 1
a 7 6 1
a 7 3 1
a 8 3 1
a 8 5 1
a 9 5 1
a 2 10 1
a 3 10 1
a 4 10 1
a 5 10 1
a 6 10 1
and the command
python dimacs_to_dot.py example.max | dot -Tpng > example.png
you get:
With the file tondeuses-a-gazon.max
(this file represents the flow problem):
p max 12 15
n 1 s
n 12 t
cn 2 X
cn 3 Y
cn 4 Z
cn 5 A
cn 6 B
cn 7 C
cn 8 u
cn 9 v
cn 10 w
cn 11 r
a 1 2 500
a 1 3 500
a 1 4 900
a 2 10 300
a 3 10 200
a 3 9 600
a 4 8 700
a 8 11 500
a 8 3 300
a 10 5 600
a 9 6 500
a 9 7 200
a 11 7 200
a 5 12 700
a 6 12 600
a 7 12 600
File res
generated by pseudo tondeuses-a-gazon.max
:
c Highest label pseudoflow algorithm (Version 3.23)
c Using FIFO buckets
c Number of nodes : 12
c Number of arcs : 15
c Time to read : 0.000
c Time to initialize : 0.000
c Time to min cut : 0.000
c Time to max flow : 0.000
c Number of arc scans : 18
c Number of mergers : 11
c Number of pushes : 16
c Number of relabels : 19
c Number of gaps : 4
c
c Solution checks as feasible.
c
c Solution checks as optimal.
c
s Max Flow : 900
c
c Nodes in source set of min s-t cut:
n 1
n 2
n 3
n 4
n 6
n 8
n 9
n 11
c
c Flow values on each arc:
a 1 2 300
a 1 4 500
a 3 10 200
a 8 11 200
a 8 3 300
a 10 5 500
a 9 6 0
a 5 12 500
a 7 12 400
a 11 7 200
a 9 7 200
a 4 8 500
a 3 9 200
a 2 10 300
a 1 3 100
Using the following command, you will get the picture example.png
:
python3 dimacs_to_dot.py tondeuses-a-gazon.max res | dot -Tpng > example.png
For the problem of factories and warehouses, here is the file factories.max
:
c Usines U1 et U2
p max 7 11
n 6 s
n 7 t
cn 1 U1
cn 2 U2
cn 3 E1
cn 4 E2
cn 5 E3
a 6 1 100
a 6 2 150
a 3 7 50
a 4 7 70
a 5 7 80
a 1 3 1000
a 1 4 1000
a 1 5 1000
a 2 3 1000
a 2 4 1000
a 2 5 1000
And the command to launch:
cat factories.max | pseudo | python3 dimacs_to_dot.py -rstdin factories.max | dot -Tpng > factories.png
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The script will recognize special comments beginning with
cn
; the syntax isFor example, if I want the node 9 to be displayed using the label
G
, I will usecn 9 G
.Problem file
example.max
You can get the picture of the flow (
example.png
) using the command: