Problem/Solver parameters

Instantiation

Instantiate your problem with proper arguments

>>> from hyperpack import HyperPack
>>> problem = hyperpack.HyperPack(
>>>     containers=containers, # problem parameter
>>>     items=items, # problem parameter
>>>     settings=settings # solver/figure parameters
>>> )

According to the arguments given, the corresponding problem will be instantiated, ready to be solved with provided guidelines.


Parameters

containers

In figure 1 the container’s (bin’s) coordinate system can be seen.

  • W is the width of the container (x coordinate).

  • L is the length of the container (y coordinate).

container

This is the containers valid structure:

containers = {
    "container-0-id": {
        "W": int, # > 0 container's width
        "L": int # > 0 container's length
    },
    "container-1-id": {
        "W": int, # > 0 container's width
        "L": int # > 0 container's length
    },
    # ... rest of the containers
    # minimum 1 container must be provided
}
A ContainersError will be raised at instantiation/validation in case:
  • The containers parameter isn’t a dictionary with the above structure specified.

  • There isn’t at least one container in containers.

  • A container’s id isn’t a string.

A DimensionsError will be raised at instantiation/validation in case:
  • "W" or "L" values aren’t positive integers.

  • "W" and/or "L" are not provided for a given container.

  • another key beside "W" or "L" is given for a container.

  • a deletion of a dimension’s key (“W” or “L”) is attempted.

The problem’s containers after instantiation can be found in the problem.containers instance attribute:

>>> problem.containers
{"container-0-id": {"W": 100, "L": 200}}

Note

Each assignement, key value change or deletion after instantiation of the containers attribute, will trigger validation, and also reset the solution attribute of the instance.

This reset affects the figure creation and the solution logging, since they both use the solution attribute.

In that case if any of the above requirements aren’t met, a ContainersError or DimensionsError will be raised.

>>> # after instantiation
>>> problem.solve() # updates self.solution with solution
>>> problem.containers = new_containers
>>> # validation happened without error
>>> assert problem.solution == {}
>>>
>>>
>>> problem.solve() # updates self.solution with solution
>>> problem.containers["container-id-0"] = {"W":100, "L":200}
>>> assert problem.solution == {}
>>>
>>>
>>> problem.solve() # updates self.solution with solution
>>> problem.containers["container-id-0"]["W"] = 200
>>> assert problem.solution == {}
>>>
>>>
>>> # now an invalid containers assignment
>>> problem.containers = {"container-id-0": {"W":-100, "L":100}}
Traceback (most recent call last):
    ...
hyperpack.exceptions.DimensionsError: Width and Length must be positine numbers
>>>
>>>
>>> # now an invalid container key assignment
>>> problem.containers["container-id-0"] = {"W": 100
Traceback (most recent call last):
    ...
hyperpack.exceptions.DimensionsError: dimensions must (only) contain Width and Length keys
>>>
>>>
>>> # last an invalid Width assignment
>>> problem.containers["container-id-0"]["W"] = 100.1
Traceback (most recent call last):
    ...
hyperpack.exceptions.DimensionsError: Width and Length must be positine numbers

Must be noted, that containers is not of type dict, but an instance of the hyperpack.structures.Containers class, that inherits from hyperpack.structures.AbstractStructureSet. That is a customized dictionary with predefined behaviour. Also each container’s (and item’s as we ‘ll see later) dimensions are an instance of hyperpack.structures.Dimensions class.

>>> type(problem.containers)
<class 'hyperpack.structures.Containers'>
>>> type(problem.containers["container-id-0"])
<class 'hyperpack.structures.Dimensions'>

Also a string representation with str() (or implicitly with print()) exists:

>>> print(problem.containers) # or str(problem.containers)
Containers
  - id: container-id-0
    width: 100
    length: 100

  - id: container-id-1
    width: 200
    length: 200

items

The image below depicts an item’s dimensions.

dimensions

This is the items valid structure:

items = {
    "item_0_id": {
        "w": int, # > 0 item's width
        "l": int, # > 0 item's length
    },
    "item_1_id": {
        "w": int, # > 0 item's width
        "l": int, # > 0 item's length
    },
    # ... rest of the items
    # minimum 1 item must be provided
}
An ItemsError will be raised at instantiation/validation in case:
  • The items parameter isn’t a dictionary with the above specified structure.

  • There isn’t at least one item in items.

  • An item’s id isn’t a string.

A DimensionsError will be raised at instantiation/validation in case:
  • "w" or "l" values aren’t positive integers

  • "w" and/or "l" keys are missing from a given item.

  • another key beside "w" or "l" is given for an item.

  • a deletion of a dimension’s key (“w” or “l”) is attempted.

The problem’s items after instantiation can be found in the items instance attribute:

>>> problem.items
{"item-0-id": {"w": 10, "l": 20}}

Note

The assignement and value update operations of the items attribute follow the same behaviour as the containers attribute. Reset of the solution attribute is guaranteed along with structure and value validation.

Must be noted, that items is not of type dict, but an instance of the hyperpack.structures.Items class, that inherits from hyperpack.structures.AbstractStructureSet. That is a customized dictionary with predefined behaviour. Also each item’s dimensions are an instance of hyperpack.structures.Dimensions class.

>>> type(problem.items)
<class 'hyperpack.structures.Items'>
>>> type(problem.items["item-0-id"])
<class 'hyperpack.structures.Dimensions'>

Also a string representation with str() exists:

>>> print(problem.items) # or str(problem.items)
Items
  - id: item-id-0
    width: 1
    length: 2

  - id: item-id-1
    width: 2
    length: 3

settings

This is the settings valid structure:

settings = {
    "workers_num": int,
        # (> 0) the number of processor threads for hypersearch
        # defaults to 1 if omitted

    "max_time_in_seconds": int,
        # (> 0) the max time for solving
        # defaults to 60

    "rotation": bool ,
    # if item rotation is enabled
    # defaults to True

    # figure key can be omitted
    "figure": {

        "export": {
            "type": str,
                # "image" or "html"
            "format": str,
                # "pdf", "png", "jpeg", "webp", "svg"
                # unecessary if html exportation
            "path": "abs/path/to/figure/directory",
                # must be valid system absolute path
            "file_name": str,
                # "PlotlyGraph" default value
                # if file_name given, it must match the
                # r"^[a-zA-Z0-9_-]{1,45}$" regex
            "width": int,
                # (> 1) pixels number (default 1700px)
            "height": int,
                # (> 1) pixels number (default 1700px)
        },

        "show": bool,
            # if the figure will be shown when create_figure is called
            # if omitted, falls back to create_figure's
            # 'show' kwarg (default False)
    }
}

The problem’s settings after instantiation can be found in the settings instance attribute:

>>> problem.settings
{"workers_num":2, "rotation": False}

If settings parameter is omitted, then settings default to the empty dictionary, but a default value is assigned for workers_num, max_time_in_seconds and rotation attributes (see below).

>>> problem = HyperPack(items=items, containers=containers)
>>> problem.settings
{}

The workers_num, max_time_in_seconds and rotation are private attributes and should be changed/set through the settings attribute.

There are two ways of changing already instantiated settings:

A. New assignment of settings:

>>> problem.settings = new_settings
>>>  # new settings have been validated and applied

B. Changing a key/value of settings. In this case no validation takes place and no settings will be applied automatically. User must do explicit validation like this:

>>> problem.settings["workers_num"] = 3 # no validation happened
>>> # workers_num setting hasn't been changed
>>> problem.validate_settings() # manually validate and apply settings
>>>  # new settings have been validated and applied

The below interactive session covers the behaviour:

>>> # after problem instantiation
>>> problem.settings = new_settings # new assignment
>>> # on the background problem.validate_settings() was called
>>> # doing validation and attributes setting
>>>
>>> problem.settings["workers_num"] = 3 # no validation happened
>>> # workers_num settings hasn't been changed
>>> problem.validate_settings() # manually validate and apply settings
>>>
>>> # in case a wrong settings is given
>>> problem.settings["workers_num"] = -3
>>> problem.validate_settings() # manually validate
Traceback (most recent call last):
    ...
hyperpack.exceptions.SettingsError: workers_num multi process setting must be positive integer

Note

solution attribute will not be reset in settings re-assignment.

Below are the detailed workers_num, max_time_in_seconds, rotation, figure settings keys.

workers_num

When settings "workers_num" is given, and is above 1, then a multiprocessing search will be deployed when hypersearch is used.

In that case, a safe guarding of the main module is needed, if execution is made in windows environment, as stated in the standard library.

# in main execution module
# safe guarding
# only in windows OS systems
if __name__ == "__main__":
    problem.hypersearch()

If a number greater than 1 is given a warning message will be logged:

>>> problem.settings["workers_num"] = 2
>>> problem.validate_settings()
In Windows OS multiprocessing needs 'Entry point protection'
which means adding if '__name__' == '__main__'
before multiprocessing depending code execution

If the value of the key is not a positive integer, a SettingsError will be raised with appropriate message.

>>> problem.settings["workers_num"] = -1
>>> problem.validate_settings()
Traceback (most recent call last):
    ...
hyperpack.exceptions.SettingsError: workers_num multi process setting must be positive integer

Note

If not provided, the default value will be 1 (no multiprocessing). The default value is a class attribute constant named WORKERS_NUM_DEFAULT_VALUE. Subclassing HyperPack and overriding can change default behaviour.

If a number of workers greater than the number of procesor threads is given, an warning message will be logged: you are trying to set more workers than your cpu threads

>>> problem.settings["workers_num"] = 10**10 # maybe valid in 2123 A.D.
>>> problem.validate_settings()
you are trying to set more workers than your cpu threads
Errors while solving in multiprocess mode:

If one process fails: the system remains functional, but an error will be logged that Some of the processes raised an exception. Please check logs.

If all the processes fail: a MultiProcessError will be raised with an All processes failed. Check logged errors message.

Beware, error logging from multiple processes to a file is a special case.

max_time_in_seconds

This value governs the maximum allowed time for solving before exiting and returning the best found solution. When using hypersearch it is essential for time constrained operations to set this value appropriately.

Note

If not provided, the default value will be 60. The default value is a class attribute constant named MAX_TIME_IN_SECONDS_DEFAULT_VALUE. Subclassing and overriding can change default behaviour.

rotation

This value enables or disables the rotation of items if they dont fit with their current orientation.

Note

If not provided, the default value will be True. The default value is a class attribute constant named ROTATE_DEFAULT_VALUE. Subclassing and overriding can change default behaviour.

figure

The settings that govern figure operations can be found here