davenport 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|