880 likes | 1k Views
Project Summary. Develop methodology and framework to automatically compose software modules Target evolutionary computation as an algorithm to search for optimal compositions (1) Identify points of automation (2) Develop appropriate genome and fitness criteria
E N D
Project Summary • Develop methodology and framework to automatically compose software modules • Target evolutionary computation as an algorithm to search for optimal compositions • (1) Identify points of automation • (2) Develop appropriate genome and fitness criteria • (3) Implement within EC framework
Agenda • Working Example • CompositionTechniques • Key Milestones • Genome Structure • OpenBEAGLE Puppy
Working Example • Attempt to find an open-source problem that we can tackle • Should be amenable to composition • Modularized • Well defined • Based in ANSI-C
Working Example • Started with FTP implementations • Quickly determined that this was too complex to start • Looked at several open-source FTP client/server implementations • Too many dependencies and interactions for creation of a generic genome • Opening / closing socket connections • Tracking a complex data flow • Will be a better example further down the road
Working Example • Sorting algorithms • Targeting three different types of sort • Bubble sort • Merge sort • Quick sort • Ideal candidate for composition • e.g. Compose in merge sort to replace bubble sort functionality • Identify key concepts and extend for problems of higher complexity
Working Example • Example of composition with sorting methods • “Compose” merge sort into bubble sort program • Three factors • How to get data in • How to invoke new functionality (bubble sort) • How to get data out
Working Example • Approach(es) • Compare function prototypes • Leverage method signatures, pre- and post-conditions • Structural compatibility • Do prototypes match in some fashion? • Can we translate source to target post-conditions? • Software engineering metrics • Intrusiveness, lines of code, etc.
Agenda • Working Example • Composition Techniques • Key Milestones • Genome Structure • OpenBEAGLE Puppy
Composition Techniques • The following slides discuss composition techniques, approaches, and how to do it by hand • We cannot automate what we do not understand
Composition Techniques • Standard (OOP) definition: • Class has-a relationship with another class • Car has-a carburetor (1:1) • Lake has-aduck* (0:many) • Generic programhas-asorting algorithm* • When parent class is destroyed, composed classes are as well • Aggregation does not destroy child classes • In C++, achieve through inheritance, templates, design patterns, etc. • Provides capabilities for software reuse • Compose software modules together
Composition Techniques • Develop a taxonomy of possible compositions • Large breadth of methods (design patterns) in OOP • Adapter pattern • Implements/inherits both expected and pre-existing interface • Class / method stereotyping • Abstraction of class’ role/responsibility in a system • Automatically understanding purpose and parameters of class/method • Could be leveraged in order to generate mappings between source and target modules
Composition Techniques • Common operations / strategies (non-OOP) • Code injection • Directly inject code into existing method • Function pointer • Utilize pointer to hijack function call • Wrapper • Add a function that encapsulates new method invocation • At the ANSI-C level it is possible, but not easy, to utilize OOP paradigms • Presently, we will focus on the above methods
Composition Techniques • Code injection (in this case, into a struct) struct Person { int age; char *name; }; Suppose we want to represent a Person’s gender
Composition Techniques • Code injection (in this case, into a struct) struct Person { int age; char *name; enum{ male, female } gender; }; Person has-a gender
Composition Techniques • Function pointers struct Sorter { Sorter(int* input); void print(); void call_sort(); intnumbers_[SIZE]; }; Allow generic sorting method invocation?
Composition Techniques • Function pointers struct Sorter { Sorter(int* input); Sorter(int* input, void* sorting_function); void print(); void call_sort(); int* (*sort_fn)(int*, int, int); intnumbers_[SIZE]; }; Add a function pointer
Composition Techniques • Wrapper int main() { … quicksort((int*)numbers); }; Easily replace quicksort?
Composition Techniques • Wrapper Wrap method invocation and data formatting within its own method int main() { … //quicksort((int*)numbers); invoke_bubblesort((int*)numbers); }; void invoke_bubblesort() { // handle data dependencies run_bubble_sort(params…); // handle data dependencies }
Back to Working Example • Given astructSorter: • Contains an integer array and a function pointer • Separate functions for each sorting method • Currently these are “floating” modules struct Sorter { Sorter(int* input); Sorter(void* sorting_function); void print(); void call_sort(); int* (*sort_fn)(int*, int, int); intnumbers_[SIZE]; }; void bubble_sort(); int* quicksort(int* input, int p, int q); void mergesort(int* input, int p, int q);
Sorting Composition • Sorting structure required a single modification in this instance • Addition of function pointer to pull in a sorting method • Existing sort methods setup to modify numbers array in place struct Sorter { Sorter(int* input); Sorter(void* sorting_function); void print(); void call_sort(); int* (*sort_fn)(int*, int, int); intnumbers_[SIZE]; }; void bubble_sort(); int* quicksort(int* input, int p, int q); void mergesort(int* input, int p, int q);
Sorting Composition • Sorting structure required a single modification in this instance • Addition of function pointer to pull in a sorting method • Existing sort methods setup to modify numbers array in place Initialize our Sorting structure with whichever sorting method we choose struct Sorter { Sorter(int* input); Sorter(int* input, void* sorting_function); void print(); void call_sort(); int* (*sort_fn)(int*, int, int); intnumbers_[SIZE]; }; void bubble_sort(); int* quicksort(int* input, int p, int q); void mergesort(int* input, int p, int q);
Composition Steps • Technically, a successful composition factors in three key points: • Data producer provides data • Composed module handles expected functionality • Data consumer accepts data and program continues to run
Composition Steps Taken • Data producer (structSorter) • Allow generic call to swap sorting method • Provide access to internal data storage • Composed module (method QuickSort) • Create function pointer • Provide function pointer to data producer • Call composed module method • If necessary, wrap module method
Composition Steps Taken • Data consumer (structSorter) • Pull data from composed module into necessary location • In this case, Sorter.numbers_
Wrapper Example • Wrapper function called from main • Passed in target data structure • Calls merge sort directly • Copies over sorted data into target data structure upon completion • In this case, passing in and copying out handled implicitly int main() { int numbers_ = [10,2,5,1,0,4,…]; … merge_sort_wrapper((int*)numbers_, 0, SIZE-1); … } void merge_sort_wrapper(int* input, int p, int q) { merge_sort((int*)input, 0, SIZE-1); }
“Canonical” Composition Steps • Automating composition requires knowledge of possible compositions • Steps in between C1 op1 op3 op2
“Canonical” Composition Steps • Automating composition requires knowledge of possible compositions • Steps in between C2 C1 op1 op3 op2 op1 op3 op2 op4 op5
“Canonical” Composition Steps • Automating composition requires knowledge of possible compositions • Steps in between Looking for common operations to code within a genome C2 C1 op1 op3 op2 op1 op3 op2 op4 op5
Composition Steps • Function pointer (given same return type / parameters) • Add function pointer to data producer struct • Pass in algorithm function to struct • Ensure that data is either formatted in place or handled after called
Composition Steps • Function pointer (different return types / parameters) • Similar approach to previous • Create wrapper that matches function pointer • Call necessary function inside wrapper • Ensure data is copied correctly
Composition Steps • Wrapper without function pointer • Instantiate struct in wrapper function • Call desired algorithm directly in wrapper • Copy data over into desired data consumer
Composition Steps • Code injection • Comment / remove previous code • Directly inject functionality as needed • Ensure correct variables contain necessary data
(Semi-)Automated Composition • Start with Quicksort program • Identify methods for composing in merge/bubble sorts • All methods here have already been implemented, now identifying methods for automating them
void run_sort(struct Sorter* s); int main(intargc, char* argv[]) { struct Sorter s; inti; // create and output original numbers int numbers[SIZE] = {10, 8, 5, 9, 1, 3, 7, 2, 4, 6}; // initialize generic data producer setup_data(&s, (int*)numbers); // print initial print(&s); // call our sorting function run_sort(&s); // print print(&s); return 0; } void run_sort(struct Sorter* _s) { // calling quicksort int* (*qs)(int*, int, int) = &quicksort; _s->sort_fn = qs; call_sort(_s); } $ [10 8 5 9 1 3 7 2 4 6 ] [1 2 3 4 5 6 7 8 9 10 ]
void run_sort(struct Sorter* s); int main(intargc, char* argv[]) { struct Sorter s; inti; // create and output original numbers int numbers[SIZE] = {10, 8, 5, 9, 1, 3, 7, 2, 4, 6}; // initialize generic data producer setup_data(&s, (int*)numbers); // print initial print(&s); // call our sorting function run_sort(&s); // print print(&s); return 0; } void run_sort(struct Sorter* _s) { // calling quicksort int* (*qs)(int*, int, int) = &quicksort; _s->sort_fn = qs; call_sort(_s); } Data producer / consumer Kick off quicksort Calling quicksort via function pointer (existing) Could easily be a pure call to quicksort(int*, int, int) $ [10 8 5 9 1 3 7 2 4 6 ] [1 2 3 4 5 6 7 8 9 10 ]
Prototypes Struct int* quicksort(int* input, int p, int r); intbubble_sort(int* input); void merge_sort(int* input, int p, int r); structSorter { int numbers_[SIZE]; int* (*sort_fn)(int*, int, int); } • Swap in bubble sort • From prototype, we know that bubble_sort takes in an int* and returns void • We can assume sorting is done either: • In-place (returned via parameter pointer) • Internally (updates local/global data) • Recognizing this method will require extra information in method signature
Prototypes Struct int* quicksort(int* input, int p, int r); intbubble_sort(int* input); void merge_sort(int* input, int p, int r); structSorter { int numbers_[SIZE]; int* (*sort_fn)(int*, int, int); } • Assume sorting done in-place • Our data producer / consumer are the same (struct Sorter s) • Composition options: • [Data] • [1] – Simply pass pointer to our array (s.numbers) • [2] – Copy our array to a temporary variable, pass it in, and copy back after bubble_sortretursn • This has more overhead, but may be necessary if we want to preserve our original data structure
Prototypes Struct int* quicksort(int* input, int p, int r); intbubble_sort(int* input); void merge_sort(int* input, int p, int r); structSorter { int numbers_[SIZE]; int* (*sort_fn)(int*, int, int); } • Composition options: • [Call] • [1] – Hijack run_sortmethod for use as our own wrapper (injection) • [2] – Comment out run_sortand inject our own code • From either [1] or [2], we can: • [1] – Call method in place (code injection) • [2] – Create a wrapper to directly call bubble_sort • [3] – Create a wrapper to use existing function pointer • [4] – Add a new function prototype to Sorter to match bubble_sort
Prototypes Struct int* quicksort(int* input, int p, int r); intbubble_sort(int* input); void merge_sort(int* input, int p, int r); structSorter { int numbers_[SIZE]; int* (*sort_fn)(int*, int, int); } • Composition options: • [Call] • Parameter consideration • bubble_sorttakes a single int* parameter, where quick_sort takes an int* and 2 ints • Same issue will occur in merge_sort • We will need context from a method signature to decide what values should be used • Automate this with continuance or def-use chain analyses?
Prototypes Struct int* quicksort(int* input, int p, int r); intbubble_sort(int* input); void merge_sort(int* input, int p, int r); structSorter { int numbers_[SIZE]; int* (*sort_fn)(int*, int, int); } • Now we want to compose in merge_sort • We determine from the prototype that it takes 3 parameters • Need context to understand what they all mean • For instance, if not an int* for the first parameter, but we know it will be the data to be sorted, we’d need to transform our data producer into what is necessary • The [Call] portion of composition would be essentially the same
Agenda • Working Example • Composition Techniques • Key Milestones • Genome Structure • OpenBEAGLE Puppy
Key Milestones • (1) Literature review • (2) Develop working example to understand composition specifics • (3) Develop genome based on strategies found from (1,2) • (4) Develop fitness criteria • (5) Implement genome and fitness criteria within an evolutionary algorithm
(Next) Key Milestones • Define generalized context-free grammar • Increase complexity of working example • Increase genome complexity as necessary • Develop deliverable tool for configuring and deploying evolutionary algorithm • Mitigate environmental uncertainty
Agenda • Working Example • Composition Techniques • Key Milestones • Genome Structure • OpenBEAGLE Puppy
Genome Structure • Genetic programming • Typically uses a tree structure, represent a program as an abstract syntax tree • ADD/OR/MUL/etc. operations • Composed of ‘operators’ • Identify composition operations • Iteratively developed from working example
Genome Structure Behavioral compatibility assumed
Genome Example Input / output