class Gecode::IntEnum::IntEnumConstraintReceiver

IntEnumConstraintReceiver contains all constraints that can be placed on a IntEnumOperand.

Constraints are placed by calling Gecode::Operand#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 #distinct:

int_enum.must_be.distinct

Constrains int_enum2 to have the same elements as int_enum, but sorted in ascending order. Uses #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)

Public Instance Methods

channel(int_enum, options = {}) click to toggle source

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
# File lib/gecoder/interface/constraints/int_enum/channel.rb, line 54
def channel(int_enum, options = {})
  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 options.has_key? :reify
    raise ArgumentError, 'The channel constraints does not support the ' +
      'reification option.'
  end
  
  @params.update(Gecode::Util.decode_options(options))
  @params.update(:rhs => int_enum)
  @model.add_constraint Channel::ChannelConstraint.new(@model, @params)
end
distinct(options = {}) click to toggle source

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)
# File lib/gecoder/interface/constraints/int_enum/distinct.rb, line 24
def distinct(options = {})
  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 options[:reify].nil?
    raise ArgumentError, 'Reification is not supported by the distinct ' + 
      'constraint.'
  end

  if options.has_key? :offsets
    offsets = options.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.decode_options(options)))
end
equal(options = {}) click to toggle source

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
# File lib/gecoder/interface/constraints/int_enum/equality.rb, line 10
def equal(options = {})
  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 options[:reify].nil?
    raise ArgumentError, 'Reification is not supported by the equality ' + 
      'constraint.'
  end

  @model.add_constraint Equality::EqualityConstraint.new(@model, 
    @params.update(Gecode::Util.decode_options(options)))
end
in(tuples, options = {}) click to toggle source

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)
# File lib/gecoder/interface/constraints/int_enum/extensional.rb, line 15
def in(tuples, options = {})
  if @params[:negate]
    raise Gecode::MissingConstraintError, 'A negated tuple constraint is ' +
      'not implemented.'
  end
  unless options[: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.decode_options(options)))
end
match(regexp, options = {}) click to toggle source

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:

Gecode::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.

Gecode::Mixin#any

Used for specifying alternatives.

Additionally Gecode::Mixin#at_least_once and Gecode::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)
# File lib/gecoder/interface/constraints/int_enum/extensional.rb, line 141
def match(regexp, options = {})
  if @params[:negate]
    raise Gecode::MissingConstraintError, 'A negated regexp constraint ' +
      'is not implemented.'
  end
  unless options[: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.decode_options(options)
  @model.add_constraint Extensional::RegexpConstraint.new(@model, @params)
end
sorted(options = {}) click to toggle source

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)
# File lib/gecoder/interface/constraints/int_enum/sort.rb, line 42
def sorted(options = {})
  # Extract and check options.
  target = options.delete(:as)
  order = options.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 = !options[:reify].nil?
  @params.update(Gecode::Util.decode_options(options))
  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