Module: Quicopt::Operators

Included in:
Expression, Variable
Defined in:
lib/quicopt/model.rb

Overview

The operator surface shared by Variable and Expression.

Every operator is written against to_expr, so a variable and an expression behave identically. coerce makes a numeric on the left work too, which is what lets 3 * x parse: Ruby hands the multiplication back to us rather than failing in Integer#*.

Instance Method Summary collapse

Instance Method Details

#!=(other) ⇒ Constraint, Boolean

A "not equal to" comparison, for Quicopt.holds only: Model#add refuses it as a row. Defined so that Ruby does not derive != from == and silently answer false.

Returns:



168
169
170
171
172
# File 'lib/quicopt/model.rb', line 168

def !=(other)
  return !equal?(other) unless Expression.castable?(other)

  Constraint.build(self, other, :ne)
end

#*(other) ⇒ Expression

Returns self * other.

Returns:

Raises:



78
79
80
# File 'lib/quicopt/model.rb', line 78

def *(other)
  to_expr.times(Expression.cast(other))
end

#**(exponent) ⇒ Expression

Integer powers 0, 1 and 2 only — a Quicopt model is linear or quadratic.

Returns:

Raises:



101
102
103
104
105
106
107
108
109
110
111
# File 'lib/quicopt/model.rb', line 101

def **(exponent)
  case exponent
  when 0 then Expression.new(constant: 1.0)
  when 1 then to_expr
  when 2 then to_expr.times(to_expr)
  else
    raise UnsupportedExpression,
          "exponent #{exponent.inspect} is not supported: a Quicopt model is " \
          "linear or quadratic, so the exponent must be 0, 1 or 2"
  end
end

#+(other) ⇒ Expression

Returns self + other.

Returns:



67
68
69
# File 'lib/quicopt/model.rb', line 67

def +(other)
  to_expr.plus(Expression.cast(other))
end

#+@ ⇒ Expression

Returns self, unchanged.

Returns:



119
120
121
# File 'lib/quicopt/model.rb', line 119

def +@
  to_expr
end

#-(other) ⇒ Expression

Returns self - other.

Returns:



72
73
74
# File 'lib/quicopt/model.rb', line 72

def -(other)
  to_expr.plus(Expression.cast(other).scale(-1.0))
end

#-@ ⇒ Expression

Returns the negation of self.

Returns:



114
115
116
# File 'lib/quicopt/model.rb', line 114

def -@
  to_expr.scale(-1.0)
end

#/(divisor) ⇒ Expression

Division by a numeric constant only — dividing by an expression is not a polynomial, and a Quicopt model is a polynomial.

Returns:

Raises:



87
88
89
90
91
92
93
94
95
# File 'lib/quicopt/model.rb', line 87

def /(divisor)
  unless divisor.is_a?(Numeric)
    raise UnsupportedExpression,
          "cannot divide by #{divisor.class}: only division by a numeric constant is supported"
  end
  raise ZeroDivisionError, "divided by 0" if divisor.zero?

  to_expr.scale(1.0 / divisor)
end

#<(other) ⇒ Constraint

A strict comparison, for Quicopt.holds only: no constraint row can be strict, so Model#add refuses it.

Returns:



137
138
139
# File 'lib/quicopt/model.rb', line 137

def <(other)
  Constraint.build(self, other, :lt)
end

#<=(other) ⇒ Constraint

Returns the row self <= other.

Returns:



124
125
126
# File 'lib/quicopt/model.rb', line 124

def <=(other)
  Constraint.build(self, other, :le)
end

#==(other) ⇒ Constraint, Boolean

Builds an equality row, not a boolean — x == 5 is a constraint.

A non-numeric, non-expression operand falls back to identity, so an incidental comparison (x == nil, x == "x") still answers false instead of raising. Hash and Set membership are unaffected either way: they key off hash/eql?, which Variable defines separately.

Returns:



157
158
159
160
161
# File 'lib/quicopt/model.rb', line 157

def ==(other)
  return equal?(other) unless Expression.castable?(other)

  Constraint.build(self, other, :eq)
end

#>(other) ⇒ Constraint

A strict comparison, for Quicopt.holds only (see <).

Returns:



144
145
146
# File 'lib/quicopt/model.rb', line 144

def >(other)
  Constraint.build(self, other, :gt)
end

#>=(other) ⇒ Constraint

Returns the row self >= other.

Returns:



129
130
131
# File 'lib/quicopt/model.rb', line 129

def >=(other)
  Constraint.build(self, other, :ge)
end

#coerce(other) ⇒ Array(Expression, Expression)

Lets Ruby fold a numeric on the left into an expression, so 3 * x and 10 - x dispatch here instead of failing inside Integer/Float.

Parameters:

  • other (Numeric)

Returns:



180
181
182
# File 'lib/quicopt/model.rb', line 180

def coerce(other)
  [Expression.cast(other), to_expr]
end