Read Out Full Progress — Structured KPIs During Optimization
JOpt.TourOptimizer can emit much richer information than a simple “percent done” number.
If you subscribe to the progress stream, you can read out live KPIs for the current best (winner) solution while the optimizer is still running.
This is essential for:
- live dashboards (web UIs, planning workbenches),
- observability and telemetry (metrics + logs),
- early stopping / loop control (“stop if improvement stalls”),
- exporting “best-so-far” solutions during long runs.
This document covers two approaches shown in the examples:
- Manual extraction (compute a progress string yourself from
IOptimizationProgress+ winnerIEntity). - Using the helper utility
ParsedProgress(recommended for maintainability and consistency).
References
Examples
- ReadOutFullProgressExample.java:
https://github.com/DNA-Evolutions/Java-TourOptimizer-Examples/blob/master/src/main/java/com/dna/jopt/touroptimizer/java/examples/advanced/readoutfullprogress/ReadOutFullProgressExample.java - ReadOutFullProgressWithUtilExample.java:
https://github.com/DNA-Evolutions/Java-TourOptimizer-Examples/blob/master/src/main/java/com/dna/jopt/touroptimizer/java/examples/advanced/readoutfullprogress/ReadOutFullProgressWithUtilExample.java
Utility
- ParsedProgress.java:
https://github.com/DNA-Evolutions/Java-TourOptimizer-Examples/blob/master/src/main/java/com/dna/jopt/touroptimizer/java/examples/util/progressparser/ParsedProgress.java
What “full progress” means in this context
Progress updates are emitted as IOptimizationProgress.
From that progress object you can read:
1) Meta progress information
- Progress (
p.getProgress()): the current stage progress percentage. - Caller ID (
p.getCallerId()): a stage/caller identifier for where the progress originates.
Practical use:
- show “SA 32%” / “GE 84%” in a UI,
- label metrics by stage,
- implement stage-specific logic (e.g., stop GE after N loops).
2) Winner (best-so-far) solution metrics
- Winner entity (
p.getResultEntity()): the current best solution. - From the winner’s detail controller (
winner.getJoinedDetailController()), you can extract live KPIs such as:- transit time, idle time, productive time,
- total distance,
- termination transit metrics (see below).
These KPIs represent what you actually care about operationally:
- how much the plan improved,
- how efficient the tours are,
- whether the optimization is converging.
Approach 1 — Manual KPI extraction (ReadOutFullProgressExample)
ReadOutFullProgressExample demonstrates the direct approach:
- subscribe to progress updates,
- read the winner entity,
- compute and format a KPI string yourself.
The core pattern
- Read the winner:
IEntity winner = p.getResultEntity();
- Build a “total route time excluding flex time”:
- transit time + idle time + productive time
- Compute utilization:
productiveTime / (transit + idle + productive)
- Print a compact status line such as:
- PC: progress percent
- AL: caller id
- JC: joined cost
- RC: route count
- EC: optimizable element count
- TC: total element count (routeCount + elementCount)
- TTh: total time (hours)
- TU%: utilization
- TDkm: total distance
Why this is useful
This approach is:
- very transparent,
- easy to customize,
- and ideal if you want a very specific formatting for your UI or logs.
Limitations
As soon as you need more KPIs (flex time, termination transit, etc.), manual extraction can become:
- repetitive,
- inconsistent across projects,
- and easy to get wrong.
That is why the repository also includes a utility.
Approach 2 — Use the ParsedProgress helper (recommended)
ReadOutFullProgressWithUtilExample demonstrates a cleaner integration:
- create a
ParsedProgressobject from the progress event:ParsedProgress pp = new ParsedProgress(p);
- print a richer KPI line assembled from typed getters.
What ParsedProgress provides (as used in the example)
Primary meta information
pp.getProgress()— progress percentpp.getCallerId()— caller/stage label
Global objective and size
pp.getCost()— joined (abstract) costpp.getRouteCount()— number of routespp.getOptimizableElementsCount()— number of optimizable elementspp.getElementsCount()— total elements (routes + elements)
Time breakdown
pp.getTime()— total time (computed as transit + idle + productive; excludes flex time)pp.getProductiveTime()— productive timepp.getIdleTime()— idle timepp.getFlexTime()— flex timepp.getTransitTime()— transit timepp.getTerminationTransitTime()— termination transit time
Distance
pp.getDistance()— total distancepp.getTerminationTransitDistance()— termination transit distance
Utilization
pp.getUtilization()—productiveTime / (productive + idle + transit)(flex time excluded)
What the example prints
The example constructs a line containing:
- progress + stage label
- cost and counts
- time breakdown (total, productive, idle, flex, transit, termination transit)
- utilization
- distance
This is an excellent “single-line telemetry output” for:
- logs,
- live terminals,
- and quick dashboards.
Why this approach is usually better
- You get a consistent definition of KPIs across examples and projects.
- It is easier to add more KPIs later without duplicating logic.
- It avoids mixing formatting logic with metric extraction logic.
Understanding the time components
While naming is self-explanatory, these distinctions matter in production:
Transit Time
Time spent traveling between nodes (including start/termination legs, depending on model and metric definition).
Productive Time
Time spent performing service/visit work at nodes (visit duration, service durations, etc.).
Idle Time
Waiting time that happens due to constraints, for example:
- arriving early and waiting for OpeningHours,
- scheduling gaps created by time windows.
Flex Time
Flex time is typically the time flexibility or slack that results from time windows and scheduling latitude.
In dashboards, it is often useful because:
- high flex time can indicate robustness (schedule has buffer),
- or inefficiency (too much waiting due to poor sequencing).
Termination Transit
Termination transit metrics relate to the “route termination” leg(s) in your model.
This is relevant especially when:
- routes are closed (return to depot), or
- termination points are explicitly modeled.
In practice:
- if you care about “deadheading back to base”, termination transit is a key KPI.
Subscribing to progress events (Observable pattern)
The util example demonstrates how to subscribe to optimization events using the optimizer’s event subjects:
progressSubject()— progress streamwarningSubject()— warningsstatusSubject()— status updateserrorSubject()— errors
This is a production-friendly pattern because it separates:
- the optimization engine,
- and the integration layer (UI/logging/monitoring).
Best practices for production usage
1) Do not log too frequently
Progress callbacks can fire very often. Excessive output can slow down the run.
Combine this doc with the “OptimizationProgress” pattern:
- configure
OnProgressOutPercentage, - and/or request progress at a controlled wall-clock frequency.
2) Keep callback work minimal
In progressSubject().subscribe(...), avoid heavy work such as:
- exporting KML on every update,
- large JSON serialization,
- database writes.
Instead:
- push to a queue,
- aggregate,
- and process asynchronously.
3) Track best-so-far improvements
If your UI needs “improvement over time” charts:
- store snapshots of (cost, distance, time) every N seconds,
- not every progress event.
4) Stage-aware monitoring
Use callerId to:
- segment metrics by stage,
- detect “we are stuck in a stage”,
- apply stage-specific early stopping rules.
Summary
- Full progress in JOpt is read from
IOptimizationProgress, including the current best winner solution. - You can extract a compact KPI string manually (ReadOutFullProgressExample) or use
ParsedProgressfor a richer and more maintainable KPI set (recommended). - The util example prints a detailed line including cost, counts, time breakdown (productive/idle/flex/transit/termination), utilization, and distance.
- This integration is central for observability, dashboards, and intelligent run control in long optimizations.
PerformanceMode — Faster Genetic Optimization With Reduced Operators
Performance Mode is a built-in execution strategy in JOpt.TourOptimizer that aims to reduce runtime by simplifying parts of the optimization process.
Relations — Coupling Nodes Across Routes, Resources, and Time
Relations are one of the most powerful modeling tools in JOpt.TourOptimizer. They let you express dependencies between nodes that cannot be captured by distance-based cost alone, such as: