Pillar nodes (CapturedNodes)

This feature guide covers pillar nodes, also called CapturedNodes: nodes whose time windows JOpt.TourOptimizer treats as hard constraints, so that their SLA is either matched or the node is removed, but never violated. It is for developers who model contractual appointments, compliance deadlines or other time windows that must not be violated. It assumes that you know nodes and opening hours from Basic elements.
Table of contents
What it does
In JOpt.TourOptimizer, the SLA of a node is an OpeningHours with a particular time window. Optimizers usually position nodes by cost and sometimes violate an SLA, depending on the cost-benefit ratio. That makes it hard to tell an optimizer that being 10 minutes late at one node is fine, but at another node it is not acceptable.
To our best knowledge, most optimizers address this by assigning extremely high cost penalties. In JOpt, a hard constraint is a property of the architecture, not of the cost: the optimizer is designed so that hard constraints cannot be violated, and the search space is structurally cut to exclude invalid solutions.
A CapturedNode (also called a pillar or PillarNode) is the primary mechanism for this. Its opening hours cannot be violated under any circumstances. The other nodes of the schedule flow around the pillar: they shift, reorder or move to other resources to keep the pillar's time window intact.
Why not penalties
Penalty-based "hard" constraints are pseudo constraints: expensive soft constraints that pretend to be hard. As the complexity of a problem grows, three things go wrong in practice:
- The optimizer wastes its budget. The search spends most of its capacity on inflated penalty terms instead of on the actual schedule. Soft constraints such as travel time, overtime and fairness are starved.
- Pseudo-hard constraints fight each other. With dozens of penalty-based "hard" constraints in the same cost function, the optimizer trades violations between them. A solution that violates one SLA to save another is "optimal" by the numbers but unacceptable for the business.
- There is no guarantee. A sufficiently complex problem finds ways to violate even very high penalties. A cost function cannot reliably distinguish "preferred" from "legally required".
This limitation was not theoretical. It was reported by customers running large-scale field-service operations with thousands of daily dispatches and contractual SLAs. As their problem complexity grew, penalty-based solvers produced increasingly problematic results. The motivation for Pillar Nodes came directly from these real-world scenarios.
How pillars work
A pillar acts as a fixed anchor in the route timeline. Its opening hours are absolute feasibility boundaries:
- The visit must be placed within the time window.
- The pillar can move within its window, or be fully constrained to a specific slot.
- In all cases, the window boundaries are inviolable.
- Arriving outside the Pillar's time window is structurally impossible. The optimizer will not generate such a solution.
This follows from JOpt's attempt-based architecture: every permutation of nodes and resources leads to a valid solution, because the optimizer cuts the invalid solutions out of the solution space. In return, every solution it finds is valid. It is not necessarily converged or at optimal cost, but it contains no hard constraint violation.
Because hard constraints are a property of the data and not a parameter of the cost function, they never compete with soft constraints. Soft constraints are optimized freely within the feasible space that the hard constraints define. The two layers are separate.
Conflict resolution
When a pillar conflicts with other nodes, the optimizer follows a strict hierarchy:
- Reorder or shift normal nodes so that the pillar SLA is met. Normal nodes are moved earlier or later in the timeline.
- Accept soft violations on normal nodes. If a normal node must be late to protect the pillar, the cost function handles that trade-off.
- Move work to other resources. If the current resource cannot serve both the pillar and a conflicting node, the optimizer assigns the normal node to another resource.
- Cancel the pillar rather than violate it. In extreme cases where no feasible schedule exists, the pillar is removed from the plan. A cancelled contract is preferred over a violated one, as damage control by design.
The following cases show how the optimizer resolves each type of conflict. In the figures, John is a resource with working hours, nodes are light blue (opening hours) with their task duration in dark blue, and CapturedNodes are orange.
Case 1: No conflict
In the usual case, matching the opening hours to the working hours of a resource is unproblematic. Visiting CapturedNodes and nodes in the same route causes no conflict, and the CapturedNode behaves like a normal node. John can visit both nodes within their opening hours and finish the tasks, and fitting in the CapturedNode causes no violation.
Figure: CapturedNode case 1, no conflict
Case 2: Conflict solved by moving a node
In some cases, visiting a node before a CapturedNode would cause a late violation at the CapturedNode. The optimizer recognizes the potential late violation and solves it by repositioning the node.
Figure: CapturedNode case 2, solved by moving a node (left: before, right: after)
Case 3: Conflict solved via late violation at the node
Sometimes a node and a CapturedNode conflict so that only one of them can be visited without a late violation. By definition, late violations at CapturedNodes are not allowed. The optimizer therefore accepts a late violation at the normal node to protect the CapturedNode.
Figure: CapturedNode case 3, late violation accepted at the normal node
Case 4: Conflict solved by utilizing an additional resource
In bigger problems, more resources are usually available. Often the best way to solve a conflict is to move a conflicting node to another resource. Which node stays in John's route depends on factors such as driving times, required skills or the optional mandatory resource.
Figure: CapturedNode case 4, solved by assigning a node to another resource
Case 5: Conflict solved by removing a CapturedNode
If John is the only resource available for two CapturedNodes that conflict with each other, the only possible solution is to remove one of the CapturedNodes. This shows the extreme case: the SLAs of CapturedNodes cannot be violated under any circumstances. If no other solution is viable, a CapturedNode contract is cancelled rather than violated.
Figure: CapturedNode case 5, contract cancelled rather than violated
CapturedNodes keep their SLAs; only an exclusive conflict removes a CapturedNode (case 5).
When to use it
Use pillars when violations are unacceptable
Use pillars for truly non-negotiable requirements:
- contractual appointment windows with penalties for late arrival;
- compliance deadlines imposed by regulation;
- dangerous-goods pickups that require certified personnel within specific time slots;
- named-person commitments, where the wrong technician showing up is as bad as showing up late.
Do not use pillars for preferences
If a time window is preferred but not mandatory, model it as a normal node and handle it through soft constraint penalties, objective weighting or business rules. Do not use pillars for, say, a hairdressing appointment where a 10-minute delay is tolerable.
Keep the two apart:
- architecture for obligations;
- cost function for preferences and trade-offs.
The cost function then carries no pseudo-hard constraints that consume the optimizer's budget.
How to use it
Pillar node classes
The pillar concept appears as dedicated node classes that implement IPillarNode:
| Class | Description |
|---|---|
PillarTimeWindowGeoNode | Geo-located pillar (latitude and longitude plus strict opening hours) |
PillarEventNode | Pillar without a location (event style, strict opening hours) |
Mandatory resource
A pillar can optionally be tied to a mandatory resource:
pillar.attachResource(resource);
When attached, the node is not only time-critical but also bound to one specific resource. This is useful for:
- skills or certifications (for example dangerous-goods handling);
- legal requirements;
- named-person commitments (for example "this customer expects technician John").
JOpt supports hard constraints beyond pillar time windows, including mandatory resource assignment and skill or type matching. Every hard constraint in JOpt can also be configured to behave as a soft constraint if desired, giving full control over the boundary between obligation and preference.
The code in the next three sections is adapted from the example PillarExample.java, which contains three pillar variants. The dates and times are sample data.
Geo pillar with mandatory resource
A plumbing job in Cologne with a strict 50-minute time window, assigned to John:
IOpeningHours ohPlumbing =
new OpeningHours(
ZonedDateTime.of(2019, JUNE.getValue(), 5, 10, 0, 0, 0,
ZoneId.of("Europe/Berlin")),
ZonedDateTime.of(2019, JUNE.getValue(), 5, 10, 50, 0, 0,
ZoneId.of("Europe/Berlin")));
IPillarNode colognePlumbing =
new PillarTimeWindowGeoNode(
"Cologne Plumbing", 50.9333, 6.95, ohPlumbing);
// John must handle this job
colognePlumbing.attachResource(johnRes);
opti.addElement(colognePlumbing);
The optimizer guarantees that John visits this node between 10:00 and 10:50. If the visit duration equals the opening hours, it is not necessary to provide the duration separately.
Event pillar with mandatory resource
An important phone call for Jack between 10:30 and 12:00 that lasts 30 minutes. It needs no location:
IOpeningHours ohCall =
new OpeningHours(
ZonedDateTime.of(2019, JUNE.getValue(), 6, 10, 30, 0, 0,
ZoneId.of("Europe/Berlin")),
ZonedDateTime.of(2019, JUNE.getValue(), 6, 12, 0, 0, 0,
ZoneId.of("Europe/Berlin")));
Duration visitDuration = Duration.ofMinutes(30);
IPillarNode callJack =
new PillarEventNode("Important call", ohCall, visitDuration);
// Jack must take this call
callJack.attachResource(jackRes);
opti.addElement(callJack);
The call can be placed anywhere within the 10:30 to 12:00 window, and Jack is guaranteed to be available for 30 minutes within that range.
Geo pillar without mandatory resource
A maintenance job in Stuttgart between 13:30 and 16:00 that lasts 90 minutes. Either resource can take it:
IOpeningHours ohMaintenance =
new OpeningHours(
ZonedDateTime.of(2019, JUNE.getValue(), 4, 13, 30, 0, 0,
ZoneId.of("Europe/Berlin")),
ZonedDateTime.of(2019, JUNE.getValue(), 4, 16, 0, 0, 0,
ZoneId.of("Europe/Berlin")));
Duration visitDuration = Duration.ofMinutes(90);
IPillarNode maintenanceJob =
new PillarTimeWindowGeoNode(
"Maintenance job", 48.7667, 9.18333,
ohMaintenance, visitDuration);
// No resource attached: optimizer selects the best fit
opti.addElement(maintenanceJob);
The time window is still treated as a hard constraint. The optimizer selects whichever resource can serve this node without violating the SLA.
Limits
A CapturedNode limits the available optimization space, because the optimizer cannot freely rearrange it. This is by design. Overusing pillars (marking every node as a pillar) therefore shrinks the solution space unnecessarily and reduces the optimization quality. Reserve pillars for the constraints that truly require them.
In an exclusive conflict, the optimizer removes a pillar from the plan rather than violate it; see Case 5.
Related examples
- PillarExample.java on GitHub: the three pillar variants above
- Pillar example: the walkthrough of the example in the hub
Related pages
- Guaranteed matching SLAs on the special features page: the CapturedNode and its conflict cases among the other special features
- Hard constraints on the special features page: hard constraints in JOpt beyond pillar time windows, including mandatory resource conditions and skill or type matching
- Nodes: the node types and their constructors
Authors
DNA Evolutions GmbH
Added: 2026-03-28 (JOpt core 7.5.3-j17) | Last updated: 2026-10-19
Pickup and delivery
How to model pickup and delivery (PND) problems with JOpt in Java, with node depots, resource depots, the load types, capacity factors and the depot reports.
Quantum construction plugin (D-Wave)
Feature guide to the D-Wave quantum construction plugin for JOpt.TourOptimizer, with its build, its development and its activation in a REST request