davenport 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e60864e6083af33645d1f935132f958e613db8acffdd2523c29fafde6eaa7673
4
+ data.tar.gz: 643c0618945c97d49340f1aaf711b72dbe31e674ae4752563568591cd18c54bd
5
+ SHA512:
6
+ metadata.gz: 50daf9a754d6c3f1b72297cd9a5688dfbc4c52c292dbe7d358715439c17ce63abdc7c3d0944acf54c2b17c8a9ac6b6d70bab543df54812c4288cc522b0f18cc1
7
+ data.tar.gz: 6e033d3ecdeacf7305759b1c5da0563d6fe4655147d7651d2693f05ed9292e3a9a914fec0fd32fb385ba71a0faedf22d37dc057070ff25a334248b05cc8cb852
data/.autotest ADDED
@@ -0,0 +1,25 @@
1
+ # -*- ruby -*-
2
+
3
+ require "autotest/restart"
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.testlib = "minitest/unit"
7
+ #
8
+ # at.extra_files << "../some/external/dependency.rb"
9
+ #
10
+ # at.libs << ":../some/external"
11
+ #
12
+ # at.add_exception "vendor"
13
+ #
14
+ # at.add_mapping(/dependency.rb/) do |f, _|
15
+ # at.files_matching(/test_.*rb$/)
16
+ # end
17
+ #
18
+ # %w(TestA TestB).each do |klass|
19
+ # at.extra_class_map[klass] = "test/test_misc.rb"
20
+ # end
21
+ # end
22
+
23
+ # Autotest.add_hook :run_command do |at|
24
+ # system "rake build"
25
+ # end
data/History.txt ADDED
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2019-05-16
2
+
3
+ * 1 major enhancement
4
+
5
+ * Initialize Ruby gem using Hoe
6
+
data/Manifest.txt ADDED
@@ -0,0 +1,9 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ ext/davenport/extconf.rb
7
+ ext/davenport/davenport.c
8
+ lib/davenport.rb
9
+ test/test_davenport.rb
data/README.rdoc ADDED
@@ -0,0 +1,75 @@
1
+ = davenport
2
+
3
+ home :: https://github.com/wbreeze/davenport-ruby
4
+ code :: https://github.com/wbreeze/davenport-ruby
5
+ rdoc :: FIX (url)
6
+ bugs :: https://github.com/wbreeze/davenport-ruby/issues
7
+ C-lib :: https://github.com/wbreeze/davenport
8
+
9
+ == DESCRIPTION:
10
+
11
+ A ranking is a partial order that, given some set of alternatives, places
12
+ some before others.
13
+ A preference graph, given some number of rankings, expresses the combination
14
+ of all of those rankings. It is a directed, weighted graph in which the
15
+ nodes represent the alternatives and the edges represent preferences.
16
+
17
+ Use this to build a preference graph from individual rankings and compute
18
+ a Kemeny order, aggregated preference that minimizes the number of
19
+ pair-wise disagreements with the individual rankings.
20
+
21
+ This is a Ruby binding built around a C language implementation of
22
+ Davenport's algorithm, found on github at
23
+ {wbreeze/davenport}[https://github.com/wbreeze/davenport]
24
+
25
+ == FEATURES/PROBLEMS:
26
+
27
+ Please see https://github.com/wbreeze/davenport-ruby/issues
28
+ for a list of any issues and add your own there, or contact
29
+ the author.
30
+
31
+ == SYNOPSIS:
32
+
33
+ pg = Davenport::PreferenceGraph.new(4)
34
+ pg.add_preference([1, 3, 2, 4]);
35
+ pg.add_preference([1, 2, 2, 4]);
36
+ pg.add_preference([2, 1, 3, 4]);
37
+ pg.add_preference([1, 3, 2, 4]);
38
+ pg.davenport
39
+ => [1, 3, 2, 4]
40
+
41
+ The single array parameter for `add_preference` has a rank number for the item
42
+ at the corresponding index. For example, with four items, `[1, 2, 2, 4]`
43
+ assigns rank 1 to the first item, 2 to the second and third items, and 4 to the
44
+ final item. The preference, `[2, 2, 2, 1]` ranks the last item first and all of
45
+ the others equally after it.
46
+
47
+ == REQUIREMENTS:
48
+
49
+ See .ruby-version for the version of Ruby used in development.
50
+
51
+ This requires the Davenport library installed according to
52
+ {wbreeze/davenport}[https://github.com/wbreeze/davenport]
53
+
54
+ == INSTALL:
55
+
56
+ gem install davenport
57
+
58
+ == DEVELOPERS:
59
+
60
+ After checking out the source, run:
61
+
62
+ $ rake newb
63
+
64
+ This task will install any missing dependencies, run the tests/specs,
65
+ and generate the RDoc.
66
+
67
+ == LICENSE:
68
+
69
+ GNU Lesser General Public License v3 (LGPL-3.0)
70
+
71
+ See the file,
72
+ {LICENSE}[https://github.com/wbreeze/davenport-ruby/blob/master/LICENSE]
73
+ for the full text.
74
+
75
+ Copyright (c) 2019 Douglas Lovell
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require 'rake/extensiontask'
6
+
7
+ # Hoe.plugin :compiler
8
+ # Hoe.plugin :gem_prelude_sucks
9
+ # Hoe.plugin :inline
10
+ # Hoe.plugin :minitest
11
+ # Hoe.plugin :racc
12
+ # Hoe.plugin :rcov
13
+ # Hoe.plugin :rdoc
14
+
15
+ HOE = Hoe.spec "davenport" do
16
+ developer("Douglas Lovell", "doug@wbreeze.com")
17
+ license "LGPL-3.0"
18
+ self.spec_extras = {
19
+ extensions: ['ext/davenport/extconf.rb']
20
+ }
21
+ end
22
+
23
+ Rake::ExtensionTask.new('davenport', HOE.spec) do |ext|
24
+ ext.lib_dir = File.join('lib', 'davenport')
25
+ end
26
+
27
+ Rake::Task[:test].prerequisites << :compile
28
+
29
+ # vim: syntax=ruby
@@ -0,0 +1,116 @@
1
+ #include <ruby.h>
2
+ #include <ruby/intern.h>
3
+ #include "davenport/aggregate_solution.h"
4
+ #include "davenport/davenport.h"
5
+ #include "davenport/network.h"
6
+ #include "davenport/preference_graph.h"
7
+ #include "extconf.h"
8
+
9
+ typedef struct RbDavenport {
10
+ int node_ct;
11
+ int *preference_graph;
12
+ } RbDavenport;
13
+
14
+ static void pg_deallocate(void *data)
15
+ {
16
+ RbDavenport *dvdata = (RbDavenport *)data;
17
+ if (dvdata->preference_graph != NULL) free(dvdata->preference_graph);
18
+ free(dvdata);
19
+ }
20
+
21
+ static size_t pg_report_size(const void *data)
22
+ {
23
+ RbDavenport *dvdata = (RbDavenport *)data;
24
+ return sizeof(RbDavenport) + ESZ(dvdata->node_ct);
25
+ }
26
+
27
+ static const rb_data_type_t rb_davenport_type = {
28
+ .wrap_struct_name = "RbDavenport",
29
+ .function = {
30
+ .dmark = NULL,
31
+ .dfree = pg_deallocate,
32
+ .dsize = pg_report_size,
33
+ },
34
+ .data = NULL,
35
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY,
36
+ };
37
+
38
+ static VALUE pg_allocate(VALUE self)
39
+ {
40
+ RbDavenport *dvdata = malloc(sizeof(RbDavenport));
41
+ dvdata->node_ct = 0;
42
+ dvdata->preference_graph = NULL;
43
+ return TypedData_Wrap_Struct(self, &rb_davenport_type, dvdata);
44
+ }
45
+
46
+ static VALUE initialize(VALUE self, VALUE node_ct)
47
+ {
48
+ Check_Type(node_ct, T_FIXNUM);
49
+ RbDavenport *dvdata;
50
+ TypedData_Get_Struct(self, RbDavenport, &rb_davenport_type, dvdata);
51
+ dvdata->node_ct = FIX2INT(node_ct);
52
+ dvdata->preference_graph = edge_array_calloc(dvdata->node_ct);
53
+ return self;
54
+ }
55
+
56
+ VALUE add_preference(VALUE self, VALUE pref_array)
57
+ {
58
+ Check_Type(pref_array, T_ARRAY);
59
+ RbDavenport *dvdata;
60
+ TypedData_Get_Struct(self, RbDavenport, &rb_davenport_type, dvdata);
61
+ const long len = RARRAY_LEN(pref_array);
62
+ if (len != dvdata->node_ct) {
63
+ // says "define" but actually does lookup
64
+ VALUE mDv = rb_define_module("Davenport");
65
+ VALUE exc_cl = rb_define_class_under(mDv,
66
+ "PreferenceGraphCountException", rb_eStandardError);
67
+ rb_raise(exc_cl,
68
+ "Expected array of length %d, given %ld", dvdata->node_ct, len);
69
+ }
70
+ const int node_ct = dvdata->node_ct;
71
+ int rankings[node_ct];
72
+ for (int i = 0; i < node_ct; ++i) {
73
+ VALUE vi = rb_ary_entry(pref_array, i);
74
+ Check_Type(vi, T_FIXNUM);
75
+ rankings[i] = FIX2LONG(vi);
76
+ }
77
+ preference_graph_add_preference(dvdata->preference_graph, rankings, node_ct);
78
+ return Qnil;
79
+ }
80
+
81
+ VALUE davenport(VALUE self)
82
+ {
83
+ RbDavenport *dvdata;
84
+ TypedData_Get_Struct(self, RbDavenport, &rb_davenport_type, dvdata);
85
+ int node_ct = dvdata->node_ct;
86
+
87
+ int *majority_graph = edge_array_calloc(node_ct);
88
+ preference_graph_to_majority_graph(
89
+ dvdata->preference_graph, majority_graph, node_ct);
90
+
91
+ AggregateSolution *asol = aggregate_solution_create(node_ct);
92
+ Davenport *dv = davenport_create(majority_graph, node_ct);
93
+ davenport_set_solution_callback(dv, &aggregate_solution_add_solution, asol);
94
+ davenport_compute(dv);
95
+ dv = davenport_destroy(dv);
96
+
97
+ int *r = aggregate_solution_ranking(asol);
98
+ VALUE pref = rb_ary_new();
99
+ for (int i = 0; i < node_ct; ++i) {
100
+ rb_ary_push(pref, INT2NUM(r[i]));
101
+ }
102
+
103
+ asol = aggregate_solution_destroy(asol);
104
+ free(majority_graph);
105
+ return pref;
106
+ }
107
+
108
+ void Init_davenport()
109
+ {
110
+ VALUE mDv = rb_define_module("Davenport");
111
+ VALUE cPg = rb_define_class_under(mDv, "PreferenceGraph", rb_cData);
112
+ rb_define_alloc_func(cPg, pg_allocate);
113
+ rb_define_method(cPg, "initialize", initialize, 1);
114
+ rb_define_method(cPg, "add_preference", add_preference, 1);
115
+ rb_define_method(cPg, "davenport", davenport, 0);
116
+ }
@@ -0,0 +1,9 @@
1
+ #! ruby
2
+ require 'mkmf'
3
+
4
+ unless have_library('davenport', 'preference_graph_add_preference')
5
+ abort 'Need libdavenport, see https://github.com/wbreeze/davenport'
6
+ end
7
+
8
+ create_header
9
+ create_makefile 'davenport/davenport'
data/lib/davenport.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'davenport/davenport'
2
+
3
+ module Davenport
4
+ VERSION = "1.0.0"
5
+
6
+ class PreferenceGraphCountException < StandardError
7
+ end
8
+ end
@@ -0,0 +1,54 @@
1
+ require "minitest/autorun"
2
+ require "davenport"
3
+
4
+ class TestDavenport < Minitest::Test
5
+ def test_sanity
6
+ refute_nil(Davenport::VERSION)
7
+ end
8
+
9
+ def test_one_ranking
10
+ node_ct = 4;
11
+ pg = Davenport::PreferenceGraph.new(node_ct)
12
+ expected_ranking = [3, 4, 1, 2];
13
+ pg.add_preference(expected_ranking);
14
+ assert_equal(expected_ranking, pg.davenport);
15
+ end
16
+
17
+ def test_constructor_type_error
18
+ assert_raises(TypeError) do
19
+ Davenport::PreferenceGraph.new(Object.new)
20
+ end
21
+ end
22
+
23
+ def test_add_preference_type_error
24
+ pg = Davenport::PreferenceGraph.new(4)
25
+ assert_raises(TypeError) do
26
+ pg.add_preference(Object.new)
27
+ end
28
+ end
29
+
30
+ def test_add_preference_array_type_error
31
+ pg = Davenport::PreferenceGraph.new(3)
32
+ assert_raises(TypeError) do
33
+ a = [Object.new, Object.new, Object.new];
34
+ pg.add_preference(a)
35
+ end
36
+ end
37
+
38
+ def test_add_preference_array_length_error
39
+ pg = Davenport::PreferenceGraph.new(3)
40
+ assert_raises(Davenport::PreferenceGraphCountException) do
41
+ a = [1, 1];
42
+ pg.add_preference(a)
43
+ end
44
+ end
45
+
46
+ def test_ranking_two
47
+ pg = Davenport::PreferenceGraph.new(4)
48
+ pg.add_preference([1, 3, 2, 4])
49
+ pg.add_preference([1, 2, 2, 4])
50
+ pg.add_preference([2, 1, 3, 4])
51
+ pg.add_preference([1, 3, 2, 4])
52
+ assert_equal([1, 3, 2, 4], pg.davenport)
53
+ end
54
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: davenport
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Douglas Lovell
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '7'
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '4.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '7'
33
+ - !ruby/object:Gem::Dependency
34
+ name: hoe
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.17'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.17'
47
+ description: |-
48
+ A ranking is a partial order that, given some set of alternatives, places
49
+ some before others.
50
+ A preference graph, given some number of rankings, expresses the combination
51
+ of all of those rankings. It is a directed, weighted graph in which the
52
+ nodes represent the alternatives and the edges represent preferences.
53
+
54
+ Use this to build a preference graph from individual rankings and compute
55
+ a Kemeny order, aggregated preference that minimizes the number of
56
+ pair-wise disagreements with the individual rankings.
57
+
58
+ This is a Ruby binding built around a C language implementation of
59
+ Davenport's algorithm, found on github at
60
+ {wbreeze/davenport}[https://github.com/wbreeze/davenport]
61
+ email:
62
+ - doug@wbreeze.com
63
+ executables: []
64
+ extensions:
65
+ - ext/davenport/extconf.rb
66
+ extra_rdoc_files:
67
+ - History.txt
68
+ - Manifest.txt
69
+ - README.rdoc
70
+ files:
71
+ - ".autotest"
72
+ - History.txt
73
+ - Manifest.txt
74
+ - README.rdoc
75
+ - Rakefile
76
+ - ext/davenport/davenport.c
77
+ - ext/davenport/extconf.rb
78
+ - lib/davenport.rb
79
+ - test/test_davenport.rb
80
+ homepage: https://github.com/wbreeze/davenport-ruby
81
+ licenses:
82
+ - LGPL-3.0
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options:
86
+ - "--main"
87
+ - README.rdoc
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ requirements: []
101
+ rubygems_version: 3.0.3
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: A ranking is a partial order that, given some set of alternatives, places
105
+ some before others
106
+ test_files: []