Reference
API reference and quick lookup guides
Quick reference guides and API documentation.
In This Section
Import Summary
// Domain modeling
use solverforge_core::domain::{
DomainClass, DomainModel, FieldDescriptor, FieldType,
PlanningAnnotation, PrimitiveType, ScoreType,
};
// Constraints
use solverforge_core::{
Collector, Constraint, ConstraintSet, Joiner,
StreamComponent, WasmFunction,
};
// Scores
use solverforge_core::{
HardSoftScore, HardMediumSoftScore, SimpleScore, BendableScore,
Score, // trait
};
// Solver
use solverforge_core::{
SolveRequest, SolveResponse, SolverConfig, TerminationConfig,
EnvironmentMode, MoveThreadCount,
};
// WASM
use solverforge_core::wasm::{
Expr, FieldAccessExt, Expression,
WasmModuleBuilder, PredicateDefinition, HostFunctionRegistry,
};
// Errors
use solverforge_core::{SolverForgeError, SolverForgeResult};
// Service
use solverforge_service::{EmbeddedService, ServiceConfig};
Crate Structure
solverforge_core
├── domain # Domain model definitions
│ ├── DomainModel
│ ├── DomainClass
│ ├── FieldDescriptor
│ ├── FieldType
│ ├── PlanningAnnotation
│ └── PrimitiveType
├── constraints # Constraint streams
│ ├── StreamComponent
│ ├── Collector
│ ├── Joiner
│ └── WasmFunction
├── score # Score types
│ ├── Score (trait)
│ ├── HardSoftScore
│ ├── HardMediumSoftScore
│ └── BendableScore
├── solver # Solver configuration
│ ├── SolverConfig
│ ├── TerminationConfig
│ └── EnvironmentMode
├── wasm # WASM generation
│ ├── Expression
│ ├── Expr
│ ├── WasmModuleBuilder
│ └── PredicateDefinition
└── error # Error types
├── SolverForgeError
└── SolverForgeResult
1 - API Quick Reference
Cheat sheet for common SolverForge APIs
Domain Model
DomainModel
DomainModel::builder()
.add_class(class)
.build() // Build without validation
.build_validated()? // Build with validation
DomainClass
DomainClass::new("ClassName")
.with_annotation(PlanningAnnotation::PlanningEntity)
.with_field(field)
FieldDescriptor
FieldDescriptor::new("fieldName", FieldType::...)
.with_planning_annotation(PlanningAnnotation::...)
Field Types
| Type | Rust |
|---|
| Primitives | FieldType::Primitive(PrimitiveType::String) |
| Object | FieldType::object("ClassName") |
| List | FieldType::list(element_type) |
| Set | FieldType::set(element_type) |
| Array | FieldType::array(element_type) |
| Map | FieldType::map(key_type, value_type) |
| Score | FieldType::Score(ScoreType::HardSoft) |
Primitive Types
| Type | Rust |
|---|
| Boolean | PrimitiveType::Bool |
| Int (32-bit) | PrimitiveType::Int |
| Long (64-bit) | PrimitiveType::Long |
| Float | PrimitiveType::Float |
| Double | PrimitiveType::Double |
| String | PrimitiveType::String |
| Date | PrimitiveType::Date |
| DateTime | PrimitiveType::DateTime |
Planning Annotations
| Annotation | Rust |
|---|
| PlanningId | PlanningAnnotation::PlanningId |
| PlanningEntity | PlanningAnnotation::PlanningEntity |
| PlanningSolution | PlanningAnnotation::PlanningSolution |
| PlanningVariable | PlanningAnnotation::planning_variable(vec!["id"]) |
| PlanningVariable (nullable) | PlanningAnnotation::planning_variable_unassigned(vec![]) |
| PlanningListVariable | PlanningAnnotation::planning_list_variable(vec![]) |
| PlanningScore | PlanningAnnotation::planning_score() |
| PlanningScore (bendable) | PlanningAnnotation::planning_score_bendable(2, 3) |
| ValueRangeProvider | PlanningAnnotation::value_range_provider("id") |
| ProblemFactCollectionProperty | PlanningAnnotation::ProblemFactCollectionProperty |
| PlanningEntityCollectionProperty | PlanningAnnotation::PlanningEntityCollectionProperty |
| PlanningPin | PlanningAnnotation::PlanningPin |
| InverseRelationShadow | PlanningAnnotation::inverse_relation_shadow("var") |
Constraint Streams
StreamComponent
| Operation | Rust |
|---|
| forEach | StreamComponent::for_each("Class") |
| forEach (unassigned) | StreamComponent::for_each_including_unassigned("Class") |
| forEachUniquePair | StreamComponent::for_each_unique_pair("Class") |
| forEachUniquePair (joiners) | StreamComponent::for_each_unique_pair_with_joiners("Class", joiners) |
| filter | StreamComponent::filter(WasmFunction::new("pred")) |
| join | StreamComponent::join("Class") |
| join (joiners) | StreamComponent::join_with_joiners("Class", joiners) |
| ifExists | StreamComponent::if_exists("Class") |
| ifNotExists | StreamComponent::if_not_exists("Class") |
| groupBy | StreamComponent::group_by(keys, collectors) |
| groupBy (key only) | StreamComponent::group_by_key(key) |
| groupBy (collect only) | StreamComponent::group_by_collector(collector) |
| map | StreamComponent::map(mappers) |
| map (single) | StreamComponent::map_single(mapper) |
| flattenLast | StreamComponent::flatten_last() |
| expand | StreamComponent::expand(mappers) |
| complement | StreamComponent::complement("Class") |
| penalize | StreamComponent::penalize("1hard/0soft") |
| penalize (weigher) | StreamComponent::penalize_with_weigher("1hard", weigher) |
| reward | StreamComponent::reward("1soft") |
Joiners
| Joiner | Rust |
|---|
| equal | Joiner::equal(map) |
| equal (separate) | Joiner::equal_with_mappings(left, right) |
| lessThan | Joiner::less_than(map, comparator) |
| greaterThan | Joiner::greater_than(map, comparator) |
| overlapping | Joiner::overlapping(start, end) |
| filtering | Joiner::filtering(filter) |
Collectors
| Collector | Rust |
|---|
| count | Collector::count() |
| countDistinct | Collector::count_distinct() |
| sum | Collector::sum(map) |
| average | Collector::average(map) |
| min | Collector::min(map, comparator) |
| max | Collector::max(map, comparator) |
| toList | Collector::to_list() |
| toSet | Collector::to_set() |
| loadBalance | Collector::load_balance(map) |
| loadBalance (with load) | Collector::load_balance_with_load(map, load) |
| compose | Collector::compose(collectors, combiner) |
| conditionally | Collector::conditionally(pred, collector) |
| collectAndThen | Collector::collect_and_then(collector, mapper) |
Scores
Score Types
| Type | Create | Parse |
|---|
| SimpleScore | SimpleScore::of(-5) | SimpleScore::parse("-5") |
| HardSoftScore | HardSoftScore::of(-2, 10) | HardSoftScore::parse("-2hard/10soft") |
| HardMediumSoftScore | HardMediumSoftScore::of(-1, 5, 10) | HardMediumSoftScore::parse(...) |
| BendableScore | BendableScore::of(hard_vec, soft_vec) | N/A |
Score Methods
score.is_feasible() // hard >= 0
score + other // Addition
score - other // Subtraction
-score // Negation
Solver Configuration
SolverConfig
SolverConfig::new()
.with_solution_class("Schedule")
.with_entity_class("Shift")
.with_environment_mode(EnvironmentMode::Reproducible)
.with_random_seed(42)
.with_move_thread_count(MoveThreadCount::Auto)
.with_termination(termination)
TerminationConfig
TerminationConfig::new()
.with_spent_limit("PT5M")
.with_unimproved_spent_limit("PT30S")
.with_best_score_feasible(true)
.with_best_score_limit("0hard/-100soft")
.with_step_count_limit(1000)
.with_move_count_limit(10000)
WASM Expressions
Expr Builder
| Expression | Rust |
|---|
| Integer | Expr::int(42) |
| Boolean | Expr::bool(true) |
| Null | Expr::null() |
| Parameter | Expr::param(0) |
| Field access | expr.get("Class", "field") |
| Equal | Expr::eq(left, right) |
| Not equal | Expr::ne(left, right) |
| Less than | Expr::lt(left, right) |
| Greater than | Expr::gt(left, right) |
| AND | Expr::and(left, right) |
| OR | Expr::or(left, right) |
| NOT | Expr::not(expr) |
| Is null | Expr::is_null(expr) |
| Is not null | Expr::is_not_null(expr) |
| Add | Expr::add(left, right) |
| Subtract | Expr::sub(left, right) |
| Multiply | Expr::mul(left, right) |
| Divide | Expr::div(left, right) |
| List contains | Expr::list_contains(list, elem) |
| String equals | Expr::string_equals(left, right) |
| Ranges overlap | Expr::ranges_overlap(s1, e1, s2, e2) |
| If-then-else | Expr::if_then_else(cond, then, else) |
WasmModuleBuilder
WasmModuleBuilder::new()
.with_domain_model(model)
.with_host_functions(HostFunctionRegistry::with_standard_functions())
.with_initial_memory(16)
.with_max_memory(Some(256))
.add_predicate(PredicateDefinition::from_expression(name, arity, expr))
.build()? // Vec<u8>
.build_base64()? // String
2 - Error Handling
Handle SolverForgeError types and troubleshoot common issues
SolverForge uses the SolverForgeError enum for all error types.
SolverForgeError
use solverforge_core::{SolverForgeError, SolverForgeResult};
fn solve_problem() -> SolverForgeResult<String> {
// ... operations that may fail
Ok("solution".to_string())
}
match solve_problem() {
Ok(solution) => println!("Success: {}", solution),
Err(e) => eprintln!("Error: {}", e),
}
Error Variants
Serialization
JSON serialization/deserialization errors:
SolverForgeError::Serialization(String)
Common causes:
- Invalid JSON in problem data
- Malformed score strings
- Type mismatches
Example:
let score = HardSoftScore::parse("invalid")?;
// Error: Serialization error: Invalid HardSoftScore format...
Http
HTTP communication errors with the solver service:
SolverForgeError::Http(String)
Common causes:
- Service not running
- Network timeout
- Connection refused
Solver
Errors returned by the solver service:
SolverForgeError::Solver(String)
Common causes:
- Invalid constraint configuration
- WASM execution failure
- Memory allocation failure
WasmGeneration
Errors during WASM module generation:
SolverForgeError::WasmGeneration(String)
Common causes:
- Invalid expression tree
- Unknown field access
- Missing domain model
Bridge
Language binding bridge errors:
SolverForgeError::Bridge(String)
Common causes:
- Handle invalidation
- Type conversion failures
Validation
Domain model validation errors:
SolverForgeError::Validation(String)
Common causes:
- Missing
@PlanningSolution class - No
@PlanningEntity classes - Missing
@PlanningVariable on entities - Missing
@PlanningScore field
Example:
let model = DomainModel::builder()
.add_class(DomainClass::new("Shift")) // No annotations!
.build_validated()?;
// Error: Validation error: No @PlanningSolution class found
Configuration
Configuration errors:
SolverForgeError::Configuration(String)
Common causes:
- Invalid termination config
- Invalid environment mode
Service
Embedded service lifecycle errors:
SolverForgeError::Service(String)
Common causes:
- Java not found
- Service startup timeout
- Port already in use
Io
Standard I/O errors:
SolverForgeError::Io(std::io::Error)
Common causes:
- File not found
- Permission denied
Other
Generic errors:
SolverForgeError::Other(String)
Error Conversion
SolverForgeError automatically converts from common error types:
// From serde_json::Error
let err: SolverForgeError = serde_json::from_str::<i32>("bad")
.unwrap_err()
.into();
// From std::io::Error
let err: SolverForgeError = std::fs::read("nonexistent")
.unwrap_err()
.into();
Using SolverForgeResult
The type alias simplifies return types:
use solverforge_core::SolverForgeResult;
fn build_model() -> SolverForgeResult<DomainModel> {
let model = DomainModel::builder()
.add_class(/* ... */)
.build_validated()?; // Returns SolverForgeResult
Ok(model)
}
Error Handling Patterns
Match on Variants
match result {
Ok(solution) => { /* handle success */ }
Err(SolverForgeError::Validation(msg)) => {
eprintln!("Model validation failed: {}", msg);
}
Err(SolverForgeError::Http(msg)) => {
eprintln!("Service communication failed: {}", msg);
}
Err(e) => {
eprintln!("Other error: {}", e);
}
}
Propagate with ?
fn solve() -> SolverForgeResult<SolveResponse> {
let model = build_model()?;
let wasm = build_wasm(&model)?;
let response = send_request(&wasm)?;
Ok(response)
}
Convert to String
let error_message = format!("{}", error);
Troubleshooting
“Service not running”
Error: Http error: connection refused
Fix: Start the solver service:
let service = EmbeddedService::start(ServiceConfig::new())?;
“WASM generation failed”
Error: WasmGeneration error: Unknown class 'Shift'
Fix: Ensure domain model is set:
WasmModuleBuilder::new()
.with_domain_model(model) // Required!
“Validation error: No solution class”
Error: Validation error: No @PlanningSolution class found
Fix: Add PlanningSolution annotation:
DomainClass::new("Schedule")
.with_annotation(PlanningAnnotation::PlanningSolution)
Error: Serialization error: Invalid HardSoftScore format
Fix: Use correct format: "0hard/-5soft" not "0/-5"