Module: Quicopt

Defined in:
lib/quicopt/wire.rb,
lib/quicopt/model.rb,
lib/quicopt/client.rb,
lib/quicopt/version.rb,
lib/quicopt/indicators.rb,
lib/quicopt/structured.rb,
lib/quicopt.rb

Overview

Quicopt — a Ruby client for the Quicopt optimization service.

Write a model in the native DSL, encode it, and let the service solve it:

require "quicopt"

m = Quicopt::Model.new
x = m.int_var(0, 4, "x")
y = m.bin_var("y")
z = m.num_var(0, 10, "z")
m.maximize(3 * x + 2 * y + z)
m.add(x + y <= 5)

result = Quicopt::Client.new.solve(m)
puts result.display

Three pieces, nothing more: Model authors, Wire encodes, Client ships.

Defined Under Namespace

Modules: Conditions, Format, Operators, Positions, Wire Classes: Arrangement, Block, Client, Condition, Constraint, Error, Expression, Extremum, Grouping, Job, LookupTable, Model, PartRead, Partition, Permutation, Placement, QuicoptError, Result, TableEntry, UnsupportedExpression, Variable

Constant Summary collapse

DEFAULT_BASE_URL =

The public Quicopt free-tier endpoint a Client targets when no base_url is given. Mirrors the Python and Julia clients' default, so every client reaches the same server out of the box.

"https://try.quicoptapi.pgi.fz-juelich.de"
SOURCE_LANGUAGE =

The modelling library a model was written in, sent as the source_language call tag. It names the library, not the programming language — the service also sees jump, pyomo, pulp and mathopt — so this gem's own DSL carries its own name here.

"quicopt-ruby"
WAIT_MARGIN =

How long a request may take beyond the solve itself — waiting in the queue, reading the model, writing the answer — before the client stops waiting on a solve whose time limit it set.

60.0
KEY_PATH_ENV =

Environment variable overriding where the free key is cached. Point it at durable storage in an environment whose home directory does not survive the run (CI, containers), where the default location is wiped between sessions and every run would otherwise mint a fresh key.

"QUICOPT_KEY_PATH"
VERSION =

The gem version, tracking the client — not the wire schema, which is versioned separately by its protobuf package (quicopt.modeler.v1).

"0.3.0"
Program =

The structured message a model becomes, re-exported so callers can inspect the bytes or route them themselves rather than going through Client.

Wire::PB::Program

Class Method Summary collapse

Class Method Details

.decode(bytes) ⇒ Program

Parse bytes back into a Program.

Parameters:

  • bytes (String)

Returns:



47
48
49
# File 'lib/quicopt.rb', line 47

def decode(bytes)
  Wire.decode(bytes)
end

.encode(model) ⇒ String

Encode a model to bytes.

Parameters:

Returns:

  • (String) —

    binary protobuf bytes



39
40
41
# File 'lib/quicopt.rb', line 39

def encode(model)
  Wire.encode(model)
end

.holds(comparison, tol: 0.0) ⇒ Expression

1 where the comparison holds and 0 where it does not, as an expression the service evaluates at the values it chooses. Its main use is a cost or a discount that applies once a total crosses a level: a warehouse's opening fee, due once it ships anything, is

opening_fee * Quicopt.holds(shipped >= 1)

and a discount of 30 from 60 units on is -30 * Quicopt.holds(shipped >= 60).

Parameters:

  • comparison (Constraint) —

    built with <=, >=, <, >, == or != between expressions, such as shipped >= 60

  • tol (Numeric) (defaults to: 0.0) —

    how far a comparison may miss and still count as holding: a >= b holds from a = b − tol on, a > b from a > b + tol. Non-negative.

Returns:

  • (Expression) —

    a 0/1 expression; a number when the comparison involves no decision

Raises:

  • (TypeError) —

    if comparison is not a comparison — often one Ruby already reduced to true or false because both sides were numbers

  • (ArgumentError) —

    if tol is negative, or the two sides belong to different models



48
49
50
51
52
53
54
55
# File 'lib/quicopt/indicators.rb', line 48

def holds(comparison, tol: 0.0)
  unless comparison.is_a?(Constraint)
    raise TypeError, "holds takes a comparison such as `x >= 3`, got #{comparison.inspect}"
  end
  raise ArgumentError, "tol is a non-negative number, got #{tol.inspect}" unless tol.is_a?(Numeric) && tol >= 0

  Conditions.holds(comparison.expr, comparison.sense, tol.to_f)
end

.max(*args) ⇒ Expression

The largest of the given expressions, as an expression the service evaluates.

Parameters:

Returns:

  • (Expression) —

    a number when every argument is one

Raises:

  • (ArgumentError) —

    on fewer than two arguments, or arguments from different models



76
77
78
# File 'lib/quicopt/indicators.rb', line 76

def max(*args)
  Extremum.build(:max, args)
end

.min(*args) ⇒ Expression

The smallest of the given expressions, as an expression the service evaluates. fee * Quicopt.min(1, a.active + b.active) is a fee that is due once, however many of a and b are in use.

Parameters:

Returns:

  • (Expression) —

    a number when every argument is one

Raises:

  • (ArgumentError) —

    on fewer than two arguments, or arguments from different models



65
66
67
# File 'lib/quicopt/indicators.rb', line 65

def min(*args)
  Extremum.build(:min, args)
end