JijModeling 1.9.0 Release Note#

Random Generation for Instance Data#

Problem.generate_random_dataset and Problem.generate_random_instance#

  • The function Problem.generate_random_dataset generates a dictionary of input values to be fed into jm.Interpreter.

  • Use Problem.generate_random_instance to directly generating OMMX instance without manually initiating Interpreter.

Overview#

To use random generation feature, you should specify some information in Placeholder declaration. The following random-generation related attributes are added in this version:

  • dtype: The type of the element of an array (or scalar). Must be either jm.DataType.INTEGER or jm.DataType.FLOAT.

  • jagged: set to True to generate a jagged array. False by default.

  • shape: To generate an \(n\)-dimensional array or jagged array, you should specify the shape. This should be a tuple, with components dimension expression or None.

    • None means the size of the axis is indeterminate. For \(n\)-dimensional array, each axis has random but fixed size regardless of parent axes; for jagged arrays, the same axis can have different size for each parent axes.

If only dtype is specified, the placeholder is considered as a scalar value as in other cases.

Problem.generate_random_* takes the following keyword arguments:

  • seed (optional): a seed for random data generation. If omitted, it samples from environmental entropy.

  • options (optional): ranges for values and axis size for each placeholder.

  • default (optional): default ranges option for placeholders absent in options argument.

You will specify range objects in options and default. A concrete syntax is described in “Range Parameters and Range Syntax” section of API Reference. In a nutshell, range object consists of the following fields:

  • value: range of the values in placeholder. (default: uniformly samples from closed interval \([-1, 1]\))

  • size: range of the size of each axis in placeholder - ignored for scalar placeholders. (default: uniformly samples from closed interval \([1, 5] \cap \mathbb{N}\))

You can use functions from jijmodeling.range, jijmodeling.range.value, or jijmodeling.range.size modules and builtin range(N, M) function for size range.

Examples#

import jijmodeling as jm

v = jm.Placeholder("v", dtype=jm.DataType.INTEGER, shape=[None])
N = v.len_at(0, latex="n")
w = jm.Placeholder("w", dtype=jm.DataType.FLOAT, shape=[N])
C = jm.Placeholder("C", dtype=jm.DataType.FLOAT)
x = jm.BinaryVar("x", shape=[N])
i = jm.Element("i", belong_to=N)

problem = jm.Problem("MyProblem", sense=jm.ProblemSense.MAXIMIZE)
problem += jm.sum(i, v[i] * x[i])
problem += jm.Constraint("capacity", jm.sum(i, w[i] * x[i]) <= C)

problem
\[\begin{split}\begin{array}{cccc}\text{Problem:} & \text{MyProblem} & & \\& & \max \quad \displaystyle \sum_{i = 0}^{n - 1} v_{i} \cdot x_{i} & \\\text{{s.t.}} & & & \\ & \text{capacity} & \displaystyle \sum_{i = 0}^{n - 1} w_{i} \cdot x_{i} \leq C & \\\text{{where}} & & & \\& x & 1\text{-dim binary variable}\\\end{array}\end{split}\]
# Get the dictionary of generated data
data_set = problem.generate_random_dataset(
    seed=42,
    # Default Options.
    default={
        "value": jm.range.open_closed(5, 15),  # Samples values 5 < x <= 15
    },
    # Placheolder specific options.
    options={
        "v": {
            "value": range(1000, 10000),
            "size": range(2, 10),  # Each axis should be of length 2 <= n < 10
        }
    },
)
data_set
{'C': 13.010242975288078,
 'v': array([7120., 9322., 9926., 7927., 7473.]),
 'w': array([13.50008444, 12.61374381, 10.8334931 , 11.8245287 ,  7.90677762])}
config = {
    "seed": 42,
    # Default Options.
    "default": {
        "value": jm.range.open_closed(5, 15),  # Samples values 5 < x <= 15
    },
    # Placheolder specific options.
    "options": {
        "v": {
            "value": range(1000, 10000),
            "size": range(2, 10),  # Each axis should be of length 2 <= n < 10
        }
    },
}

# `generate_random_instance` is just a wrapper around generate_random_dataset and `Interpreter.eval_problem`.
instance = problem.generate_random_instance(**config)
assert instance == jm.Interpreter(data_set).eval_problem(problem)
# $n$-dimensionary array vs jagged array.

N = jm.Placeholder("N", dtype=jm.DataType.INTEGER)

# When `jagged` unspecified (or set to `False`), the generated data will be a $n$-dimensionary array.
ND = jm.Placeholder("ND", dtype=jm.DataType.FLOAT, shape=[N, None])

# When `jagged` is set to `True`, the generated data will be a jagged array.
JD = jm.Placeholder("JD", dtype=jm.DataType.FLOAT, shape=[N, None], jagged=True)

W = jm.BinaryVar("W", shape=[N])

# Both ND and JD has ndim 3, but `None` has different meaning.

# Create dummy problem

problem = jm.Problem("MyProblem2")
problem += jm.sum(i, ND[i, 0] * JD[i, 0] * W[i])
data_set = problem.generate_random_dataset(
    seed=42, default={"size": range(2, 5)}, options={"N": {"value": range(2, 7)}}
)
data_set["ND"]
array([[ 0.43851716,  0.70001689,  0.52274876,  0.16669862],
       [ 0.36490574, -0.41864448,  0.6020486 , -0.35717673],
       [ 0.42229989,  0.75553446,  0.23352309,  0.70278017],
       [ 0.41508094,  0.41565856, -0.81427987, -0.64195438],
       [-0.12760731,  0.20402936, -0.37202221, -0.02161649],
       [-0.20201642,  0.57824385,  0.27011351, -0.53733093]])
jd = data_set["JD"]
assert type(jd) is jm.JaggedArray
jd.dim
2
[[jd.get([i, j]) for j in range(jd.size_at([i]))] for i in range(jd.size_at([]))]
[[0.24546263509057664, 0.7263253599570634, 0.8895181708443229],
 [0.4939994897028319,
  0.5691758844274051,
  -0.9103425154924141,
  -0.0625037501158896],
 [0.40134384632666964, 0.6071495573420034, 0.6363935389166486],
 [-0.748701159594753,
  0.1299655840672893,
  0.1084981932704634,
  0.7144708283472148],
 [0.03940420021641855,
  -0.3650379384649608,
  -0.6156945696387756,
  0.2877456030741061],
 [-0.2834771222423055, 0.13862576961911643]]