Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/content/blog/2024.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
title: "2024"
menu:
main:
identifier: "2024"
parent: "Blog"
weight: 20
---
37 changes: 37 additions & 0 deletions docs/content/blog/posts/2024/24-12-06-layer-unzipping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: "Layer Unzipping"
menu:
main:
identifier: "24-12-06-layer-unzipping"
parent: "2024"
weight: 10
---

_By Maximilian Kasperowski, December 6, 2024_

The coming update (ELK 0.9.2) introduces a new feature to control the positioning of nodes in a layer for further compaction.
Layer Unzipping can split up the nodes of a single layer into multiple layers.
This reduces the total height and can be helpful in certain situations.

The update introduces three new properties:
- `org.eclipse.elk.layerUnzipping.strategy`
- `org.eclipse.elk.layerUnzipping.layerSplit`
- `org.eclipse.elk.layerUnizpping.resetOnLongEdges`

This [elklive example](https://rtsys.informatik.uni-kiel.de/elklive/elkgraph.html?compressedContent=MYewdgzglgJgpgJwLIngGwPIPggdBAFwQEMC4BzATwC4ACABQCUBRAMWcYH1mARAcWYBlAFBpilRAFUwALygAHeVDDl8RUhRq0AggBkAKhwBy2-QEkjfYcLCo4tMAEZa1uDHL2ntALQA+BwBMLm4eDs5+DgDMwiGeQRFgACwx7p4ArD7+Sa6pYZkOybF5CWkpoV4JAGzWAPQ1tABmUAiEtGIIoWISCDZ2gbQA3sK0I23iUrIKSiq4CHAQcAQYYLrg5MypEHQNxGgLtHW0MFAQxABGaPbwOwCuaAS0Z3AAFsQAblAgNwi0xGAwtCIcFIbTWtFiEF+kLm5DuxB+tngEGEAF9evAooNhqMuhM5IplKpcQhBPI0FACHQAqj0Z5Ei5EekGX0wJUXMJDgtQP8xh17MTaYFnENRmNutJ8dMieMSWSKXRomjGVFnIKkqrlWA0hqWZVVWU6fl1cFcqyjXqcuUAuEsgB2E3lSI2hz2g0FZ1gV1FLUer2mvVG11q21AA) demonstrates the properties and the exact behaviour of the properties is explained in the sections below.

## Layer Unzipping Strategy
For now there is only the strategy `LayerUnzippingStrategy.ALTERNATING`.
It evenly distributes the nodes into several sub-layers.
The first node goes into the first sub-layer, the second goes into the second and so on.
The default configuration is to use two sub-layers.

## Configuring the Number of Sub-layers
The number of sub-layers is two by default and can be changed with the `layerSplit` property.
The property applies to an entire layer.
To use it, it must be set on any node of that layer.
If multiple values are set, then the lowest value is used.

## Long Edge Treatment
Under the hood long edges are implemented using invisible dummy nodes.
The default behaviour is begin the alternatinon anew after a long edge, but this behaviour can be disabled for a layer by setting `resetOnLongEdges` to `false` for any node in the layer.
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ private LayoutProcessorConfiguration<LayeredPhases, LGraph> getPhaseIndependentL
}

switch (lgraph.getProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY)) {
case N_LAYERS:
configuration.addBefore(LayeredPhases.P4_NODE_PLACEMENT, IntermediateProcessorStrategy.LAYER_UNZIPPER);
case ALTERNATING:
configuration.addBefore(LayeredPhases.P4_NODE_PLACEMENT, IntermediateProcessorStrategy.ALTERNATING_LAYER_UNZIPPER);
break;
default:
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -960,25 +960,26 @@ group layerUnzipping {
advanced option layerSplit: Integer {
label "Unzipping Layer Split"
description
"Defines the number of sublayers to split a layer into when using the N_LAYERS strategy.
The property can be set to the first node in a layer, which then applies the property
for the layer the node belongs to."
"Defines the number of sublayers to split a layer into. The property can be set to the nodes in a layer,
which then applies the property for the layer. If multiple nodes set the value to different values,
then the lowest value is chosen."
default = 2
targets nodes
lowerBound = 1
requires layerUnzipping.strategy == LayerUnzippingStrategy.N_LAYERS
}

option resetOnLongEdges: Boolean {
label "Reset Alternation on Long Edges"
description
"If set to true, nodes will always be placed in the first sublayer after a long edge.
"If set to true, nodes will always be placed in the first sublayer after a long edge when using the
ALTERNATING strategy.
Otherwise long edge dummies are treated the same as regular nodes. The default value is true.
The property can be set to the first node in a layer, which then applies the property
for the layer the node belongs to."
The property can be set to the nodes in a layer, which then applies the property
for the layer. If any node sets the value to false, then the value is set to false for the entire
layer."
default = true
targets nodes
requires layerUnzipping.strategy == LayerUnzippingStrategy.N_LAYERS
requires layerUnzipping.strategy == LayerUnzippingStrategy.ALTERNATING
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import org.eclipse.elk.alg.layered.graph.LGraph;
import org.eclipse.elk.alg.layered.intermediate.compaction.HorizontalGraphCompactor;
import org.eclipse.elk.alg.layered.intermediate.unzipping.GeneralLayerUnzipper;
import org.eclipse.elk.alg.layered.intermediate.unzipping.AlternatingLayerUnzipper;
import org.eclipse.elk.alg.layered.intermediate.wrapping.BreakingPointInserter;
import org.eclipse.elk.alg.layered.intermediate.wrapping.BreakingPointProcessor;
import org.eclipse.elk.alg.layered.intermediate.wrapping.BreakingPointRemover;
Expand Down Expand Up @@ -97,7 +97,7 @@ public enum IntermediateProcessorStrategy implements ILayoutProcessorFactory<LGr
/** Position self loops after phase 3. */
SELF_LOOP_PORT_RESTORER,
/** Unzips layers for compaction. */
LAYER_UNZIPPER,
ALTERNATING_LAYER_UNZIPPER,
/** Wraps graphs such that they better fit a given drawing area, allowing only a single edge per cut. */
SINGLE_EDGE_GRAPH_WRAPPER,
/** Makes sure that in-layer constraints are handled. */
Expand Down Expand Up @@ -331,8 +331,8 @@ public ILayoutProcessor<LGraph> create() {
case SELF_LOOP_PORT_RESTORER:
return new SelfLoopPortRestorer();

case LAYER_UNZIPPER:
return new GeneralLayerUnzipper();
case ALTERNATING_LAYER_UNZIPPER:
return new AlternatingLayerUnzipper();

case SELF_LOOP_POSTPROCESSOR:
return new SelfLoopPostProcessor();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
* </dl>
*
*/
public class GeneralLayerUnzipper implements ILayoutProcessor<LGraph> {
public class AlternatingLayerUnzipper implements ILayoutProcessor<LGraph> {

@Override
public void process (LGraph graph, IElkProgressMonitor progressMonitor) {
Expand All @@ -58,8 +58,8 @@ public void process (LGraph graph, IElkProgressMonitor progressMonitor) {
List<Pair<Layer, Integer>> newLayers = new ArrayList<>();
for (int i = 0; i < graph.getLayers().size(); i++) {

int N = graph.getLayers().get(i).getProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT);
boolean resetOnLongEdges = graph.getLayers().get(i).getProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES);
int N = getLayerSplitProperty(graph.getLayers().get(i));
boolean resetOnLongEdges = getResetOnLongEdgesProperty(graph.getLayers().get(i));

// only split if there are more nodes than the resulting sub-layers
// an alternative would be to reduce N for this layer, this may or may
Expand Down Expand Up @@ -110,6 +110,45 @@ public void process (LGraph graph, IElkProgressMonitor progressMonitor) {


}

/**
* Checks all nodes of a layer for the layerSplit property and returns the lowest set value.
*
* @param layer The layer to determine the layerSplit property for
* @return the layerSplit value
*/
private int getLayerSplitProperty(Layer layer) {
int layerSplit = Integer.MAX_VALUE;
boolean propertyUnset = true;
for (int i = 0; i < layer.getNodes().size(); i++) {
if (layer.getNodes().get(i).hasProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT)) {
propertyUnset = false;
int nodeValue = layer.getNodes().get(i).getProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT);
layerSplit = layerSplit < nodeValue ? layerSplit : nodeValue;
}
}
if (propertyUnset) {
layerSplit = LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT.getDefault();
}
return layerSplit;
}

/**
* Checks all nodes of a layer for the resetOnLongEdges property and if any sets the value to false, returns false.
*
* @param layer The layer to determine the resetOnLongEdges property for.
* @return the resetOnLongEdges value
*/
private boolean getResetOnLongEdgesProperty(Layer layer) {
for (int i = 0; i < layer.getNodes().size(); i++) {
if (layer.getNodes().get(i).hasProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES)) {
if (!layer.getNodes().get(i).getProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES)) {
return false;
}
}
}
return true;
}

/**
* checks the layer split property of the first node in a layer and copies the property to the layer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
public enum LayerUnzippingStrategy {

NONE,
/** Splits all layers with more than two nodes into two layers. */
N_LAYERS;
/** Splits all layers with more than two nodes into several layers in an alternating pattern. */
ALTERNATING;

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@
import org.junit.Test;

/**
* Tests the general n-way layer unzipper.
* Tests the general n-way alternating layer unzipper.
*
*/
public class GeneralLayerUnzipperTest {
public class AlternatingLayerUnzipperTest {

LayeredLayoutProvider layeredLayout;

Expand Down Expand Up @@ -71,7 +71,7 @@ public void simpleTwoSplit() {
ElkGraphUtil.createSimpleEdge(node4, nodeFinal);


graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);

Expand Down Expand Up @@ -125,7 +125,7 @@ public void simpleThreeSplit() {
ElkGraphUtil.createSimpleEdge(node4, nodeFinal);
ElkGraphUtil.createSimpleEdge(node5, nodeFinal);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 3);

Expand Down Expand Up @@ -173,7 +173,7 @@ public void danglingOutgoing() {
ElkGraphUtil.createSimpleEdge(node1, node4);
ElkGraphUtil.createSimpleEdge(node1, node5);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);

Expand Down Expand Up @@ -225,7 +225,7 @@ public void danglingIncoming() {
ElkGraphUtil.createSimpleEdge(node4, nodeFinal);
ElkGraphUtil.createSimpleEdge(node5, nodeFinal);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);

Expand Down Expand Up @@ -290,7 +290,7 @@ public void multipleLayersSplit() {
ElkGraphUtil.createSimpleEdge(node4, node22);
ElkGraphUtil.createSimpleEdge(node4, node23);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node1.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
node21.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
Expand Down Expand Up @@ -354,7 +354,7 @@ public void multipleIncomingEdges() {
ElkGraphUtil.createSimpleEdge(node4, nodeFinal);


graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);

Expand Down Expand Up @@ -409,7 +409,7 @@ public void multipleOutgoingEdges() {
ElkGraphUtil.createSimpleEdge(node4, nodeFinal);


graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);

Expand Down Expand Up @@ -466,7 +466,7 @@ public void mixedDanglingIncoming() {
ElkGraphUtil.createSimpleEdge(node3, node6);
ElkGraphUtil.createSimpleEdge(node4, node7);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
node5.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
Expand Down Expand Up @@ -530,7 +530,7 @@ public void mixedDanglingOutgoing() {
ElkGraphUtil.createSimpleEdge(node3, node6);
ElkGraphUtil.createSimpleEdge(node4, node7);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
node5.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
Expand Down Expand Up @@ -598,7 +598,7 @@ public void mixedTwoThreeLayerSplit() {
ElkGraphUtil.createSimpleEdge(node4, node22);
ElkGraphUtil.createSimpleEdge(node4, node23);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node1.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 3);
node21.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
Expand Down Expand Up @@ -652,7 +652,7 @@ public void resetOnLongEdges() {

ElkGraphUtil.createSimpleEdge(node4, node5);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES, true);
Expand Down Expand Up @@ -707,7 +707,7 @@ public void noResetOnLongEdges() {
ElkGraphUtil.createSimpleEdge(node4, node5);
ElkGraphUtil.createSimpleEdge(node1, node6);

graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.N_LAYERS);
graph.setProperty(LayeredOptions.LAYER_UNZIPPING_STRATEGY, LayerUnzippingStrategy.ALTERNATING);
graph.setProperty(LayeredOptions.CONSIDER_MODEL_ORDER_STRATEGY, OrderingStrategy.PREFER_EDGES);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_LAYER_SPLIT, 2);
node2.setProperty(LayeredOptions.LAYER_UNZIPPING_RESET_ON_LONG_EDGES, false);
Expand Down
Loading