Class: Gecode::IntEnum::IntEnumConstraintReceiver
- Inherits:
-
ConstraintReceiver
- Object
- ConstraintReceiver
- Gecode::IntEnum::IntEnumConstraintReceiver
- Defined in:
- lib/gecoder/interface/constraints/int_enum_constraints.rb,
lib/gecoder/interface/constraints/int_enum/sort.rb,
lib/gecoder/interface/constraints/int_enum/channel.rb,
lib/gecoder/interface/constraints/int_enum/distinct.rb,
lib/gecoder/interface/constraints/int_enum/equality.rb,
lib/gecoder/interface/constraints/int_enum/extensional.rb
Overview
IntEnumConstraintReceiver contains all constraints that can be placed on a IntEnumOperand.
Constraints are placed by calling IntEnumOperand#must (or any other of the variations defined in Operand), which produces a IntEnumConstraintReceiver from which the desired constraint can be used.
Some constraint accepts a number of options. See ConstraintReceiver for more information.
Examples
Constrains the integer operands in int_enum
to take on different values by using IntEnumConstraintReceiver#distinct:
int_enum.must_be.distinct
Constrains int_enum2
to have the same elements as int_enum
, but sorted in ascending order. Uses IntEnumConstraintReceiver#sorted:
int_enum.must_be.sorted(:as => int_enum2)
The same as above, but specifying that strength :domain should be used and that the constraint should be reified with bool_operand
:
int_enum.must_be.sorted(:as => int_enum2, :strength => :domain, :reify => bool_operand)
Instance Method Summary collapse
-
#channel(int_enum, options = {}) ⇒ Object
Constrains this enumeration to “channel”
int_enum
. -
#distinct(options = {}) ⇒ Object
Constrains all integer operands in the enumeration to be distinct (different).
-
#equal(options = {}) ⇒ Object
Constrains all operands in the enumeration to be equal.
-
#in(tuples, options = {}) ⇒ Object
Constrains all the operands in this enumeration to be equal to one of the specified tuples.
-
#initialize(model, params) ⇒ IntEnumConstraintReceiver
constructor
Raises TypeError unless the left hand side is an int enum operand.
-
#match(regexp, options = {}) ⇒ Object
Constrains the sequence of operands in this enumeration to match a specified regexp in the integer domain.
-
#sorted(options = {}) ⇒ Object
Constrains the elements in this enumeration to be sorted in ascending order.
Constructor Details
#initialize(model, params) ⇒ IntEnumConstraintReceiver
Raises TypeError unless the left hand side is an int enum operand.
78 79 80 81 82 83 84 |
# File 'lib/gecoder/interface/constraints/int_enum_constraints.rb', line 78 def initialize(model, params) #:nodoc: super unless params[:lhs].respond_to? :to_int_enum raise TypeError, 'Must have int enum operand as left hand side.' end end |
Instance Method Details
#channel(int_enum, options = {}) ⇒ Object
Constrains this enumeration to “channel” int_enum
. Channel constraints are used to give access to multiple viewpoints when modelling.
The channel constraint can be thought of as constraining the arrays to be each other’s inverses. I.e. if the i:th value in the first enumeration is j, then the j:th value in the second enumeration is constrained to be i.
Neither reification nor negation is supported.
Examples
Lets say that we’re modelling a sequence of numbers that must be distinct and that we want access to the following two view simultaneously.
First view
The sequence is modelled as an array of integer variables where the first variable holds the value of the first position in the sequence, the second the value of the second position and so on.
# n variables with values from 0 to n-1.
elements = int_var_array(n, 0...n)
elements.must_be.distinct
That way elements
will contain the actual sequence when the problem has been solved.
Second view
The sequence is modelled as the positions of each value in 0..(n-1) in the sequence. That way the first variable would hold the positions of 0 in the sequence, the second variable would hold the positions of 1 in the sequence and so on.
positions = int_var_array(n, 0...n)
positions.must_be.distinct
Connecting the views
In essence the relationship between the two arrays elements
and positions
is that
elements.map{ |e| e.val }[i] == positions.map{ |p| p.val }.index(i)
for all i in 0..(n-1). This relationship is enforced by the channel constraint as follows.
elements.must.channel positions
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/gecoder/interface/constraints/int_enum/channel.rb', line 54 def channel(int_enum, = {}) if @params[:negate] raise Gecode::MissingConstraintError, 'A negated channel constraint ' + 'is not implemented.' end unless int_enum.respond_to? :to_int_enum raise TypeError, "Expected int enum, got #{int_enum.class}." end if .has_key? :reify raise ArgumentError, 'The channel constraints does not support the ' + 'reification option.' end @params.update(Gecode::Util.()) @params.update(:rhs => int_enum) @model.add_constraint Channel::ChannelConstraint.new(@model, @params) end |
#distinct(options = {}) ⇒ Object
Constrains all integer operands in the enumeration to be distinct (different). The constraint can also be used with constant offsets, so that the operands, with specified offsets added, must be distinct.
The constraint does not support negation nor reification.
Examples
# Constrains all operands in +int_enum+ to be assigned different
# values.
int_enum.must_be.distinct
# The same as above, but also selects that the strength +domain+ should
# be used.
int_enum.must_be.distinct(:strength => :domain)
# Uses the offset to constrain that no number may be the previous number
# incremented by one.
numbers = int_var_array(8, 0..9)
numbers.must_be.distinct(:offsets => (1..numbers.size).to_a.reverse)
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/gecoder/interface/constraints/int_enum/distinct.rb', line 24 def distinct( = {}) if @params[:negate] # The best we could implement it as from here would be a bunch of # reified pairwise equality constraints. raise Gecode::MissingConstraintError, 'A negated distinct is not ' + 'implemented.' end unless [:reify].nil? raise ArgumentError, 'Reification is not supported by the distinct ' + 'constraint.' end if .has_key? :offsets offsets = .delete(:offsets) unless offsets.kind_of? Enumerable raise TypeError, 'Expected Enumerable as offsets, got ' + "#{offsets.class}." end @params[:offsets] = offsets end @model.add_constraint Distinct::DistinctConstraint.new(@model, @params.update(Gecode::Util.())) end |
#equal(options = {}) ⇒ Object
Constrains all operands in the enumeration to be equal. Neither negation nor reification is supported.
Examples
# Constrains all operands in +int_enum+ to be equal.
int_enum.must_be.equal
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/gecoder/interface/constraints/int_enum/equality.rb', line 10 def equal( = {}) if @params[:negate] # The best we could implement it as from here would be a bunch of # reified pairwise inequality constraints. raise Gecode::MissingConstraintError, 'A negated equality is not ' + 'implemented.' end unless [:reify].nil? raise ArgumentError, 'Reification is not supported by the equality ' + 'constraint.' end @model.add_constraint Equality::EqualityConstraint.new(@model, @params.update(Gecode::Util.())) end |
#in(tuples, options = {}) ⇒ Object
Constrains all the operands in this enumeration to be equal to one of the specified tuples. Neither negation nor reification is supported.
Examples
# Constrains the two integer operands in +numbers+ to either have
# values 1 and 7, or values 47 and 11.
numbers.must_be.in [[1,7], [47,11]]
# The same as above, but preferring speed over low memory usage.
numbers.must_be.in([[1,7], [47,11]], :kind => :speed)
15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/gecoder/interface/constraints/int_enum/extensional.rb', line 15 def in(tuples, = {}) if @params[:negate] raise Gecode::MissingConstraintError, 'A negated tuple constraint is ' + 'not implemented.' end unless [:reify].nil? raise ArgumentError, 'Reification is not supported by the tuple ' + 'constraint.' end util = Gecode::Util # Check that the tuples are correct. expected_size = @params[:lhs].size util::Extensional.perform_tuple_checks(tuples, expected_size) do |tuple| unless tuple.all?{ |x| x.kind_of? Fixnum } raise TypeError, 'All tuples must contain Fixnum.' end end @params[:tuples] = tuples @model.add_constraint Extensional::TupleConstraint.new(@model, @params.update(util.())) end |
#match(regexp, options = {}) ⇒ Object
Constrains the sequence of operands in this enumeration to match a specified regexp in the integer domain. Neither negation nor reification is supported.
Regexp syntax
The regular expressions are specified using arrays, integers and a few methods provided by Mixin. Arrays are used to group the integers in sequences that must be matched. The following array describes a regular expression matching a 1 followed by a 7.
[1, 7]
Arrays can be nested or left out when not needed. I.e. the above is semantically equal to
[[[1], 7]]
A couple of methods provided by Mixin are used to express patterns beyond mere sequences:
- Mixin#repeat
-
Used for specifying patterns that include patterns that may be repeated a given number of times. The number of times to repeat a pattern can be specified using a lower and upper bound, but the bounds can be omitted to for instance allow an expression to be repeated any number of times.
- Mixin#any
-
Used for specifying alternatives.
Additionally Mixin#at_least_once and Mixin#at_most_once are provided as convenience methods.
Examples
# Matches 1 followed by any number of 2s.
[1, repeat(2)]
# Semantically the same as above. It just has a bunch of
# needless brackets thrown in.
[[1], [repeat([2])]]
# Matches 1 followed by [a 2 followed by a 3] at least two times.
# Matches e.g. 1, 2, 3, 2, 3
[1, repeat([2, 3], 2)]
# Matches between one and two [2 followed by [at least three 1]]
# followed by between three and four 3. Matches e.g.
# 2, 1, 1, 1, 2, 1, 1, 1, 3, 3, 3
[repeat([2, repeat(1, 3], 1, 2), repeat(3, 3, 4)]
# Matches [1, 2 or 3] followed by 4. Matches e.g. 2, 4
[any(1, 2, 3), 4]
# Matches 0 followed by [[1 followed by 2] or [3 followed by 5]].
# Matches e.g. 0, 1, 2 as well as 0, 3, 5
[0, any([1, 2], [3, 5])]
# Matches 0 followed by [[[1 followed by 7] at least two times]
# or [[8, 9], at most two times]. Matches e.g.
# 0, 1, 7, 1, 7, 1, 7 as well as 0, 8, 9
[0, any(repeat([1, 7], 2), repeat([8, 9], 0, 2)]
# Matches 0 followed by at least one 1.
[0, at_least_once(1)]
# Exactly the same as the above.
[0, repeat(1, 1)]
# Matches 0 followed by at least one [[1 followed by 7] or [3
# followed by 2]]. Matches e.g. 0, 1, 7, 3, 2, 1, 7
[0, at_least_once(any([1, 7], [3, 2]]
# Matches 0 followed by at either [[1 followed by 7] at least once]
# or [[3 followed by 2] at least once]. Matches e.g.
# 0, 1, 7, 1, 7 but does _not_ match 0, 1, 7, 3, 2, 1, 7
[0, any(at_least_once([1, 7]), at_least_once([3, 2])]
# Matches 0, followed by at most one 1. Matches 0 as well as
# 0, 1
[0, at_most_once(1)]
# Exactly the same as the above.
[0, repeat(1, 0, 1)]
Examples
# Constrains the two integer operands in +numbers+ to have
# values 1 and 7.
numbers.must.match [1, 7]
# Constrains the integer operands in +numbers+ to contain the
# value 47 followed by 11, with all other values set to -1.
numbers.must.match [repeat(-1), 47, 11, repeat(-1)]
# Constrains exactly three of the integer operands in +numbers+ to
# contain 47 or 11, each followed by at least two
# operands set to -1. All other operands are constrained to
# equal -1.
numbers.must.match repeat([repeat(-1), any(11, 47),
repeat(-1, 2)], 3, 3)
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/gecoder/interface/constraints/int_enum/extensional.rb', line 141 def match(regexp, = {}) if @params[:negate] raise Gecode::MissingConstraintError, 'A negated regexp constraint ' + 'is not implemented.' end unless [:reify].nil? raise ArgumentError, 'Reification is not supported by the regexp ' + 'constraint.' end @params[:regexp] = Gecode::Util::Extensional.parse_regexp regexp @params.update Gecode::Util.() @model.add_constraint Extensional::RegexpConstraint.new(@model, @params) end |
#sorted(options = {}) ⇒ Object
Constrains the elements in this enumeration to be sorted in ascending order. The following options can be given in addition to the common constraint options:
- :as
-
Defines a target (must be an IntEnum) that will hold the sorted version of the original enumerable. The original enumerable will not be affected (i.e. will not necessarily be sorted)
- :order
-
Sets an IntEnum that should be used to store the order of the original enum’s operands when sorted. The original enumerable will not be affected (i.e. will not necessarily be sorted)
If neither of those options are specified then the original enumerable will be constrained to be sorted (otherwise not). Sort constraints with options do not allow negation.
Examples
# Constrain +numbers+ to be sorted.
numbers.must_be.sorted
# Constrain +numbers+ to not be sorted.
numbers.must_not_be.sorted
# Constrain +sorted_numbers+ to be a sorted version of +numbers+.
numbers.must_be.sorted(:as => sorted_numbers)
# Constrain +order+ to be the order in which +numbers+ has to be
# ordered to be sorted.
numbers.must_be.sorted(:order => order)
# Constrain +sorted_numbers+ to be +numbers+ sorted in the order
# described by the IntEnum +order+.
numbers.must_be.sorted(:as => sorted_numbers, :order => order)
# Constrains +numbers+ to be sorted, reifying with the boolean
# operand +is_sorted+, while selecting +domain+ as strength.
numbers.must_be.sorted(:reify => :is_sorted, :strength => :domain)
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/gecoder/interface/constraints/int_enum/sort.rb', line 42 def sorted( = {}) # Extract and check options. target = .delete(:as) order = .delete(:order) unless target.nil? or target.respond_to? :to_int_enum raise TypeError, 'Expected int var enum as :as, got ' + "#{target.class}." end unless order.nil? or order.respond_to? :to_int_enum raise TypeError, 'Expected int var enum as :order, got ' + "#{order.class}." end # Extract standard options and convert to constraint. reified = ![:reify].nil? @params.update(Gecode::Util.()) if target.nil? and order.nil? @model.add_constraint Sort::SortConstraint.new(@model, @params) else # Do not allow negation. if @params[:negate] raise Gecode::MissingConstraintError, 'A negated sort with options ' + 'is not implemented.' end if reified raise ArgumentError, 'Reification is not supported by the sorted ' + 'constraint.' end @params.update(:target => target, :order => order) @model.add_constraint Sort::SortConstraintWithOptions.new(@model, @params) end end |