# ML Experiment Log - Differentiable graph environment

I'm interested in generative design, especially geometric deep learning.

For a simple environment I'd like to have an environment where given some sizes of boxes, the network will pack the boxes as compactly as possible by outputting a set of positions for those boxes. This output should also prefer to be square overall.

For scoring there will be 2 components to the score:

- a penalty for boxes that are taking up the same space
- a smaller penalty for the amount of space the outputs as a whole take up

Several qualities we want in our score function

- The different loss components should scale similarly for different numbers of boxes.
- The loss for conflict should be roughly the right amount to exactly move outside the box if lr=1.
- The reasoning for this is sgd directly on our loss function would hopefully have a less

- Due to the previous 2 statements, our loss functions should both scale roughly with average height/width of all boxes.

On another note, our score function should be differentiable. I'm hoping this will help reduce the amount of compute needed to train a network on the environment, but that'll be for future experiments.

## Conflict

Conflict penalty is the length of the smallest dimension's overlap.

Pseudocode

```
for each rect i
for each rect j
dx = abs(i.pos_x - j.pos_x)
x_conf = maximum(0, (i.w + j.w)/2 - dx) # Only conflicts if inner term was > 0
dy = abs(i.pos_y - j.pos_y)
y_conf = maximum(0, (i.y + j.y)/2 - dy) # Only conflicts if inner term was > 0
```

## Footprint area

For footprint area, this is calculated by taxing the max and min of each dimension (x/y). This is basically calculating the smallest rectangle that fits around all our boxes.

Pseudo code for calculating foot print area.

```
max_x, min_x, max_y, min_y = get_min_max(box_sizes, box_positions)
box_areas = (max_x - min_x) * (max_y - min_y)
box_area_sum = reduce_sum(box_areas)
batch_max_x, batch_min_x = reduce_max(max_x), reduce_min(min_x)
batch_max_y, batch_min_y = reduce_max(max_y), reduce_min(min_y)
total_footprint = (batch_max_x - batch_min_x) * (batch_max_y - batch_min_y)
# A perfect score would result in an exactly square footprint, with total footprint area
# equal to the sum of areas of individual boxes. We multiply by rectangle count and
# average height/width to keep this scoring roughly the same as the other score functions.
footprint_penalty = (1 - sqrt(box_area_sum) / sqrt(total_footprint)) * rect_count * sqrt(box_area_sum)
```

## Important notes

The way it's implemented, our minimum will actually be with a small amount of box conflict. I think this is fine for the moment, but can be fixed.