whatnot 1.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
+ SHA1:
3
+ metadata.gz: d99b73ec46c7bc6666a955f1964d5ac9c899e800
4
+ data.tar.gz: 06fa24a955a158fd0c353a27ac4b8668351dfc6a
5
+ SHA512:
6
+ metadata.gz: 326771f21e8b3fb830fa4984274dacb681ab94d007a8e9742287a848e64fba0e402a1a30aa8a65c8ca696da3dcfbb01928dc67b9314c97dc7c6161a505909cb8
7
+ data.tar.gz: 06c18358cab757a294f5c9492e1cad0c19f80452159b6aa749bd47e307ac13ae3eabe30d3355dbb4609e3d79fb52be2ee466b602803348d03e478a37d1f9d342
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'bundler' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('bundler', 'bundler')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'git-changelog' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('whatnot', 'git-changelog')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'htmldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('diff-lcs', 'htmldiff')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'ldiff' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('diff-lcs', 'ldiff')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.3
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ whatnot (1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.5)
10
+ rake (10.4.2)
11
+ rspec (3.3.0)
12
+ rspec-core (~> 3.3.0)
13
+ rspec-expectations (~> 3.3.0)
14
+ rspec-mocks (~> 3.3.0)
15
+ rspec-core (3.3.2)
16
+ rspec-support (~> 3.3.0)
17
+ rspec-expectations (3.3.1)
18
+ diff-lcs (>= 1.2.0, < 2.0)
19
+ rspec-support (~> 3.3.0)
20
+ rspec-mocks (3.3.2)
21
+ diff-lcs (>= 1.2.0, < 2.0)
22
+ rspec-support (~> 3.3.0)
23
+ rspec-support (3.3.0)
24
+
25
+ PLATFORMS
26
+ ruby
27
+
28
+ DEPENDENCIES
29
+ rake (>= 0.8.7)
30
+ rspec (>= 1.3.0)
31
+ whatnot!
32
+
33
+ BUNDLED WITH
34
+ 1.10.3
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 PRIMEDIA
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # Whatnot
2
+
3
+ Whatnot is a Ruby wrapper for the Minisat CSP solver with flexible constraint definition syntax.
4
+
5
+ ### Motivation
6
+
7
+ I looked at the existing tools for constraint-solving in Ruby and found that they were either: (a) too slow to handle highly complex systems of constraints, or (b) too inflexible to support all but the most well-known types of problems. So I wanted to provide a tool that was both flexible and fast.
8
+
9
+ ### Concepts
10
+
11
+ To define a constraint problem, you can create two types of variables:
12
+
13
+ 1. A *slot*: a variable that can only hold one value in an array of possible values.
14
+ 2. A *set*: a variable that can hold one or more of an array of values.
15
+
16
+ Then you may specify any number of *constraints* between any number of variables.
17
+
18
+ ### Syntax
19
+
20
+ #### Initializing
21
+
22
+ First, you have to create a SwitchInterpreter:
23
+
24
+ ```ruby
25
+ i = SwitchInterpreter.new
26
+ ```
27
+
28
+ #### Creating variables
29
+
30
+ To create a slot called `:B` which can either hold `1` or `2`:
31
+
32
+ ```ruby
33
+ i.create_slot(:B, [1,2])
34
+ ```
35
+
36
+ To create a set called `:C` which can hold up to 3 values between 1 and 6:
37
+
38
+ ```ruby
39
+ i.create_set(:C, [1,2,3,4,5,6], max_values: 3)
40
+ ```
41
+
42
+ #### Creating constraints
43
+
44
+ To create a constraint that all values of C should be greater than B:
45
+
46
+ ```ruby
47
+ i.create_constraint(:C, :B) { |**sol| sol[:C].all? { |c| c > sol[:B] } }
48
+ ```
49
+
50
+ ### Installing
51
+
52
+ Minisat must be installed. On a Mac:
53
+
54
+ ```bash
55
+ $ brew install gcc
56
+ $ brew install minisat
57
+ ```
58
+
data/lib/whatnot.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'pp'
2
+
3
+ Dir.glob(File.expand_path("../whatnot/*", __FILE__)).each { |f| require f }
@@ -0,0 +1,116 @@
1
+ require 'json'
2
+
3
+ module Whatnot
4
+ class DimacsCNFPrinter
5
+ def initialize(*args)
6
+ @vars = {}
7
+ @vars_total = 1
8
+ @file = File.open("solver-out.txt", "w+")
9
+ end
10
+
11
+ attr_reader :file
12
+
13
+ def puts(str="")
14
+ @file << str.chomp + "\n"
15
+ end
16
+
17
+ def value_name_from_pair(name, value)
18
+ "#{name}=#{value.to_json}"
19
+ end
20
+
21
+ def vars(names, domain)
22
+ names.each do |name|
23
+ var(name, domain)
24
+ end
25
+ end
26
+
27
+ def var(name, domain)
28
+ puts "c -------------"
29
+ puts "c uniquifying..."
30
+ puts "c var: #{name.inspect}"
31
+
32
+ iter1 = true
33
+ JSON.pretty_generate(domain).each_line do |slice|
34
+ prefix = iter1 ? "c domain: " : "c "
35
+ iter1 = false
36
+ puts "#{prefix}#{slice}"
37
+ end
38
+
39
+ new_var = DimacsCNFVar.new(name, domain, key_iter: @vars_total)
40
+ @vars_total = new_var.key_iter()
41
+
42
+ # it must be at least one of the possible values
43
+ puts "#{new_var.all_keys_as_array().join(" ")} 0"
44
+
45
+ # it can't be two values
46
+ new_var.all_keys_as_array().combination(2).each do |key1, key2|
47
+ puts "-#{key1} -#{key2} 0"
48
+ end
49
+
50
+ @vars[name] = new_var
51
+
52
+ puts "c -------------"
53
+ puts
54
+ end
55
+
56
+ def all_different(varnames)
57
+ puts "c -------------"
58
+ puts "c all_different..."
59
+ puts "c varnames: #{varnames.inspect}"
60
+
61
+ varnames.combination(2).each do |varname1, varname2|
62
+ var1 = @vars[varname1]
63
+ var2 = @vars[varname2]
64
+
65
+ var1.matching_pairs(var2).each do |key1, key2|
66
+ puts "-#{key1} -#{key2} 0"
67
+ end
68
+ end
69
+ puts "c -------------"
70
+
71
+ end
72
+
73
+ def constrain(*varnames)
74
+ puts "c -------------"
75
+ puts "c constraining..."
76
+ puts "c varnames: #{varnames.inspect}"
77
+
78
+ argument_sets = nil
79
+ varnames.each do |varname|
80
+ argument_set = @vars[varname].argument_set()
81
+
82
+ if argument_sets.nil?
83
+ argument_sets ||= argument_set
84
+ else
85
+ argument_sets = argument_sets.product(argument_set).map(&:flatten)
86
+ end
87
+ end
88
+
89
+ argument_sets.each do |argument_set|
90
+ arguments_to_constraint = argument_set.values_at(* argument_set.each_index.select {|i| i.even?})
91
+ key_set = argument_set.values_at(* argument_set.each_index.select {|i| i.odd?})
92
+
93
+ result = yield(*arguments_to_constraint)
94
+
95
+ if !result
96
+ puts "#{key_set.map { |k| "-" + k.to_s }.join(" ")} 0"
97
+ end
98
+ end
99
+
100
+ puts "c -------------"
101
+ end
102
+
103
+ def all_pairs(vars, &block)
104
+ vars.combination(2).each do |var1, var2|
105
+ constrain(var1, var2, &block)
106
+ end
107
+ end
108
+
109
+ def solve
110
+ `minisat solver-out.txt minisat-out.txt`
111
+
112
+ solution = File.read("minisat-out.txt").lines[1]
113
+ DimacsCNFVar.interpret(solution)
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,73 @@
1
+ module Whatnot
2
+ class DimacsCNFVar
3
+ def self.names
4
+ @@names ||= {}
5
+ end
6
+
7
+ def self.keys
8
+ @@keys ||= {}
9
+ end
10
+
11
+ def value_name_from_pair(name, value)
12
+ "#{name}=#{value.to_json}"
13
+ end
14
+
15
+ def self.interpret(solution)
16
+ keys = solution.split(" ").map(&:to_i).select { |k| k > 0 }
17
+ values = DimacsCNFVar.names.values_at(*keys)
18
+
19
+ values = values.map do |val|
20
+ key, v = *val.match(/\A([^=]+)=(.*)\Z/)[1..2]
21
+ [key.to_sym, JSON.parse(v)]
22
+ end
23
+
24
+ Hash[values]
25
+ end
26
+
27
+ attr_reader :all_keys_as_array, :key_iter
28
+
29
+ def initialize(name, domain, key_iter: 1)
30
+ @name = name
31
+ @domain = domain
32
+ @key_iter = key_iter
33
+
34
+ generate_keys
35
+ end
36
+
37
+ def matching_pairs(var2)
38
+ (self.keys_by_value.keys & var2.keys_by_value.keys).map do |value|
39
+ [self.keys_by_value[value], var2.keys_by_value[value]]
40
+ end
41
+ end
42
+
43
+ def argument_set
44
+ keys_by_value.to_a
45
+ end
46
+
47
+ protected
48
+
49
+ def generate_keys
50
+ all_val_keys = []
51
+
52
+ @domain.each do |value|
53
+ value_name = value_name_from_pair(@name, value)
54
+ all_val_keys << @key_iter
55
+
56
+ DimacsCNFVar.names[@key_iter] = value_name
57
+ DimacsCNFVar.keys[value_name] = @key_iter
58
+ @key_iter += 1
59
+ end
60
+
61
+ @all_keys_as_array = all_val_keys
62
+ end
63
+
64
+ def keys_by_value
65
+ @keys_by_value ||=
66
+ begin
67
+ arr = DimacsCNFVar.keys.select { |k,v| k.start_with?(@name.to_s) }
68
+ arr = arr.map { |k,v| [JSON.parse(k[(@name.to_s.size+1)..-1], symbolize_names: true), v] }
69
+ Hash[arr]
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,25 @@
1
+ # a simple object that represents a failed constraint. acts as SwitchGroup.
2
+ class FailedSolutionSwitchGroup
3
+ attr_accessor :failed_solutions
4
+
5
+ def initialize(failed_solution_dimacs_strings)
6
+ @failed_solutions = failed_solution_dimacs_strings
7
+ end
8
+
9
+ def dimacs
10
+ @failed_solutions.map { |str| inverse_of(str) }.join
11
+ end
12
+
13
+ def switches
14
+ []
15
+ end
16
+
17
+ private
18
+
19
+ def inverse_of(solution_string)
20
+ solution_string.
21
+ split(" ").
22
+ map { |num| (num.to_i * -1).to_s }.
23
+ join(" ") + "\n"
24
+ end
25
+ end