Thursday, March 14, 2013

#22 - POM3 Code Release

POM3 is the name given to my adaptation of POM2 (2009).  Similarly, POM2 is an adaptation of the original in 2008, POM.  Named for its originating authors, Port, Olkov and Menzies, POM was a simulation of the Requirements Engineering process in Software Engineering.

The job of the Requirements Engineer is to decide what gets implemented in a software project.  Typically this is done via priorities, but the prioritization strategies differ.  Traditionally, the Requirements Engineer deployed a Plan-Based strategy in which all requirements were prioritized once at the beginning of the project and never altered throughout the course of development.  In the alternative Agile development strategy however, requirements get re-prioritized during every iteration of development.  The question which POM tried to answer, is "which strategy is better?"

To answer the question, POM had to simulate many software projects very quickly.  And secondly, it had to evaluate those simulations.  Formally, this problem fits into the realm of Search Based Software Engineering. POM had become a Single-Objective Problem.  For the originating authors, the search for answering which strategy is better was a manual one involving the plot of parameters given 1,000 trials, and graphs.  In other words, a very inefficient approach at eyeing the results.  The authors of POM2 however, deploy a search technique in the algorithm known as KEYS.  In either case, the results are similar.  Plan-Based strategy is good in some cases, when requirements are stable, but Agile is better for when requirements are volatile and prone to change throughout development.

For more information, refer to references section.  [1] is the POM paper, and [2] is the POM2 paper.

POM3 however is no longer a Single-Objective Problem and is no longer a simulation intended only to focus on Requirements Engineering prioritization strategies, but rather a general software project simulation used to control our additional objectives in Cost, Idle and Completion (with Score).  Below, we discuss both the decisions and objectives of POM3 and then give an outline of the code, followed by some samples of the code.

POM3 Decisions

Decisions are those inputs to the POM3 simulation.  They are as follows.

  1. Culture.  The percentage of personnel on the project that thrive on chaos.  Some projects are chaotic.  That is, a lot of requirements change and are unstable, i.e. volatile.
  2. Criticality.  The impact to cost that safety-critical systems have upon projects.  Some projects have safety-critical requirements.  That is, some requirements are safety-critical in the event that failure absolutely cannot happen for the safety of human lives.
  3. Criticality Modifier.  The percentage of teams that are dealing with safety-critical parts of the project.
  4. Dynamism.  How dynamic is the project?  This affects the rate at which new requirements are found.
  5. Interdependency.  A percentage of how many requirements depend on other requirements (assigned to other teams in the project).
  6. Initial Known.  The percentage of the project that is initially known.  In the real world, we just don't know this, but we can guess.  In software world, all requirements exist, but some are marked as hidden.
  7. Team Size.  The size of teams, in terms of how many personnel are on each team.
  8. Size.  The project size.  Very small, small, medium, large, very large.
  9. Plan.  The requirements prioritization strategy.  We provide 5 different schemes.

POM3 Objectives

Objectives are the outputs of the POM3 simulation that score how well the project was finished.

  1. Score.  A score indicating the performance of the project's simulation.
  2. Cost.  The average cost per completed requirement across all teams.
  3. Idle.  The overall percentage of idleness of the project's teams.
  4. Completion.  The overall percentage of how many visible requirements were completed.

POM3 Algorithm

The outline of the POM3 simulation is as follows.  We avoid core details while providing the code.

  1. Initialization.
  2. Requirements Generation.
  3. Teams Generation.
  4. Shuffling.
    • For Each Team, Do: 
      1. Assess Budget Information
      2. Collect Available Tasks
      3. Apply Sorting Strategy
      4. Execute Available Tasks
      5. Discover New Tasks
      6. Update Task Priorities
  5. Scoring.
This outline is contained in the pom3.py file.  The initialization step processes the inputs by containing them in a pom3_decisions class structure.  The number of shuffling iterations is determined.  And then we move on to step 2, where requirements are generated into a heap.  The code for this is handled in pom3_requirements.py, which further makes use of the pom3_requirements_tree.py file.  In step 3, we generate teams and assign parts of the requirements heap to each team.  The code that manages the assembly of teams is in pom3_teams.py, and the code that manages each team individually is in pom3_team.py.

Finally, shuffling occurs.  Only a few iterations of shuffling occurs, as determined back in the initialization step.  In each iteration, requirements are shuffled through each team over a series of steps.  At first, the team assesses their budget information and collects the money that is allocated to them based on their workload.  Secondly, the team collects any tasks that they can possibly work on.  Thirdly, they sort through these available tasks, according to some sorting strategy (requirements prioritization strategy).  Remember when I said Plan-Based strategies don't do this every iteration?  Well, in POM3 something happens during every iteration, so we're ignoring the traditional strategy for the sake of simplicity.  But it would be relatively simple to reintroduce it properly.  The reason we ignore it, is because quite simply, we don't care too much about it.

The fourth step of shuffling involves the execution of available tasks as possible, within budget limitations as assessed in step one of shuffling.  Fifthly, we look to discovering new tasks; which basically, all this means is marking some more requirements of the heap as visible which were previously invisible.  Finally, tasks are all updated on their 'values', i.e. priorities.  By the way, the code for each shuffling step is all contained in the pom3_team.py file, as all the work here is for each team individually.

Lastly, now that the simulation is finished, we score our objectives.  Score is the funkiest objective, as it considers each completed task's final priority value, as updated in every iteration as the optimal_value.  The true value is marked as well as they are completed in real time, before they are further updated via the last shuffling phase.  As a ratio, the score is calculated as frontier/optimal_frontier, where frontier = value/cost, and optimal_frontier = optimal_value / optimal_cost.  In the code, we refer to frontier as "our frontier", and the optimal frontier as the "god frontier", as it is information only a god-like entity could possibly know (we don't actually update the completed tasks in real life; but they get considered in the simulation for scoring purposes).

Extra

By the way, here's some fun python stuff.

class pom3_decisions:
    def __init__(p3d, X):
        p3d.culture = X[0]
        p3d.criticality = X[1]
        p3d.criticality_modifier = X[2]
        p3d.initial_known = X[3]
        p3d.interdependency = X[4]
        p3d.dynamism = X[5]
        p3d.size = int(X[6])
        p3d.plan = int(X[7])
        p3d.team_size = X[8]

The code above is a sample from POM3.  A simple class structure makes any instance of the pom3_decisions class an effective record of sorts.  When one instances the pom3_decisions class, they are making use of the class constructor (def __init__).  Now, the cool part is that unlike languages such as java, I can give my own name to the "this" reference.  In the sample above, I use the name p3d, and assign class data using it.  This is no different than using python's default "this" name, which is "self".  But sometimes, just sometimes - you can make the code look like syntactic candy with just the right name.

class pom3_requirements:
    def __init__(requirements, decisions):
        requirements.heap = requirements_tree()
        requirements.count = int(2.5*[3,10,30,100,300][decisions.size])
        requirements.decisions = decisions
        
        for i in range(requirements.count):
            
            ...
            
            parent = requirements.heap.tree[i]
            requirements.recursive_adder(parent, 1)

A heap is a bunch of trees crammed together.  Requirements.heap.tree[i] accesses the i'th tree of the heap.  Looks just like English.  But it's not, it's Python!

References

[1] D. Port, A. Olkov, and T. Menzies, “Using simulation to investigate requirements prioritization strategies,” in Automated Software Engineering, 2008. ASE 2008. 23rd IEEE/ACM International Conference on, Sept. 2008, pp. 268–277.
[2] Lemon, B.; et al., "Applications of Simulation and AI Search: Assessing the Relative Merits of Agile vs Traditional Software Development," in Automated Software Engineering, 2009. ASE 2009. 24th IEEE/ACM International Conference on, Nov. 2009, pp.580-584.

No comments:

Post a Comment