Skip to content

Commit

Permalink
chore(jssp): mostly document structures used to model JSSP (#426)
Browse files Browse the repository at this point in the history
- Inline calls in IndividualTrait impl for JsspIndividual
- Comments in individual & machine impl
- Use Option for operation finish time
- Add comments to Edge, EdgeKind & Operation
- Extend comments on Operation
- Add more comments in problem module
- Comments in population module

<!-- If applicable - remember to add the PR to the EA Rust project (ONLY
IF THERE IS NO LINKED ISSUE) -->

## Description

<!-- Please describe the motivation & changes introduced by this PR -->

## Linked issues

<!-- Please use "Resolves #<issue_no> syntax in case this PR should be
linked to an issue -->

## Important implementation details

<!-- if any, optional section -->
  • Loading branch information
kkafar authored Oct 8, 2023
1 parent 74ee3a0 commit 436b8e2
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 12 deletions.
2 changes: 1 addition & 1 deletion examples/jssp/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ impl TryFrom<&PathBuf> for JsspInstance {
.for_each(|op_def| {
jobs.last_mut().unwrap().push(Operation::new(
op_id,
usize::MAX,
op_def[1].parse().unwrap(),
op_def[0].parse().unwrap(),
None,
Vec::from_iter(first_job_in_batch..op_id),
));
op_id += 1;
Expand Down
68 changes: 62 additions & 6 deletions examples/jssp/problem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,21 @@ pub mod population;
pub mod probe;
pub mod replacement;

/// Describes relation between two operations
#[derive(Debug, Clone, PartialEq, Eq, Copy)]
pub enum EdgeKind {
/// Operation that the edge points to is from the same job (ops are on different machines)
JobSucc,
/// Operation that the edge points to is on the same machine (ops are from different jobs)
MachineSucc,
}

/// Models the edge in neighbourhood graph where operations are nodes
#[derive(Debug, Clone, Copy)]
pub struct Edge {
/// Unique id of the neighbour operation
pub neigh_id: usize,
/// Describes the relation between the operations
pub kind: EdgeKind,
}

Expand All @@ -28,27 +34,57 @@ impl Edge {
}
}

/// Models Operation that is a part of some job
///
/// TODO: Cleanup this struct.
/// 1. Move all data non-intrinsic to the Operation model to separate structs
/// 2. `critical_distance` should be an Option
#[derive(Debug, Clone)]
pub struct Operation {
/// Unique id of this operation
id: usize,
finish_time: usize,
/// Duration of the operation
duration: usize,
/// Machine this operation is assigned to
machine: usize,

/// Finish time tick of this operation as determined by the solver. The value of this field
/// is modified during the algorithm run
finish_time: Option<usize>,
/// Ids of all ops that this op depends on. TODO: Was the order guaranteed?
preds: Vec<usize>,
/// Edges describing relations to other ops in neighbourhood graph. It contains *at most* two elements
/// as each op might have at most two successors: next operation in the job or next operation on the same machine
/// this op is executed on. The value of this field is modified as the algorithm runs
edges_out: Vec<Edge>,
/// Operation id of direct machine predecessor of this op. This might be `None` in following scenarios:
/// 1. Op is the first op on particular machine TODO: I'm not sure now, whether I set op no. 0 as machine predecessor
/// of every first op on given machine or not, so please verify it before using this fact.
/// 2. This is op with id 0
///
/// The value of this field is modified as the algorithm runs.
machine_pred: Option<usize>,
/// If this operation lies on critical path in neighbourhood graph (as defined in paper by Nowicki & Smutnicki)
/// this is the edge pointing to next op on critical path, if there is one - this might be the last operation
/// or simply not on the path. The value of this field is modified as the algorithm runs.
critical_path_edge: Option<Edge>,
/// If this operation lies on critical path this field is used by the local search algorithm to store
/// distance from this op to the sink node. The value of this field is modified as the algorithm runs.
critical_distance: usize,
}

impl Operation {
pub fn new(id: usize, finish_time: usize, duration: usize, machine: usize, preds: Vec<usize>) -> Self {
pub fn new(
id: usize,
duration: usize,
machine: usize,
finish_time: Option<usize>,
preds: Vec<usize>,
) -> Self {
Self {
id,
finish_time,
duration,
machine,
finish_time,
preds,
edges_out: Vec::new(),
machine_pred: None,
Expand All @@ -57,9 +93,12 @@ impl Operation {
}
}

/// Resets the state of the operation so that this object can be reused to find new solution
pub fn reset(&mut self) {
self.finish_time = usize::MAX;
self.finish_time = None;
self.machine_pred = None;
// Job edges are determined by the problem instance we consider, while machine edges
// are determined by the scheduling process
if let Some(edge_to_rm) = self
.edges_out
.iter()
Expand All @@ -77,14 +116,16 @@ impl Operation {
}
}

/// Models the machine -- when it is occupied
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct Machine {
/// Unique id of the machine
id: usize,

// For naive implementation
// rmc: Vec<usize>,

/// Remaining machine capacity. If a range is added -> this means that the machine is occupied in that range
// For "possibly better implementation"
rmc: Vec<Range<usize>>,
pub last_scheduled_op: Option<usize>,
Expand Down Expand Up @@ -124,27 +165,42 @@ impl Machine {
self.last_scheduled_op = Some(op);
}

/// Removes all ranges from the machine state allowing instance of this type to be reused
pub fn reset(&mut self) {
self.rmc.clear();
self.last_scheduled_op = None;
}
}

/// Basic information (metadata) about the jssp instance.
#[derive(Debug, Clone)]
pub struct JsspConfig {
/// Total number of jobs. Note that the job/operation naming/meaning is not consistent.
/// TODO: Unify this so that job is a ordered set of operations.
pub n_jobs: usize,
/// Total number of machines in this problem instance
pub n_machines: usize,
/// Total number of operations. Note that the job/operation naming/meaning is not consistent across
/// the codebase (but also in article...)
pub n_ops: usize,
}

#[derive(Debug, Clone)]
pub struct JsspInstanceMetadata {
/// Name of the instance. In case the instance was loaded from the disk,
/// the `name` should be related to the data file name.
pub name: String,
}

/// Describes single JSSP problem instance.
/// Instance is modeled as a set of jobs.
/// Each job is modeled as a set of operations.
/// Operations have precedency relation estabilished
/// and each operation is assigned to a particular machine.
#[derive(Debug, Clone)]
pub struct JsspInstance {
pub jobs: Vec<Vec<Operation>>,
pub cfg: JsspConfig,
// TODO: I should merge Instance metadata with config
pub metadata: JsspInstanceMetadata,
}
21 changes: 19 additions & 2 deletions examples/jssp/problem/individual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,24 @@ use log::{debug, info, trace, warn};

use super::{Edge, EdgeKind, Machine, Operation};

/// Models single solution to the JSSP problem instance
#[derive(Debug, Clone)]
pub struct JsspIndividual {
/// Encoding of the solution. This can be decoded to the proper solution
pub chromosome: Vec<f64>,
/// Clone of all operations from the problem instance
pub operations: Vec<Operation>,
/// Clone of all machines from the problem instance
pub machines: Vec<Machine>,
/// If computed - fitness value of this solution. Check `is_fitness_valid`
/// property to determine whether this value is up to date
/// This is not an Option for some practical reasons
/// TODO: But this should be an Option or some enum with additional information
pub fitness: usize,
/// If `true` the `fitness` field holds the value for the current `chromosome`
/// and does need to be recomputed. This must be kept in sync!
pub is_fitness_valid: bool,
/// TODO: Determine what I've used it for
is_dirty: bool,
}

Expand Down Expand Up @@ -247,7 +258,7 @@ impl JsspIndividual {

scheduled.insert(0);
finish_times[0] = 0;
self.operations[0].finish_time = 0;
self.operations[0].finish_time = Some(0);

let mut g = 1;
let mut t_g = 0;
Expand Down Expand Up @@ -277,7 +288,7 @@ impl JsspIndividual {
let op_j_machine = self.operations[j].machine;
let op_j = &self.operations[j];

// Calculate earliset finish time (in terms of precedence only)
// Calculate the earliest finish time (in terms of precedence only)
let pred_j_finish = op_j
.preds
.iter()
Expand Down Expand Up @@ -331,6 +342,7 @@ impl JsspIndividual {
makespan
}

/// Resets all machines & operations associated with this individual
fn reset(&mut self) {
self.machines.iter_mut().for_each(|machine| machine.reset());
self.operations.iter_mut().for_each(|op| op.reset());
Expand Down Expand Up @@ -361,22 +373,27 @@ impl IndividualTrait for JsspIndividual {
type ChromosomeT = Vec<f64>;
type FitnessValueT = usize;

#[inline]
fn chromosome(&self) -> &Self::ChromosomeT {
&self.chromosome
}

#[inline]
fn chromosome_mut(&mut self) -> &mut Self::ChromosomeT {
&mut self.chromosome
}

#[inline]
fn fitness(&self) -> Self::FitnessValueT {
self.fitness
}

#[inline]
fn fitness_mut(&mut self) -> &mut Self::FitnessValueT {
&mut self.fitness
}

#[inline]
fn requires_evaluation(&self) -> bool {
!self.is_fitness_valid
}
Expand Down
9 changes: 6 additions & 3 deletions examples/jssp/problem/population.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ pub struct JsspPopProvider {

impl JsspPopProvider {
pub fn new(mut instance: JsspInstance) -> Self {
// Finding dimension of the chromosome
// Finding dimension of the chromosome -- total number of operations (later multiplied)
let dim: usize = instance.jobs.iter().map(|job| job.len()).sum();

// Prepare mock operations
// TODO: Shouldn't the duration be set to 0?
let mut zero_op = Operation::new(0, usize::MAX, 0, None, Vec::new());
let sink_op = Operation::new(dim + 1, usize::MAX, 0, None, Vec::from_iter(0..=dim));

// Shift all ids by 1 && and job 0 & n + 1
let mut zero_op = Operation::new(0, usize::MAX, 0, 0, Vec::new());
let sink_op = Operation::new(dim + 1, usize::MAX, 0, 0, Vec::from_iter(0..=dim));
instance.jobs.iter_mut().for_each(|job| {
job.iter_mut().for_each(|op| {
op.id += 1;
Expand Down

0 comments on commit 436b8e2

Please sign in to comment.