ravensat 0.2.1 → 0.3.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: 76d71945818d257f977fde25685896cc7e8c7da0d290f329525beb5d7e5c2ca2
4
- data.tar.gz: 2c201e0b4815822e40f7b15fe8b1492a532c6665bc456a87c8c6f42227468f38
3
+ metadata.gz: 47afc062227efddad35ed4643e28c4eea8957ecb32c8aa34d3c69a9ff52d3a88
4
+ data.tar.gz: a39c4f15fb17321ded803e79b702c55150197b3a613a340ca4a728e0a6a27402
5
5
  SHA512:
6
- metadata.gz: 4a25a24ca9553e5ea66737f21d5cf9942b9202f106f3c0063d3a4fa84816b665797a761ac2399ef0de374e36bfa29a23c5dccaf7f78f0adfa73cd9e45558e591
7
- data.tar.gz: 74baa2d28e39e50dca6d280a411cc0fd5727f15dca74ada2ec94eb4a1c1c7d785e686293de0202d0e249c03a78685be3c823917519936d79438740c0bd861b90
6
+ metadata.gz: f29375d0971be0c3a721e9932fec0b69ad9db5b6df089e9d07fad557a82fac176faf7b71eea0ca73b8ee76ba60b413bfc799fd2de798c0b0fc513e39c8c0388d
7
+ data.tar.gz: a596fa29bfb55341a016ddde64b2e7d788a4877d4504cd5921cee81e486ee28207fd14027c81c0e113c8485edd0cc9bd5921beac75728da85e3390c1eab4d23f
@@ -0,0 +1,18 @@
1
+ name: Ruby
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v2
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 3.0.0
14
+ - name: Run the default task
15
+ run: |
16
+ gem install bundler -v 2.2.3
17
+ bundle install
18
+ bundle exec rake
data/README.md CHANGED
@@ -1,8 +1,50 @@
1
1
  # Ravensat
2
2
 
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 .)
3
+ [![Ruby](https://github.com/matsuda0528/ravensat/actions/workflows/main.yml/badge.svg)](https://github.com/matsuda0528/ravensat/actions/workflows/main.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/ravensat.svg)](https://badge.fury.io/rb/ravensat)
5
+ [![LICENSE](https://img.shields.io/github/license/matsuda0528/ravensat)](https://opensource.org/licenses/MIT)
6
+
7
+ Ravensat is an interface to SAT solver in Ruby.
8
+
9
+ In order to use Ravensat, you need to install SAT solver.
10
+ If you do not install SAT solver, it will use the one bundled in the gem.
11
+
12
+ About [SAT](https://en.wikipedia.org/wiki/Boolean_satisfiability_problem), [SAT solver](https://en.wikipedia.org/wiki/SAT_solver)
13
+
14
+ ## Description
15
+ To solve SAT, we usually use SAT solver.
16
+ Now, let's solve the following SAT with SAT solver.
17
+ <p align="center">
18
+ <img src="https://latex.codecogs.com/svg.image?\inline&space;\large&space;\bg{white}(1&space;\lor&space;\lnot&space;5&space;\lor&space;4)&space;\land&space;(\lnot&space;1&space;\lor&space;5&space;\lor&space;3&space;\lor&space;4)&space;\land&space;(\lnot&space;3&space;\lor&space;\lnot&space;4)" style="background-color:white;"/>
19
+ </p>
20
+
21
+ Most SAT solvers are input in [DIMACS Format](https://www.cs.utexas.edu/users/moore/acl2/manuals/current/manual/index-seo.php/SATLINK____DIMACS).
22
+ Converting the example SAT to DIMACS Format yields the following.
23
+
24
+ ```DIMACS Format
25
+ p cnf 5 3
26
+ 1 -5 4 0
27
+ -1 5 3 4 0
28
+ -3 -4 0
29
+ ```
30
+ DIMACS Format is widely distributed as an I/O format for SAT solver.
31
+ However, when solving a large SAT, the following problems occur.
32
+ - Need to create a file with thousands of lines.
33
+ - Confusion arises because of the inability to name variables meaningfully.
34
+
35
+ To solve these problems, Ravensat can be used.
36
+ Using Ravensat, propositional variables can be defined as local variables in Ruby.
37
+ ```ruby
38
+ fuji_is_the_highest_mountain_in_japan = Ravensat::VarNode.new
39
+ ```
40
+ In addition, you can write logical expressions intuitively.
41
+ ```ruby
42
+ x = Ravensat::VarNode.new
43
+ y = Ravensat::VarNode.new
44
+
45
+ # (x or y) and (not x or y)
46
+ (x | y) & (~x | y)
47
+ ```
6
48
 
7
49
 
8
50
  ## Installation
@@ -22,23 +64,81 @@ Or install it yourself as:
22
64
  $ gem install ravensat
23
65
 
24
66
  ## Usage
25
-
67
+ ### Basic Usage
68
+ This is a basic usage example of the library.
26
69
  ```ruby
27
70
  require 'ravensat'
28
71
 
72
+ # Define propositional variables
29
73
  a = Ravensat::VarNode.new
30
74
  b = Ravensat::VarNode.new
31
75
 
32
- a.value #=> nil
33
- b.value #=> nil
76
+ a.result #=> nil
77
+ b.result #=> nil
34
78
 
79
+ # Generate logical expressions as CNF
35
80
  logic = (a | b) & (~a | b) & (a | ~b)
36
81
 
82
+ # Launch SAT solver
37
83
  solver = Ravensat::Solver.new
38
84
  solver.solve logic #=> true(SAT)
39
85
 
40
- a.value #=> true
41
- b.value #=> true
86
+ # Refer to the satisfiability
87
+ a.result #=> true
88
+ b.result #=> true
89
+ ```
90
+
91
+ If you have SAT solver installed, you can write:
92
+ ```ruby
93
+ # Launch SAT solver
94
+ solver = Ravensat::Solver.new("<solver_name>")
95
+ solver.solve logic
96
+ ```
97
+ The available solvers are assumed to be those that can be I/O in the DIMACS Format.
98
+ At least, we have confirmed that it works properly with [MiniSat](https://github.com/niklasso/minisat).
99
+
100
+ If you do not use an external SAT solver, create a SAT solver object without any constructor arguments.
101
+ In that case, **Arcteryx**(the very simple SAT solver built into Ravensat) will launch.
102
+
103
+ ### Extension Usage
104
+ In Ravensat::Extension, C-like variable definitions are available.
105
+
106
+ *Note: In Ravensat::Extension, all undefined variables and methods are caught by method_missing method.*
107
+
108
+ ```ruby
109
+ require 'ravensat'
110
+
111
+ module Ravensat
112
+ module Extension
113
+ bool a, b
114
+ logic = (a | b) & (~a | b) & (a | ~b)
115
+
116
+ solver = Ravensat::Solver.new
117
+ solver.solve logic #=> true
118
+
119
+ a.result #=> true
120
+ b.result #=> true
121
+ end
122
+ end
123
+ ```
124
+
125
+ ### Extension Usage(CSP; Constraint Satisfaction Problem)
126
+ It is possible to define integer variables and to describe some integer constraints.
127
+ ```ruby
128
+ require 'ravensat'
129
+
130
+ module Ravensat
131
+ module Extension
132
+ int a(1..10), b(1..10)
133
+ constraint = (a.only_one & b.only_one & (a != b))
134
+
135
+ solver = Ravensat::Solver.new
136
+ solver.solve constraint #=> true
137
+
138
+ a.result #=> 1
139
+ b.result #=> 2
140
+ end
141
+ end
42
142
  ```
43
143
 
44
144
  ## Development
data/exe/ravensat ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
+ require 'ravensat'
5
+ require 'pry'
6
+
7
+ Pry.start(Ravensat::Extension)
@@ -0,0 +1,11 @@
1
+ module Ravensat
2
+ class InitialNode < Node
3
+ def &(object)
4
+ object
5
+ end
6
+
7
+ def |(object)
8
+ object
9
+ end
10
+ end
11
+ end
@@ -14,6 +14,20 @@ module Ravensat
14
14
  end
15
15
  end
16
16
 
17
+ def each_with_clause
18
+ case self
19
+ when AndNode, OrNode
20
+ @children.first.each_with_clause{|c| yield(c)}
21
+ yield(self)
22
+ @children.last.each_with_clause{|c| yield(c)}
23
+ when NotNode
24
+ yield(self)
25
+ @children.first.each_with_clause{|c| yield(c)}
26
+ when VarNode
27
+ yield(self)
28
+ end
29
+ end
30
+
17
31
  def &(object)
18
32
  AndNode.new(self, object)
19
33
  end
@@ -22,12 +36,6 @@ module Ravensat
22
36
  OrNode.new(self, object)
23
37
  end
24
38
 
25
- # def tree_text
26
- # self.each do |child|
27
- # child.to_s
28
- # end
29
- # end
30
-
31
39
  def to_s
32
40
  self.class.name
33
41
  end
@@ -13,5 +13,9 @@ module Ravensat
13
13
  def cnf?
14
14
  true
15
15
  end
16
+
17
+ def result
18
+ @value
19
+ end
16
20
  end
17
21
  end
@@ -0,0 +1,11 @@
1
+ module Ravensat
2
+ dir = File.dirname(__FILE__) + '/ast'
3
+
4
+ autoload :Node, dir + '/node.rb'
5
+ autoload :VarNode, dir + '/var_node.rb'
6
+ autoload :OprNode, dir + '/opr_node.rb'
7
+ autoload :AndNode, dir + '/and_node.rb'
8
+ autoload :OrNode, dir + '/or_node.rb'
9
+ autoload :NotNode, dir + '/not_node.rb'
10
+ autoload :InitialNode, dir + '/initial_node.rb'
11
+ end
@@ -8,19 +8,18 @@ module Ravensat
8
8
  def to_dimacs(formula)
9
9
  return nil unless formula.cnf?
10
10
 
11
- dimacs_header = "p cnf #{formula.vars_size} #{formula.clauses_size}"
11
+ dimacs_header = "p cnf #{formula.vars_size} #{formula.clauses_size}\n"
12
12
  dimacs_body = ""
13
13
  create_table(formula)
14
- formula.each do |node|
14
+ formula.each_with_clause do |node|
15
15
  case node
16
- when AndNode
17
- when OrNode then dimacs_body << "\n"
16
+ when AndNode then dimacs_body << " 0\n"
17
+ when OrNode then dimacs_body << " "
18
18
  when NotNode then dimacs_body << "-"
19
- when VarNode then dimacs_body << @name_table[node] << " "
19
+ when VarNode then dimacs_body << @name_table[node]
20
20
  end
21
21
  end
22
-
23
- dimacs_body.gsub!(/\n{2,}/, "\n").gsub!(/\n/, "0\n") << '0'
22
+ dimacs_body << " 0\n"
24
23
 
25
24
  dimacs_header + dimacs_body
26
25
  end
@@ -0,0 +1,6 @@
1
+ module Ravensat
2
+ dir = File.dirname(__FILE__) + '/dimacs'
3
+
4
+ autoload :DimacsEncoder, dir + '/dimacs_encoder.rb'
5
+ autoload :DimacsDecoder, dir + '/dimacs_decoder.rb'
6
+ end
@@ -0,0 +1,24 @@
1
+ module Ravensat
2
+ module Extension
3
+ module Domain
4
+ LOCAL_VARIABLE_TABLE = {}
5
+ def int(*vars)
6
+ vars.each do |var|
7
+ next if var.is_defined?
8
+ LOCAL_VARIABLE_TABLE[var.name] = Ravensat::Extension::IntegerVariable.new(var.name, var.args)
9
+ end
10
+ end
11
+
12
+ def bool(*vars)
13
+ vars.each do |var|
14
+ next if var.is_defined?
15
+ LOCAL_VARIABLE_TABLE[var.name] = Ravensat::Extension::BooleanVariable.new(var.name, var.args)
16
+ end
17
+ end
18
+
19
+ def method_missing(name, *args)
20
+ LOCAL_VARIABLE_TABLE[name] || UndefinedVariable.new(name, args)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ require 'forwardable'
2
+
3
+ module Ravensat
4
+ module Extension
5
+ class BooleanVariable < Variable
6
+ extend Forwardable
7
+ delegate Node.public_instance_methods(false) => :@var_node
8
+ delegate OprNode.public_instance_methods(false) => :@var_node
9
+ delegate VarNode.public_instance_methods(false) => :@var_node
10
+
11
+ def initialize(name, args)
12
+ super
13
+ @var_node = VarNode.new
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ module Ravensat
2
+ module Extension
3
+ class IntegerVariable < Variable
4
+
5
+ attr_reader :var_nodes
6
+ def initialize(name, args)
7
+ super
8
+ return unless args.first.is_a? Range
9
+ @var_nodes = args.first.zip(Array.new(args.first.size){Ravensat::VarNode.new}).to_h
10
+ end
11
+
12
+ def ==(object)
13
+ case object
14
+ when Integer
15
+ return @var_nodes[object]
16
+ when IntegerVariable
17
+ result_formula = Ravensat::InitialNode.new
18
+ duplicated_keys = self.var_nodes.keys & object.var_nodes.keys
19
+
20
+ [self.var_nodes, object.var_nodes].repeated_permutation(duplicated_keys.size) do |var_nodes|
21
+ result_formula &= Ravensat::RavenClaw.alo(var_nodes.zip(duplicated_keys).map{|arr| arr.first[arr.last]})
22
+ end
23
+ return result_formula
24
+ else
25
+ raise ArgumentError
26
+ end
27
+ end
28
+
29
+ def !=(object)
30
+ case object
31
+ when Integer
32
+ return ~(@var_nodes[object])
33
+ when IntegerVariable
34
+ result_formula = Ravensat::InitialNode.new
35
+ duplicated_keys = self.var_nodes.keys & object.var_nodes.keys
36
+
37
+ duplicated_keys.each do |index|
38
+ result_formula &= ~(self.var_nodes[index]) | ~(object.var_nodes[index])
39
+ end
40
+ return result_formula
41
+ else
42
+ raise ArgumentError
43
+ end
44
+ end
45
+
46
+ def only_one
47
+ result_formula = Ravensat::InitialNode.new
48
+ result_formula &= Ravensat::RavenClaw.alo @var_nodes.values
49
+ result_formula &= Ravensat::RavenClaw.amo @var_nodes.values
50
+ result_formula
51
+ end
52
+
53
+ def result
54
+ result = @var_nodes.select{|key, var_node| var_node.value}.keys
55
+ if result.one?
56
+ result.first
57
+ else
58
+ result
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,9 @@
1
+ module Ravensat
2
+ module Extension
3
+ class UndefinedVariable < Variable
4
+ def is_defined?
5
+ false
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ module Ravensat
2
+ module Extension
3
+ class Variable
4
+
5
+ attr_reader :name, :args
6
+ def initialize(name, args)
7
+ @name = name
8
+ @args = args
9
+ end
10
+
11
+ def is_defined?
12
+ true
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module Ravensat
2
+ module Extension
3
+ dir = File.dirname(__FILE__) + '/extension'
4
+
5
+ autoload :Domain, dir + '/domain.rb'
6
+ autoload :Variable, dir + '/variable/variable.rb'
7
+ autoload :BooleanVariable, dir + '/variable/boolean_variable.rb'
8
+ autoload :IntegerVariable, dir + '/variable/integer_variable.rb'
9
+ autoload :UndefinedVariable, dir + '/variable/undefined_variable.rb'
10
+
11
+ extend Domain
12
+ end
13
+ end
@@ -1,15 +1,19 @@
1
1
  module Ravensat
2
- class RavenClaw
3
- def initialize; end
4
-
5
- def self.alo(prop_vars)
6
- prop_vars.reduce(:|)
2
+ module RavenClaw
3
+ def self.alo(bool_vars)
4
+ bool_vars.reduce(:|)
7
5
  end
8
6
 
9
- def self.amo(prop_vars)
10
- prop_vars.combination(2).map do |e|
7
+ def self.amo(bool_vars)
8
+ bool_vars.combination(2).map do |e|
11
9
  e.map(&:~@).reduce(:|)
12
10
  end.reduce(:&)
13
11
  end
12
+
13
+ def self.all_different(*int_vars)
14
+ int_vars.combination(2).map do |int_var|
15
+ int_var.reduce(:!=)
16
+ end.reduce(:&)
17
+ end
14
18
  end
15
19
  end
@@ -5,15 +5,8 @@ module Ravensat
5
5
  attr_accessor :name
6
6
  def initialize( default_solver_name = "arcteryx" )
7
7
  @name = default_solver_name
8
- # @cnf = Array.new
9
- # @nr_vars
10
- # @nr_clses
11
8
  end
12
9
 
13
- # def <<( clause )
14
- # 'this is << method'
15
- # end
16
-
17
10
  def solve( cnf )
18
11
  encoder = DimacsEncoder.new
19
12
  @input_file = Tempfile.open(["ravensat",".cnf"])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ravensat
4
- VERSION = "0.2.1"
4
+ VERSION = "0.3.1"
5
5
  end
data/lib/ravensat.rb CHANGED
@@ -7,17 +7,12 @@ module Ravensat
7
7
  ast = File.dirname(__FILE__) + '/ravensat/ast'
8
8
  arcteryx = File.dirname(__FILE__) + '/arcteryx'
9
9
 
10
+ require_relative ravensat + "/ast.rb"
11
+ require_relative ravensat + "/dimacs.rb"
12
+
10
13
  autoload :Solver, ravensat + '/solver.rb'
11
- autoload :DimacsEncoder, ravensat + '/dimacs_encoder.rb'
12
- autoload :DimacsDecoder, ravensat + '/dimacs_decoder.rb'
13
14
  autoload :RavenClaw, ravensat + '/ravenclaw.rb'
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'
15
+ autoload :Extension, ravensat + '/extension.rb'
21
16
 
22
17
  autoload :Arcteryx, arcteryx + '/arcteryx.rb'
23
18
  end
metadata CHANGED
@@ -1,22 +1,24 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ravensat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.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: 2022-05-16 00:00:00.000000000 Z
11
+ date: 2022-05-24 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description:
14
14
  email:
15
15
  - p99y92og@s.okayama-u.ac.jp
16
- executables: []
16
+ executables:
17
+ - ravensat
17
18
  extensions: []
18
19
  extra_rdoc_files: []
19
20
  files:
21
+ - ".github/workflows/main.yml"
20
22
  - ".gitignore"
21
23
  - ".rspec"
22
24
  - ".ruby-version"
@@ -28,17 +30,27 @@ files:
28
30
  - bin/console
29
31
  - bin/setup
30
32
  - example/magic_square_3x3.rb
33
+ - exe/ravensat
31
34
  - lib/arcteryx/arcteryx.rb
32
35
  - lib/arcteryx/cnf.rb
33
36
  - lib/ravensat.rb
37
+ - lib/ravensat/ast.rb
34
38
  - lib/ravensat/ast/and_node.rb
39
+ - lib/ravensat/ast/initial_node.rb
35
40
  - lib/ravensat/ast/node.rb
36
41
  - lib/ravensat/ast/not_node.rb
37
42
  - lib/ravensat/ast/opr_node.rb
38
43
  - lib/ravensat/ast/or_node.rb
39
44
  - lib/ravensat/ast/var_node.rb
40
- - lib/ravensat/dimacs_decoder.rb
41
- - lib/ravensat/dimacs_encoder.rb
45
+ - lib/ravensat/dimacs.rb
46
+ - lib/ravensat/dimacs/dimacs_decoder.rb
47
+ - lib/ravensat/dimacs/dimacs_encoder.rb
48
+ - lib/ravensat/extension.rb
49
+ - lib/ravensat/extension/domain.rb
50
+ - lib/ravensat/extension/variable/boolean_variable.rb
51
+ - lib/ravensat/extension/variable/integer_variable.rb
52
+ - lib/ravensat/extension/variable/undefined_variable.rb
53
+ - lib/ravensat/extension/variable/variable.rb
42
54
  - lib/ravensat/ravenclaw.rb
43
55
  - lib/ravensat/solver.rb
44
56
  - lib/ravensat/version.rb