# This file adds a small layer on top of the bindings. It alters the allocation # of variables so that a single array is allocated in each space which is then # used to store variable. The variables themselves are not directly returned, # rather they are represented as the index in that store, which allows the # variable to be retrieved back given a space. # # This layer should be moved to the C++ side instead when possible for better # performance. module GecodeRaw #:nodoc: all class Space # Creates the specified number of integer variables in the space with the # specified domain. Returns the indices with which they can then be # accessed using int_var. The domain can be given as a Range (trated # specially) or as another enum. def new_int_vars(domain, count = 1) int_var_store.new_vars(domain, count) end # Gets the int variable with the specified index, nil if none exists. def int_var(index) int_var_store[index] end # Creates the specified number of boolean variables in the space. Returns # the indices with which they can then be accessed using bool_var. def new_bool_vars(count = 1) bool_var_store.new_vars(count) end # Gets the bool variable with the specified index, nil if none exists. def bool_var(index) bool_var_store[index] end # Creates the specified number of set variables in the space with the # specified domain for greatest lower bound and least upper bound # (specified as either a range or enum). A range for the allowed # cardinality of the set can also be specified, if none is specified, or # nil is given, then the default range (anything) will be used. Returns # the indices with which they can then be accessed using set_var. def new_set_vars(*vars) set_var_store.new_vars(*vars) end # Gets the set variable with the specified index, nil if none exists. def set_var(index) set_var_store[index] end # Used by Gecode during BAB-search. def constrain(best_so_far_space) Gecode::Model.constrain(self, best_so_far_space) end # Refreshes the underlying stores used by the space. def refresh @int_var_store = nil @bool_var_store = nil @set_var_store = nil end private # Retrieves the store used for integer variables. Creates one if none # exists. def int_var_store # TODO: caching interferes with the variable creation during BAB-search, # find out why. #if @int_var_store.nil? @int_var_store = Gecode::Util::IntVarStore.new(self) #end return @int_var_store end # Retrieves the store used for boolean variables. Creates one if none # exists. def bool_var_store #if @bool_var_store.nil? @bool_var_store = Gecode::Util::BoolVarStore.new(self) #end return @bool_var_store end # Retrieves the store used for set variables. Creates one if none exists. def set_var_store #if @set_var_store.nil? @set_var_store = Gecode::Util::SetVarStore.new(self) #end return @set_var_store end end end module Gecode # Various utility (mainly used to change the behavior of the raw bindings). module Util #:nodoc: all # Provides common methods to the variable stores. The stores must provide # @next_index, @var_array, @size, ARRAY_IDENTIFIER and #new_storage_array . module VarStoreMethods # Returns the int var with the specified index, nil if none exists. def [](index) if index < 0 or index >= @next_index return nil end return @var_array.at(index) end private # Grows the store to the new size. def grow(new_size) @var_array.enlargeArray(@space, new_size - @size) @size = new_size end end # A store in which int variables are created and stored. class IntVarStore # Design note: The store used to double its size when it needed to grow # leaving unallocated slots (in rev 16). This was changed to only growing # the amount of space needed because the additional information about which # slot is the next unallocated one could not be encoded without changes to # the bindings (and without that information we can not deduce the store # from the new copy of space). So for additional performance the bindings # should grow the array more than needed (when this is moved to the bindings). include VarStoreMethods private # A string that identifies the array used by the store. ARRAY_IDENTIFIER = 'int_array' public # Creates a store for the specified space with the specified capacit. def initialize(space) @space = space @var_array = space.int_var_array(ARRAY_IDENTIFIER) if @var_array.nil? # Create a new one. @var_array = new_storage_array(0) end @size = @var_array.size @next_index = @size end # Creates the specified number of integer variables in the space with the # specified domain. Returns the indices with which they can then be # accessed using int_var. The domain can be given as a Range (trated # specially) or as another enum. def new_vars(domain, count = 1) grow(@next_index + count) # See the design note for more information. count.times do |i| if domain.kind_of? Range domain_params = [domain.first, domain.last] elsif domain.kind_of? Enumerable arr = domain.to_a domain_params = [Gecode::Raw::IntSet.new(arr, arr.size)] else raise TypeError, "Expected Enumerable, got #{domain.class}." end @var_array[@next_index] = Gecode::Raw::IntVar.new(@space, *domain_params) @next_index += 1 end ((@next_index - count)...@next_index).to_a end # Returns the int var with the specified index, nil if none exists. def [](index) if index < 0 or index >= @next_index return nil end return @var_array.at(index) end private # Creates a new storage array for int variables. def new_storage_array(new_size) arr = Gecode::Raw::IntVarArray.new(@space, new_size) @space.own(arr, ARRAY_IDENTIFIER) return arr end end # A store in which int variables are created and stored. class BoolVarStore # TODO: can we refactor this better seeing as IntVarStore and BoolVarStore # are similar? include VarStoreMethods private # A string that identifies the array used by the store. ARRAY_IDENTIFIER = 'bool_array' public # Creates a store for the specified space with the specified capacit. def initialize(space) @space = space @var_array = space.bool_var_array(ARRAY_IDENTIFIER) if @var_array.nil? # Create a new one. @var_array = new_storage_array(0) end @size = @var_array.size @next_index = @size end # Creates the specified number of new bool variables. Returns the indices # of the created variables as an array. def new_vars(count = 1) grow(@next_index + count) # See the design note for more information. count.times do |i| @var_array[@next_index] = Gecode::Raw::BoolVar.new(@space, 0, 1) @next_index += 1 end ((@next_index - count)...@next_index).to_a end private # Creates a new storage array for bool variables. def new_storage_array(new_size) arr = Gecode::Raw::BoolVarArray.new(@space, new_size) @space.own(arr, ARRAY_IDENTIFIER) return arr end end # A store in which int variables are created and stored. class SetVarStore include VarStoreMethods private # A string that identifies the array used by the store. ARRAY_IDENTIFIER = 'set_array' public # Creates a store for the specified space with the specified capacit. def initialize(space) @space = space @var_array = space.set_var_array(ARRAY_IDENTIFIER) if @var_array.nil? # Create a new one. @var_array = new_storage_array(0) end @size = @var_array.size @next_index = @size end # Creates the specified number of set variables in the space with the # specified domain for greatest lower bound and least upper bound # (specified as either a range or enum). A range for the allowed # cardinality of the set can also be specified, if none is specified, or # nil is given, then the default range (anything) will be used. Returns # the indices with which they can then be accessed using set_var. def new_vars(glb_domain, lub_domain, cardinality_range = nil, count = 1) grow(@next_index + count) # See the design note for more information. if cardinality_range.nil? cardinality_range = 0..Gecode::Raw::SetLimits::CARD end params = [@space] params << domain_to_args(glb_domain) params << domain_to_args(lub_domain) params << cardinality_range.first << cardinality_range.last count.times do |i| @var_array[@next_index] = Gecode::Raw::SetVar.new(*params.flatten) @next_index += 1 end ((@next_index - count)...@next_index).to_a end private # Transforms a lub or glb domain given as a range or enumeration into one # or more parameters that describe the domain to Gecode::Raw::SetVar . def domain_to_args(domain) if domain.kind_of? Range return domain.first, domain.last else elements = domain.to_a return Gecode::Raw::IntSet.new(domain, domain.size) end end # Creates a new storage array for bool variables. def new_storage_array(new_size) arr = Gecode::Raw::SetVarArray.new(@space, new_size) @space.own(arr, ARRAY_IDENTIFIER) return arr end end end end