ravensat 0.1.1 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a47d909acb01362653613bd0a4df6da1d426303a5e0919987b587fe3adcb77fd
4
- data.tar.gz: 46f323addbd8e5f183eeb8bd81c28ffd0994553bc63a2e99689b991a43d1e508
3
+ metadata.gz: 76d71945818d257f977fde25685896cc7e8c7da0d290f329525beb5d7e5c2ca2
4
+ data.tar.gz: 2c201e0b4815822e40f7b15fe8b1492a532c6665bc456a87c8c6f42227468f38
5
5
  SHA512:
6
- metadata.gz: 31ebd03521bce3df1d2447d9bddba7eef23deced640285f9bbfb79cf790bdc2a4af45b6c299e4ed5c6aa8000a43e4114b4d7045f1e963305175de85728a01cee
7
- data.tar.gz: '097189b8c4d177f9cd8658fa4ff16481616dfb9ca72c65fbe80fbcbb63eaa453db55067312ff152c4fff01e613f4f82c745e7594b6a00861ee2b266c7ea740d1'
6
+ metadata.gz: 4a25a24ca9553e5ea66737f21d5cf9942b9202f106f3c0063d3a4fa84816b665797a761ac2399ef0de374e36bfa29a23c5dccaf7f78f0adfa73cd9e45558e591
7
+ data.tar.gz: 74baa2d28e39e50dca6d280a411cc0fd5727f15dca74ada2ec94eb4a1c1c7d785e686293de0202d0e249c03a78685be3c823917519936d79438740c0bd861b90
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ravensat (0.1.0)
4
+ ravensat (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # Ravensat
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/ravensat`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ Ravensat is an interface to SAT Solver .
4
+ In order to use Ravensat, you need to install SAT Solver and specify the name of the Solver .
5
+ (If you do not specify SAT Solver, it will use the one bundled in the gem .)
4
6
 
5
- TODO: Delete this and the text above, and describe your gem
6
7
 
7
- ## Installation(Not implemented)
8
+ ## Installation
8
9
 
9
10
  Add this line to your application's Gemfile:
10
11
 
@@ -25,8 +26,8 @@ Or install it yourself as:
25
26
  ```ruby
26
27
  require 'ravensat'
27
28
 
28
- a = Ravensat::PropVar.new
29
- b = Ravensat::PropVar.new
29
+ a = Ravensat::VarNode.new
30
+ b = Ravensat::VarNode.new
30
31
 
31
32
  a.value #=> nil
32
33
  b.value #=> nil
@@ -40,7 +41,7 @@ a.value #=> true
40
41
  b.value #=> true
41
42
  ```
42
43
 
43
- ## Development(Not implemented)
44
+ ## Development
44
45
 
45
46
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
46
47
 
@@ -0,0 +1,48 @@
1
+ require 'bundler/setup'
2
+ require 'ravensat'
3
+ require 'pry'
4
+
5
+ # | ---- | ---- | ---- |
6
+ # | X1 | X2 | X3 |
7
+ # | X4 | X5 | X6 |
8
+ # | X7 | X8 | X9 |
9
+ # | ---- | ---- | ---- |
10
+
11
+ x1 = Array.new(9){ Ravensat::VarNode.new }
12
+ x2 = Array.new(9){ Ravensat::VarNode.new }
13
+ x3 = Array.new(9){ Ravensat::VarNode.new }
14
+ x4 = Array.new(9){ Ravensat::VarNode.new }
15
+ x5 = Array.new(9){ Ravensat::VarNode.new }
16
+ x6 = Array.new(9){ Ravensat::VarNode.new }
17
+ x7 = Array.new(9){ Ravensat::VarNode.new }
18
+ x8 = Array.new(9){ Ravensat::VarNode.new }
19
+ x9 = Array.new(9){ Ravensat::VarNode.new }
20
+ x = [x1, x2, x3, x4, x5, x6, x7, x8, x9]
21
+
22
+ logic = x1[4]
23
+
24
+ # one square, one number
25
+ 9.times do |i|
26
+ logic &= Ravensat::RavenClaw.alo [x[i][0], x[i][1], x[i][2], x[i][3], x[i][4], x[i][5], x[i][6], x[i][7], x[i][8]]
27
+ logic &= Ravensat::RavenClaw.amo [x[i][0], x[i][1], x[i][2], x[i][3], x[i][4], x[i][5], x[i][6], x[i][7], x[i][8]]
28
+ end
29
+
30
+ # x1 ~ x9 are all different numbers
31
+ 9.times do |i|
32
+ logic &= Ravensat::RavenClaw.alo [x[0][i], x[1][i], x[2][i], x[3][i], x[4][i], x[5][i], x[6][i], x[7][i], x[8][i]]
33
+ logic &= Ravensat::RavenClaw.amo [x[0][i], x[1][i], x[2][i], x[3][i], x[4][i], x[5][i], x[6][i], x[7][i], x[8][i]]
34
+ end
35
+
36
+ # TODO: generate constraint that sum to 15 in a row
37
+ # sum to 15 in a row
38
+
39
+ solver = Ravensat::Solver.new
40
+ solver.solve logic
41
+
42
+ puts <<~"MAGIC_SQUARE"
43
+ | --- | --- | --- |
44
+ | #{x1.index{|x| x.value} + 1} | #{x2.index{|x| x.value} + 1} | #{x3.index{|x| x.value} + 1} |
45
+ | #{x4.index{|x| x.value} + 1} | #{x5.index{|x| x.value} + 1} | #{x6.index{|x| x.value} + 1} |
46
+ | #{x7.index{|x| x.value} + 1} | #{x8.index{|x| x.value} + 1} | #{x9.index{|x| x.value} + 1} |
47
+ | --- | --- | --- |
48
+ MAGIC_SQUARE
@@ -0,0 +1,4 @@
1
+ module Ravensat
2
+ class AndNode < OprNode
3
+ end
4
+ end
@@ -0,0 +1,51 @@
1
+ module Ravensat
2
+ class Node
3
+ include Enumerable
4
+
5
+ attr_reader :children
6
+ def initialize
7
+ @children = []
8
+ end
9
+
10
+ def each
11
+ yield(self)
12
+ @children.each do |child|
13
+ child.each {|c| yield(c)}
14
+ end
15
+ end
16
+
17
+ def &(object)
18
+ AndNode.new(self, object)
19
+ end
20
+
21
+ def |(object)
22
+ OrNode.new(self, object)
23
+ end
24
+
25
+ # def tree_text
26
+ # self.each do |child|
27
+ # child.to_s
28
+ # end
29
+ # end
30
+
31
+ def to_s
32
+ self.class.name
33
+ end
34
+
35
+ def cnf?
36
+ @children.map(&:cnf?).reduce(:&)
37
+ end
38
+
39
+ def vars
40
+ self.select{|node| node.is_a? VarNode}.uniq
41
+ end
42
+
43
+ def vars_size
44
+ self.vars.size
45
+ end
46
+
47
+ def clauses_size
48
+ self.count{|node| node.is_a? AndNode} + 1
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,4 @@
1
+ module Ravensat
2
+ class NotNode < OprNode
3
+ end
4
+ end
@@ -0,0 +1,7 @@
1
+ module Ravensat
2
+ class OprNode < Node
3
+ def initialize(*nodes)
4
+ @children = nodes
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Ravensat
2
+ class OrNode < OprNode
3
+ def cnf?
4
+ return false if @children.any?{|node| node.is_a? AndNode}
5
+ @children.map(&:cnf?).reduce(:&)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,17 @@
1
+ module Ravensat
2
+ class VarNode < Node
3
+ attr_accessor :value
4
+ def initialize
5
+ @value
6
+ @children = []
7
+ end
8
+
9
+ def ~@
10
+ NotNode.new(self)
11
+ end
12
+
13
+ def cnf?
14
+ true
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,24 @@
1
+ module Ravensat
2
+ class DimacsDecoder
3
+ def decode(model, name_table)
4
+ inverted_name_table = name_table.invert
5
+ case model.first
6
+ when "SAT"
7
+ model.last.split.each do |e|
8
+ if e == '0'
9
+ next
10
+ elsif e[0] == "-"
11
+ index = e.slice(1..-1)
12
+ inverted_name_table[index].value = false
13
+ else
14
+ index = e
15
+ inverted_name_table[index].value = true
16
+ end
17
+ end
18
+ true
19
+ when "UNSAT"
20
+ false
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ module Ravensat
2
+ class DimacsEncoder
3
+ attr_reader :name_table
4
+ def initialize
5
+ @name_table = {}
6
+ end
7
+
8
+ def to_dimacs(formula)
9
+ return nil unless formula.cnf?
10
+
11
+ dimacs_header = "p cnf #{formula.vars_size} #{formula.clauses_size}"
12
+ dimacs_body = ""
13
+ create_table(formula)
14
+ formula.each do |node|
15
+ case node
16
+ when AndNode
17
+ when OrNode then dimacs_body << "\n"
18
+ when NotNode then dimacs_body << "-"
19
+ when VarNode then dimacs_body << @name_table[node] << " "
20
+ end
21
+ end
22
+
23
+ dimacs_body.gsub!(/\n{2,}/, "\n").gsub!(/\n/, "0\n") << '0'
24
+
25
+ dimacs_header + dimacs_body
26
+ end
27
+
28
+ def create_table(formula)
29
+ @name_table = formula.vars.zip((1..formula.vars.size).map(&:to_s)).to_h
30
+ end
31
+ end
32
+ end
@@ -15,10 +15,11 @@ module Ravensat
15
15
  # end
16
16
 
17
17
  def solve( cnf )
18
+ encoder = DimacsEncoder.new
18
19
  @input_file = Tempfile.open(["ravensat",".cnf"])
19
20
  @output_file = Tempfile.open(["ravensat",".mdl"])
20
21
 
21
- @input_file.write cnf.to_dimacs
22
+ @input_file.write encoder.to_dimacs(cnf)
22
23
  @input_file.flush
23
24
 
24
25
  case @name
@@ -28,18 +29,9 @@ module Ravensat
28
29
  system("#{@name} #{@input_file.to_path} #{@output_file.to_path}")
29
30
  end
30
31
 
32
+ decoder = DimacsDecoder.new
31
33
  model = @output_file.read.split("\n")
32
-
33
- case model.first
34
- when "SAT"
35
- model.last.split.each do |e|
36
- next if e == '0'
37
- cnf.name_table.find{|key,value| value == e.to_i.abs.to_s}.first.value = !e.start_with?('-')
38
- end
39
- Arcteryx::SAT
40
- when "UNSAT"
41
- Arcteryx::UNSAT
42
- end
34
+ decoder.decode(model, encoder.name_table)
43
35
  end
44
36
 
45
37
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ravensat
4
- VERSION = "0.1.1"
4
+ VERSION = "0.2.1"
5
5
  end
data/lib/ravensat.rb CHANGED
@@ -4,12 +4,20 @@ require_relative "ravensat/version"
4
4
 
5
5
  module Ravensat
6
6
  ravensat = File.dirname(__FILE__) + '/ravensat'
7
+ ast = File.dirname(__FILE__) + '/ravensat/ast'
7
8
  arcteryx = File.dirname(__FILE__) + '/arcteryx'
8
9
 
9
10
  autoload :Solver, ravensat + '/solver.rb'
10
- autoload :PropVar, ravensat + '/prop_var.rb'
11
- autoload :PropLogic, ravensat + '/prop_logic.rb'
11
+ autoload :DimacsEncoder, ravensat + '/dimacs_encoder.rb'
12
+ autoload :DimacsDecoder, ravensat + '/dimacs_decoder.rb'
12
13
  autoload :RavenClaw, ravensat + '/ravenclaw.rb'
13
14
 
15
+ autoload :Node, ast + '/node.rb'
16
+ autoload :VarNode, ast + '/var_node.rb'
17
+ autoload :OprNode, ast + '/opr_node.rb'
18
+ autoload :AndNode, ast + '/and_node.rb'
19
+ autoload :OrNode, ast + '/or_node.rb'
20
+ autoload :NotNode, ast + '/not_node.rb'
21
+
14
22
  autoload :Arcteryx, arcteryx + '/arcteryx.rb'
15
23
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ravensat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - rikuto matsuda
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-12-16 00:00:00.000000000 Z
11
+ date: 2022-05-16 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
@@ -27,11 +27,18 @@ files:
27
27
  - Rakefile
28
28
  - bin/console
29
29
  - bin/setup
30
+ - example/magic_square_3x3.rb
30
31
  - lib/arcteryx/arcteryx.rb
31
32
  - lib/arcteryx/cnf.rb
32
33
  - lib/ravensat.rb
33
- - lib/ravensat/prop_logic.rb
34
- - lib/ravensat/prop_var.rb
34
+ - lib/ravensat/ast/and_node.rb
35
+ - lib/ravensat/ast/node.rb
36
+ - lib/ravensat/ast/not_node.rb
37
+ - lib/ravensat/ast/opr_node.rb
38
+ - lib/ravensat/ast/or_node.rb
39
+ - lib/ravensat/ast/var_node.rb
40
+ - lib/ravensat/dimacs_decoder.rb
41
+ - lib/ravensat/dimacs_encoder.rb
35
42
  - lib/ravensat/ravenclaw.rb
36
43
  - lib/ravensat/solver.rb
37
44
  - lib/ravensat/version.rb
@@ -1,92 +0,0 @@
1
- module Ravensat
2
- class PropLogic
3
- attr_reader :formula
4
- attr_reader :name_table
5
-
6
- def initialize( init_formula )
7
- @formula = init_formula
8
- # (a | b) & (~a | b) & (a | ~b) & (~a | ~b)
9
-
10
- # [:and,
11
- # [:or, a, b],
12
- # [:or, [:not, a], b],
13
- # [:or, a, [:not, b]],
14
- # [:or, [:not, a], [:not, b]]
15
- # ]
16
- end
17
-
18
- def to_cnf
19
-
20
- end
21
-
22
- def to_dimacs
23
- cnf_text = String.new
24
- tmp_name_table = @formula.flatten.uniq.reject{|e| e.class == Symbol}
25
- @name_table = tmp_name_table.zip((1..tmp_name_table.size).to_a.map{|e| e.to_s}).to_h
26
-
27
- nr_vars = tmp_name_table.size
28
- nr_clses = @formula.size - 1
29
- cnf_header = 'p cnf ' << nr_vars.to_s << ' ' << nr_clses.to_s << "\n"
30
- # @formula.is_cnf?
31
- @formula.each do |clause|
32
- # next if clause.first != :and
33
- case clause
34
- when Symbol
35
- next
36
- when Array
37
- case clause.first
38
- when :or
39
- clause.each do |literal|
40
- case literal
41
- when Symbol
42
- next
43
- when Ravensat::PropVar
44
- cnf_text << @name_table[literal] << ' '
45
- when Array #&& literal.first == :not
46
- cnf_text << '-' << @name_table[literal.last] << ' '
47
- end
48
- end
49
- when :not
50
- cnf_text << '-' << @name_table[clause.last] << ' '
51
- end
52
- when Ravensat::PropVar
53
- cnf_text << @name_table[clause] << ' '
54
- end
55
- cnf_text << '0' << "\n"
56
- end
57
- cnf_header + cnf_text
58
- end
59
-
60
- def &( object )
61
- if @formula.first == :and
62
- @formula.append object.formula
63
- else
64
- @formula = [:and, @formula, object.formula]
65
- end
66
- self
67
- # Ravensat::PropLogic.new [:and, @formula, object]
68
- # formulaがcnfである前提の実装
69
- # @formula & other_formula
70
- end
71
-
72
- def |( object )
73
- if @formula.first == :or
74
- @formula.append object.formula
75
- else
76
- @formula = [:or, @formula, object.formula]
77
- end
78
- self
79
- # Ravensat::PropLogic.new [:or, @formula, object]
80
- # 木構造に落とすのが難しそう,実装見送り
81
- # @formula | other_formula
82
- end
83
-
84
- # def to_a
85
- # @formula.each do |f|
86
- # if f.class == PropLogic
87
- # f = f.to_a
88
- # end
89
- # end
90
- # end
91
- end
92
- end
@@ -1,45 +0,0 @@
1
- module Ravensat
2
- class PropVar
3
- attr_accessor :value
4
-
5
- def initialize
6
- @value # => true | false | undef
7
- end
8
-
9
- # def +@
10
- # # unuse?
11
- # # return PropLogic object
12
- # 'this is +@ method'
13
- # end
14
-
15
- # def -@
16
- # # return PropLogic object
17
- # 'this is -@ method'
18
- # end
19
-
20
- def ~@
21
- Ravensat::PropLogic.new [:not, self]
22
- # return PropLogic object
23
- end
24
-
25
- def &( object )
26
- Ravensat::PropLogic.new [:and, self, object.formula]
27
- # return PropLogic object
28
- end
29
-
30
- def |( object )
31
- Ravensat::PropLogic.new [:or, self, object.formula]
32
- # return PropLogic object
33
- end
34
-
35
- def formula
36
- # $BIU$1>F?O%a%=%C%I(B
37
- self
38
- end
39
-
40
- # def self
41
- # 'this is self method'
42
- # end
43
-
44
- end
45
- end