Skip to content

Commit

Permalink
Use tuple struct type for violation code
Browse files Browse the repository at this point in the history
  • Loading branch information
reinterpretcat committed Jul 26, 2024
1 parent aad9c87 commit cec76c3
Show file tree
Hide file tree
Showing 35 changed files with 142 additions and 104 deletions.
9 changes: 5 additions & 4 deletions vrp-core/src/construction/clustering/vicinity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ use std::sync::Arc;
mod estimations;
use self::estimations::*;
use crate::models::solution::Commute;
use crate::prelude::ViolationCode;

custom_dimension!(ClusterInfo typeof Vec<ClusterInfo>);

/// Holds center job and its neighbor jobs.
pub type ClusterCandidate<'a> = (&'a Job, &'a HashSet<Job>);

type CheckInsertionFn = (dyn Fn(&Job) -> Result<(), i32> + Send + Sync);
type CheckInsertionFn = (dyn Fn(&Job) -> Result<(), ViolationCode> + Send + Sync);

/// Specifies clustering algorithm configuration.
#[derive(Clone)]
Expand Down Expand Up @@ -163,8 +164,8 @@ pub fn create_job_clusters(
fn get_check_insertion_fn(
insertion_ctx: InsertionContext,
actor_filter: Arc<dyn Fn(&Actor) -> bool + Send + Sync>,
) -> impl Fn(&Job) -> Result<(), i32> {
move |job: &Job| -> Result<(), i32> {
) -> impl Fn(&Job) -> Result<(), ViolationCode> {
move |job: &Job| -> Result<(), ViolationCode> {
let eval_ctx = EvaluationContext {
goal: &insertion_ctx.problem.goal,
job,
Expand All @@ -177,7 +178,7 @@ fn get_check_insertion_fn(
.registry
.next_route()
.filter(|route_ctx| (actor_filter)(&route_ctx.route().actor))
.try_fold(Err(-1), |_, route_ctx| {
.try_fold(Err(ViolationCode::unknown()), |_, route_ctx| {
let result = eval_job_insertion_in_route(
&insertion_ctx,
&eval_ctx,
Expand Down
5 changes: 3 additions & 2 deletions vrp-core/src/construction/heuristics/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::models::problem::*;
use crate::models::solution::*;
use crate::models::GoalContext;
use crate::models::{Problem, Solution};
use crate::prelude::ViolationCode;
use rosomaxa::evolution::TelemetryMetrics;
use rosomaxa::prelude::*;
use rustc_hash::FxHasher;
Expand Down Expand Up @@ -117,9 +118,9 @@ pub enum UnassignmentInfo {
/// No code is available.
Unknown,
/// Only single code is available.
Simple(i32),
Simple(ViolationCode),
/// A collection of actor-code pairs is available.
Detailed(Vec<(Arc<Actor>, i32)>),
Detailed(Vec<(Arc<Actor>, ViolationCode)>),
}

/// Contains information regarding discovered solution.
Expand Down
11 changes: 6 additions & 5 deletions vrp-core/src/construction/heuristics/evaluators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::sync::Arc;
use crate::construction::heuristics::*;
use crate::models::problem::{Job, Multi, Single};
use crate::models::solution::{Activity, Leg, Place};
use crate::models::{ConstraintViolation, GoalContext};
use crate::models::{ConstraintViolation, GoalContext, ViolationCode};
use crate::utils::Either;

/// Specifies an evaluation context data.
Expand Down Expand Up @@ -145,7 +145,7 @@ fn eval_single(
let activities = vec![(activity, result.index)];
InsertionResult::make_success(result.cost.unwrap(), job, activities, route_ctx)
} else {
let (code, stopped) = result.violation.map_or((0, false), |v| (v.code, v.stopped));
let (code, stopped) = result.violation.map_or((ViolationCode::unknown(), false), |v| (v.code, v.stopped));
InsertionResult::make_failure_with_code(code, stopped, Some(job))
}
}
Expand Down Expand Up @@ -218,7 +218,7 @@ fn eval_multi(
let activities = result.activities.unwrap();
InsertionResult::make_success(result.cost.unwrap(), job, activities, route_ctx)
} else {
let (code, stopped) = result.violation.map_or((0, false), |v| (v.code, v.stopped));
let (code, stopped) = result.violation.map_or((ViolationCode::unknown(), false), |v| (v.code, v.stopped));
InsertionResult::make_failure_with_code(code, stopped, Some(job))
}
}
Expand Down Expand Up @@ -420,8 +420,9 @@ impl MultiContext {

/// Creates failed insertion context within reason code.
fn fail(err_ctx: SingleContext, other_ctx: MultiContext) -> ControlFlow<Self, Self> {
let (code, stopped) =
err_ctx.violation.map_or((0, false), |v| (v.code, v.stopped && other_ctx.activities.is_none()));
let (code, stopped) = err_ctx
.violation
.map_or((ViolationCode::unknown(), false), |v| (v.code, v.stopped && other_ctx.activities.is_none()));

ControlFlow::Break(Self {
violation: Some(ConstraintViolation { code, stopped }),
Expand Down
6 changes: 3 additions & 3 deletions vrp-core/src/construction/heuristics/insertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,11 @@ impl InsertionResult {

/// Creates result which represents insertion failure.
pub fn make_failure() -> Self {
Self::make_failure_with_code(-1, false, None)
Self::make_failure_with_code(ViolationCode::unknown(), false, None)
}

/// Creates result which represents insertion failure with given code.
pub fn make_failure_with_code(code: i32, stopped: bool, job: Option<Job>) -> Self {
pub fn make_failure_with_code(code: ViolationCode, stopped: bool, job: Option<Job>) -> Self {
Self::Failure(InsertionFailure { constraint: code, stopped, job })
}

Expand All @@ -327,7 +327,7 @@ impl InsertionResult {
}
}
(Self::Failure(_), Self::Failure(rhs)) => {
if rhs.constraint == -1 {
if rhs.constraint == ViolationCode::unknown() {
left
} else {
right
Expand Down
29 changes: 27 additions & 2 deletions vrp-core/src/models/goal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rosomaxa::population::Shuffled;
use rosomaxa::prelude::*;
use std::cmp::Ordering;
use std::collections::HashSet;
use std::fmt::{Debug, Formatter};
use std::fmt::{Debug, Display, Formatter};
use std::ops::ControlFlow;
use std::sync::Arc;

Expand Down Expand Up @@ -300,7 +300,32 @@ impl ConstraintViolation {
}

/// Specifies a type for constraint violation code.
pub type ViolationCode = i32;
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq)]
pub struct ViolationCode(pub i32);

impl ViolationCode {
/// Returns an unknown violation code.
pub fn unknown() -> Self {
Self(-1)
}

/// Checks whether violation code is unknown.
pub fn is_unknown(&self) -> bool {
self.0 == -1
}
}

impl From<i32> for ViolationCode {
fn from(value: i32) -> Self {
Self(value)
}
}

impl Display for ViolationCode {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("{}", self.0))
}
}

/// Provides a way to build feature with some checks.
#[derive(Default)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,9 @@ impl InsertionEvaluator for SkipBestInsertionEvaluator {
(
InsertionResult::Failure(InsertionFailure { constraint: left, .. }),
InsertionResult::Failure(InsertionFailure { constraint: right, .. }),
) => match (left, right) {
(-1, _) => Greater,
(_, -1) => Less,
) => match (left.is_unknown(), right.is_unknown()) {
(true, _) => Greater,
(_, true) => Less,
_ => Equal,
},
});
Expand Down
2 changes: 1 addition & 1 deletion vrp-core/src/solver/search/redistribute_search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl FeatureConstraint for RedistributeFeatureConstraint {
match move_ctx {
MoveContext::Route { route_ctx, job, .. } => self.rules.get(*job).and_then(|actor| {
if actor == &route_ctx.route().actor {
Some(ConstraintViolation { code: 0, stopped: true })
Some(ConstraintViolation { code: ViolationCode::default(), stopped: true })
} else {
None
}
Expand Down
4 changes: 2 additions & 2 deletions vrp-core/tests/helpers/construction/clustering/vicinity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ impl FeatureConstraint for VicinityTestFeatureConstraint {
match move_ctx {
MoveContext::Route { job, .. } => {
if self.disallow_merge_list.contains(job.dimens().get_job_id().unwrap()) {
ConstraintViolation::fail(1)
ConstraintViolation::fail(ViolationCode(1))
} else {
None
}
Expand All @@ -50,7 +50,7 @@ impl FeatureConstraint for VicinityTestFeatureConstraint {

fn merge(&self, source: Job, candidate: Job) -> Result<Job, ViolationCode> {
if self.disallow_merge_list.contains(candidate.dimens().get_job_id().unwrap()) {
Err(1)
Err(ViolationCode(1))
} else {
let source = source.to_single();
assert_eq!(source.places.len(), 1);
Expand Down
9 changes: 3 additions & 6 deletions vrp-core/tests/helpers/models/domain.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
use crate::construction::features::TransportFeatureBuilder;
use crate::construction::heuristics::*;
use crate::helpers::models::problem::{test_fleet, TestActivityCost, TestTransportCost};
use crate::models::problem::{Fleet, Job, JobIdDimension, Jobs};
use crate::models::{Extras, Feature, FeatureBuilder, GoalContext, GoalContextBuilder, Problem};
use crate::prelude::{Cost, FeatureObjective};
use rosomaxa::utils::{DefaultRandom, Random};
use crate::models::problem::JobIdDimension;
use crate::prelude::*;
use std::sync::Arc;

pub fn test_random() -> Arc<dyn Random + Send + Sync> {
Expand Down Expand Up @@ -35,7 +32,7 @@ impl TestGoalContextBuilder {
pub fn with_transport_feature() -> Self {
Self {
features: vec![TransportFeatureBuilder::new("transport")
.set_violation_code(1)
.set_violation_code(ViolationCode(1))
.set_transport_cost(TestTransportCost::new_shared())
.set_activity_cost(TestActivityCost::new_shared())
.build_minimize_cost()
Expand Down
6 changes: 3 additions & 3 deletions vrp-core/tests/helpers/solver/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn generate_matrix_routes_with_defaults(rows: usize, cols: usize, is_open_vr
TestGoalContextBuilder::default()
.add_feature(
TransportFeatureBuilder::new("transport")
.set_violation_code(1)
.set_violation_code(ViolationCode(1))
.set_transport_cost(transport)
.set_activity_cost(activity)
.build_minimize_cost()
Expand Down Expand Up @@ -72,7 +72,7 @@ pub fn generate_matrix_routes_with_disallow_list(
TestGoalContextBuilder::empty()
.add_feature(
TransportFeatureBuilder::new("transport")
.set_violation_code(1)
.set_violation_code(ViolationCode(1))
.set_transport_cost(transport)
.set_activity_cost(activity)
.build_minimize_cost()
Expand Down Expand Up @@ -229,7 +229,7 @@ impl FeatureConstraint for LegFeatureConstraint {
});

if is_disallowed {
ConstraintViolation::skip(7)
ConstraintViolation::skip(ViolationCode(7))
} else {
None
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ fn get_check_insertion_fn(disallow_insertion_list: Vec<&str>) -> Arc<CheckInsert
let id = job_to_check.dimens().get_job_id();

if id.map_or(false, |id| disallow_insertion_list.contains(id)) {
Err(-1)
Err(ViolationCode::unknown())
} else {
Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn can_get_check_insertion() {
let check_insertion = get_check_insertion_fn(insertion_ctx, actor_filter);

assert_eq!(check_insertion(jobs.first().unwrap()), Ok(()));
assert_eq!(check_insertion(jobs.get(1).unwrap()), Err(1));
assert_eq!(check_insertion(jobs.get(1).unwrap()), Err(ViolationCode(1)));
}

#[test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::helpers::models::solution::*;
use crate::models::problem::*;
use crate::models::{Feature, ViolationCode};

const VIOLATION_CODE: ViolationCode = 1;
const VIOLATION_CODE: ViolationCode = ViolationCode(1);

type VehicleData = (Location, Location, Timestamp, Timestamp);
type ActivityData = (Location, (Timestamp, Timestamp), Duration);
Expand Down
4 changes: 2 additions & 2 deletions vrp-core/tests/unit/construction/features/breaks_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::models::problem::Job;
use crate::models::problem::Single;
use std::sync::Arc;

const VIOLATION_CODE: ViolationCode = 1;
const VIOLATION_CODE: ViolationCode = ViolationCode(1);

struct JobTypeDimenKey;
struct VehicleIdDimenKey;
Expand Down Expand Up @@ -101,7 +101,7 @@ can_skip_merge_breaks! {
case_03: (create_single("source", 0), create_single("candidate", 1), Ok(())),
}

fn can_skip_merge_breaks_impl(source: Job, candidate: Job, expected: Result<(), i32>) {
fn can_skip_merge_breaks_impl(source: Job, candidate: Job, expected: Result<(), ViolationCode>) {
let feature = create_break_feature();

let result = feature.constraint.unwrap().merge(source, candidate).map(|_| ());
Expand Down
9 changes: 5 additions & 4 deletions vrp-core/tests/unit/construction/features/capacity_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::models::common::{Demand, SingleDimLoad};
use crate::models::problem::{Job, Vehicle};
use crate::models::solution::Activity;

const VIOLATION_CODE: ViolationCode = 2;
const VIOLATION_CODE: ViolationCode = ViolationCode(2);

fn create_feature() -> Feature {
CapacityFeatureBuilder::<SingleDimLoad>::new("capacity").set_violation_code(VIOLATION_CODE).build().unwrap()
Expand Down Expand Up @@ -178,8 +178,9 @@ fn can_merge_jobs_with_demand_impl(
});
let constraint = create_feature().constraint.unwrap();

let result: Result<Demand<SingleDimLoad>, i32> =
constraint.merge(cluster, candidate).and_then(|job| job.dimens().get_job_demand().cloned().ok_or(-1));
let result: Result<Demand<SingleDimLoad>, ViolationCode> = constraint
.merge(cluster, candidate)
.and_then(|job| job.dimens().get_job_demand().cloned().ok_or(ViolationCode::unknown()));

match (result, expected) {
(Ok(result), Ok((pickup0, pickup1, delivery0, delivery1))) => {
Expand All @@ -190,6 +191,6 @@ fn can_merge_jobs_with_demand_impl(
}
(Ok(_), Err(err)) => unreachable!("unexpected ok, when err '{}' expected", err),
(Err(err), Ok(_)) => unreachable!("unexpected err: '{}'", err),
(Err(result), Err(expected)) => assert_eq!(result, expected),
(Err(ViolationCode(result)), Err(expected)) => assert_eq!(result, expected),
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::helpers::models::problem::TestSingleBuilder;
use crate::helpers::models::solution::{ActivityBuilder, RouteBuilder, RouteContextBuilder, RouteStateBuilder};
use std::sync::Arc;

const VIOLATION_CODE: i32 = 1;
const VIOLATION_CODE: ViolationCode = ViolationCode(1);
const DEFAULT_JOB_LOCATION: Location = 1;

fn create_feature() -> Feature {
Expand Down Expand Up @@ -103,7 +103,7 @@ can_merge_jobs! {
fn can_merge_jobs_impl(
source_compat: Option<&str>,
candidate_compat: Option<&str>,
expected: Result<Option<String>, i32>,
expected: Result<Option<String>, ViolationCode>,
) {
let source = Job::Single(create_test_single(source_compat.map(|v| v.to_string())));
let candidate = Job::Single(create_test_single(candidate_compat.map(|v| v.to_string())));
Expand Down
6 changes: 3 additions & 3 deletions vrp-core/tests/unit/construction/features/groups_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::models::solution::Registry;
use std::collections::HashMap;
use std::sync::Arc;

const VIOLATION_CODE: ViolationCode = 1;
const VIOLATION_CODE: ViolationCode = ViolationCode(1);

fn create_test_group_feature(total_jobs: usize) -> Feature {
create_group_feature("group", total_jobs, VIOLATION_CODE).unwrap()
Expand Down Expand Up @@ -168,7 +168,7 @@ fn can_evaluate_job_impl(
routes: Vec<(&str, Vec<Option<&str>>)>,
route_idx: usize,
job_group: Option<&str>,
expected: Option<i32>,
expected: Option<ViolationCode>,
) {
let total_jobs = get_total_jobs(&routes) + 1;
let fleet = create_test_fleet();
Expand All @@ -194,7 +194,7 @@ can_merge_groups! {
case_05: (create_test_single(None), create_test_single(None), Ok(())),
}

fn can_merge_groups_impl(source: Job, candidate: Job, expected: Result<(), i32>) {
fn can_merge_groups_impl(source: Job, candidate: Job, expected: Result<(), ViolationCode>) {
let total_jobs = 1;
let constraint = create_test_group_feature(total_jobs).constraint.unwrap();

Expand Down
4 changes: 2 additions & 2 deletions vrp-core/tests/unit/construction/features/locked_jobs_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::models::solution::Activity;
use crate::models::{Lock, LockDetail, LockOrder, LockPosition};
use std::sync::Arc;

const VIOLATION_CODE: ViolationCode = 1;
const VIOLATION_CODE: ViolationCode = ViolationCode(1);

fn create_feature_constraint(fleet: &Fleet, locks: &[Arc<Lock>]) -> Arc<dyn FeatureConstraint> {
create_locked_jobs_feature("locked_jobs", fleet, locks, VIOLATION_CODE).unwrap().constraint.unwrap()
Expand Down Expand Up @@ -47,7 +47,7 @@ fn can_lock_jobs_to_actor_impl(used: String, locked: String, expected: Option<Co
}

fn stop() -> Option<ConstraintViolation> {
Some(ConstraintViolation { code: 1, stopped: false })
Some(ConstraintViolation { code: ViolationCode(1), stopped: false })
}

fn some_activity() -> Activity {
Expand Down
2 changes: 1 addition & 1 deletion vrp-core/tests/unit/construction/features/recharge_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::helpers::models::problem::*;
use crate::helpers::models::solution::{ActivityBuilder, RouteBuilder, RouteContextBuilder};
use crate::models::solution::Activity;

const VIOLATION_CODE: ViolationCode = 1;
const VIOLATION_CODE: ViolationCode = ViolationCode(1);

struct VehicleIdDimenKey;
struct JobTypeDimenKey;
Expand Down
Loading

0 comments on commit cec76c3

Please sign in to comment.