davenport 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.autotest +25 -0
- data/History.txt +6 -0
- data/Manifest.txt +9 -0
- data/README.rdoc +75 -0
- data/Rakefile +29 -0
- data/ext/davenport/davenport.c +116 -0
- data/ext/davenport/extconf.rb +9 -0
- data/lib/davenport.rb +8 -0
- data/test/test_davenport.rb +54 -0
- metadata +106 -0
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
data/Manifest.txt
ADDED
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
|
+
}
|
data/lib/davenport.rb
ADDED
@@ -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: []
|