aboutsummaryrefslogtreecommitdiff
path: root/lib/knapsack_solver/graph_printer.rb
blob: 318e02ed1b6fe15336f6a5e4fc8fe302a00d4b04 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
require 'gnuplot'

module KnapsackSolver
  # This class provides support for making graphs from statistics of datasets
  # solving results. It uses Gnuplot and also generates a Gnuplot config file
  # for each generated graph.
  class GraphPrinter
    # Initializes printer for graph data (graphs, Gnuplot config files).
    #
    # @param dataset_filenames [Array<String>] dataset filenames
    # @param stats [Hash] statistics of results
    # @param out_dir [String] statistics of results to print
    def initialize(dataset_filenames, stats, out_dir)
      @dataset_basenames = file_basenames(dataset_filenames)
      @stats = stats
      @out_dir = out_dir
    end

    # Create graphs from statistics and Gnuplot configuration files.
    def print
      stats_to_datasets.each do |title, ds|
        ofn = File.join(@out_dir, title + '.png')
        plot(title, ds, ofn)
      end
    end

    protected

    # Create graph.
    #
    # @param title [String] title of the graph
    # @param data [Array<Gnuplot::DataSet>] Gnuplot datasets to plot
    # @param filename [String] path to the output image file
    def plot(title, data, filename)
      Gnuplot.open do |gp|
        Gnuplot::Plot.new(gp, &plot_config(title, 'dataset', 'y', data, filename))
      end
      File.open(File.join(File.dirname(filename), File.basename(filename, '.png') + '.gnuplot'), 'w') do |gp|
        Gnuplot::Plot.new(gp, &plot_config(title, 'dataset', 'y', data, filename))
      end
    end

    # Creates Gnuplot datasets from statistics.
    #
    # @return [Array<Gnuplot::DataSet>] Gnuplot datasets created from the statistics.
    def stats_to_datasets
      graphs = @stats.values.first.values.first.first.keys
      x_data = @stats.keys
      datasets(graphs, x_data)
    end

    # Creates Gnuplot datasets from statistics.
    #
    # @param graphs [Array] array of graph titles
    # @param x_data [Array] array of X axis values
    def datasets(graphs, x_data)
      graphs.each_with_object({}) do |g, gnuplot_datasets|
        @stats.each_value do |s|
          gnuplot_datasets[g.to_s] = s.each_key.with_object([]) do |method, o|
            o << plot_dataset(method.to_s, x_data, @stats.map { |_, v| v[method].first[g] })
          end
        end
      end
    end

    # Create dataset from provided title, X axis data and Y axis data.
    #
    # @param title [String] Gnuplot dataset title
    # @param x_data [Array] Array of X values
    # @param y_data [Array] Array of Y values
    # @return [Gnuplot::DataSet] Gnuplot dataset.
    def plot_dataset(title, x_data, y_data)
      Gnuplot::DataSet.new([x_data, y_data]) { |ds| ds.title = escape_gnuplot_special_chars(title) }
    end

    # Creates Gnuplot plot configuration (configuration text lines).
    #
    # @param title [String] graph title
    # @param xlabel [String] label of X axis
    # @param ylabel [String] label of Y axis
    # @param plot_datasets [Array<Gnuplot::DataSet>] Gnuplot datasets for plotting
    # @param out_file [String] output file
    # @return [lambda] Lambda for setting plot configuration.
    def plot_config(title, xlabel, ylabel, plot_datasets, out_file)
      lambda do |plot|
        plot.term('png')
        plot.output(out_file)
        plot.title("'#{escape_gnuplot_special_chars(title)}'")
        plot.ylabel("'#{escape_gnuplot_special_chars(ylabel)}'")
        plot.xlabel("'#{escape_gnuplot_special_chars(xlabel)}'")
        plot.key('outside')
        plot.data = plot_datasets
      end
    end

    # Escapes Gnuplot special characters.
    #
    # @param str [String] a string
    # @return [Strnig] the string with Gnuplot special chars escaped
    def escape_gnuplot_special_chars(str)
      # underscore means subscript in Gnuplot
      str.gsub('_', '\_')
    end

    # Gets basenames of supplied file paths.
    #
    # @param paths [Array<String>] path to files
    # @return [Array<String>] basenames of the paths
    def file_basenames(paths)
      paths.each_with_object([]) do |path, basenames|
        basenames << File.basename(path, File.extname(path))
      end
    end
  end
end