A Quarkus REST service that can solve and analyze arbitrary planning problems using WASM code.
This is an extended version of the original implementation by Christopher Chianelli, with significant enhancements including performance optimizations, additional constraint stream operations, temporal type support, and comprehensive aggregation capabilities.
-
Dynamic Class Generation: Generates Java domain classes and constraint providers at runtime from JSON specifications
-
WASM Integration: Executes WebAssembly modules via Chicory compiler for high-performance constraint evaluation
-
Full Constraint Streams API: Comprehensive support for Timefold’s constraint stream operations
-
Score Analysis: Detailed constraint analysis through the
/analyzeendpoint
-
WASM Module Caching: SHA-256 based caching prevents re-parsing identical WASM modules
-
Export Function Caching: Cached WASM export lookups reduce overhead
-
Predicate Result Caching: Memoization of predicate evaluation results
-
Geometric List Growth: O(n) amortized append operations for efficient list handling
-
Memory Layout Optimization: Aligned field offsets matching Rust’s LayoutCalculator
Stream Transformations:
- forEach - Iterate over planning entities
- forEachUniquePair - Process unique pairs with joiners
- join - Join streams with custom joiners
- filter - Filter elements by predicate
- map - Transform stream elements
- expand - One-to-many transformations
- flattenLast - Flatten nested collections (including primitive int lists)
- ifExists / ifNotExists - Conditional existence checks
- complement - Set complement operations
Aggregations (via groupBy):
- count - Count elements
- sum - Sum numeric values
- min / max - Find minimum/maximum values (with null safety)
- average - Calculate averages
- loadBalance - Fair load distribution aggregator
- consecutive - Consecutive sequence detection
- connectedRange - Connected range aggregation
- Meta-aggregators: compose, collectAndThen, conditional
Joiners: - Equal, lessThan, lessThanOrEqual, greaterThan, greaterThanOrEqual joiners - Overlapping and filtering joiners
-
Primitives:
int,long,float,double -
String and collections (including primitive int lists)
-
Temporal types:
LocalDate,LocalDateTime -
Score types:
SimpleScore,HardSoftScore,HardMediumSoftScore,HardSoftBigDecimalScore
-
Auto-generated host functions for domain-specific operations
-
String comparison:
hstringEquals -
List operations:
hlistContainsString -
Dynamic domain model parsing in host functions
-
Health Endpoint: Service readiness checks via
/health -
Solver Statistics: Response includes detailed metrics:
-
Time spent (milliseconds)
-
Score calculation count and speed
-
Move evaluation count and speed
-
Enhanced Error Handling: Full cause chain with stack frames in error responses
Memory & Performance: - Geometric growth algorithm for dynamic lists (prevents O(n²) behavior) - WASM module caching eliminates redundant parsing - Critical memory layout fixes ensure correct field offset alignment - Primitive list support with optimized i32 storage
API Enhancements: - Solver statistics in solve responses - Optional solution deallocator for flexible memory management - Health check endpoint for service monitoring - Nested list field parsing
Extended Constraint Capabilities: - Complete set of aggregators (10+ types) - Advanced stream operations (expand, complement, flattenLast) - Meta-collectors for complex aggregation patterns - Comprehensive joiner implementations
A planning problem is structured as follows:
{
"domain": "DomainMap",
"constraints": "ConstraintMap",
"wasm": "Base64String",
"allocator": "ExportedWasmFunction (int) -> int",
"deallocator": "ExportedWasmFunction (int) -> void",
"solutionDeallocator": "Optional[ExportedWasmFunction (int) -> void]",
"listAccessor": "ListAccessor",
"termination": "Optional[TerminationConfig]",
"environmentMode": "Optional[EnvironmentMode]",
"problem": "String"
}The domain is a map describing the facts, entities and solution classes of the problem. The keys of the map are the domain classes name, which may be referenced in type definitions and constraints. The values of the map describe the planning annotations on the class and how to access them from a pointer:
{
"fields": "FieldMap",
"mapper": "Optional[SerializerAndDeserializer]"
}The "FieldMap" is a map where keys are field names, and values is an object describing the field’s type, annotations on the field, and how to get and set its value:
{
"type": "String",
"annotations": "Optional[List[PlanningAnnotation]]",
"accessor": "GetterAndSetter"
}"PlanningAnnotation" is an object that describe one of Timefold Solver’s annotation.
"GetterAndSetter" is an object describing exported WASM functions for getting and setting a value:
{
"getter": "ExportedWasmFunction (int) -> int",
"setter": "ExportedWasmFunction (int, int) -> void"
}The "SerializerAndDeserializer" is an object that tells the solver which exported WASM functions can be used to create an instance from a string, and how to convert an instance to a string:
{
"fromString": "ExportedWasmFunction (int) -> int",
"toString": "ExportedWasmFunction (int) -> (int)"
}"ConstraintMap" is a map describing your constraints. The keys are the constraint names, and the values are lists of stream component objects that map to the Constraint Stream API.
"ListAccessor" is an object describing how to access and create lists:
{
"new": "ExportedWasmFunction () -> int",
"get": "ExportedWasmFunction (int, int) -> int",
"set": "ExportedWasmFunction (int, int, int) -> void",
"length": "ExportedWasmFunction (int) -> int",
"append": "ExportedWasmFunction (int, int) -> void",
"insert": "ExportedWasmFunction (int, int, int) -> void",
"remove": "ExportedWasmFunction (int, int) -> void"
}"wasm" is a base-64 encoded WASM bytes.
An example planning problem request look like this:
{
"domain": {
"Employee": {
"fields": {"name": {"type": "String"}}
},
"Shift": {
"fields": {
"start": {"type": "int"},
"end": {"type": "int"},
"employee": {
"type": "Employee",
"accessor": {"getter": "getEmployee", "setter": "setEmployee"},
"annotations": [{"annotation": "PlanningVariable", "allowsUnassigned": true}]
}
}
},
"Schedule": {
"fields": {
"employees": {
"type": "Employee[]",
"accessor": {"getter": "getEmployees", "setter": "setEmployees"},
"annotations": [
{"annotation": "ProblemFactCollectionProperty"},
{"annotation": "ValueRangeProvider"}
]
},
"shifts": {
"type": "Shift[]",
"accessor": {"getter": "getShifts", "setter": "setShifts"},
"annotations": [
{"annotation": "PlanningEntityCollectionProperty"}
]
},
"score": {
"type": "SimpleScore",
"annotations": [
{"annotation": "PlanningScore"}
]
}
},
"mapper": {"fromString": "strToSchedule", "toString": "scheduleToStr"}
}
},
"constraints": {
"penalize unassigned": [
{"kind": "each", "className": "Shift"},
{"kind": "filter", "functionName": "unassigned"},
{"kind": "penalize", "weight": "1"}
],
"reward requested time off": [
{"kind": "each", "className": "Shift"},
{"kind": "filter", "functionName": "requestedTimeOff"},
{"kind": "reward", "weight": "2"}
]
},
"wasm": "...",
"allocator": "alloc",
"deallocator": "dealloc",
"listAccessor": {
"new": "newList",
"get": "getItem",
"set": "setItem",
"length": "size",
"append": "append",
"insert": "insert",
"remove": "remove"
},
"problem": "{\"employees\":[...], ...}",
"environmentMode": "FULL_ASSERT",
"termination": {"spentLimit": "1s"}
}The /solve endpoint runs Timefold Solver and returns the optimized solution along with solver statistics.
Response Format:
{
"solution": "{\"employees\":[...], ...}",
"score": "18",
"stats": {
"timeSpentMillis": 1234,
"scoreCalculationCount": 5678,
"scoreCalculationSpeed": 4567,
"moveEvaluationCount": 9012,
"moveEvaluationSpeed": 7345
}
}The /analyze endpoint returns the ScoreAnalysis of the provided solution, including per-constraint breakdowns.
This implementation extends the original proof-of-concept by Christopher Chianelli (timefold-wasm-service) with:
-
Performance optimizations including caching, geometric list growth, and memory layout improvements
-
10+ new aggregators for advanced constraint modeling
-
Extended stream operations (expand, complement, flattenLast, forEachUniquePair)
-
Temporal type support (LocalDate, LocalDateTime)
-
Production-ready features (health checks, solver statistics, enhanced error reporting)
-
Comprehensive test coverage for new functionality
The service is used as a backend for SolverForge, enabling constraint solving from Rust and other languages via WASM compilation.