Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform...

63
Faculteit Wetenschappen Vakgroep Toegepaste Wiskunde, Informatica en Statistiek Computationele benaderingen voor deductie van de computationele complexiteit van computerprogramma’s Evert Van Petegem Promotor: prof. dr. Peter Dawyndt Masterproef ingediend tot het behalen van de academische graad van Master of Science in de Informatica Academiejaar 2017-2018

Transcript of Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform...

Page 1: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Faculteit Wetenschappen

Vakgroep Toegepaste Wiskunde, Informatica en Statistiek

Computationele benaderingen voordeductie van de computationele

complexiteit van computerprogramma’s

Evert Van Petegem

Promotor: prof. dr. Peter Dawyndt

Masterproef ingediend tot het behalen van de academische graad van

Master of Science in de Informatica

Academiejaar 2017-2018

Page 2: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Dankwoord

Deze thesis was het grootste onderzoeksproject van mijn academische carrière tot nu toe.

Ik ben zeer blij met de kans die ik heb gekregen om bij te dragen aan de manier waarop

onderwijs wordt gegeven aan mijn eigen opleiding via deze thesis. Ik wil graag mijn

promotor, prof. dr. Peter Dawyndt, bedanken voor zijn steun en hulp bij het uitwerken

ervan. Zelfs wanneer ik in het eerste semester 1400 kilometer ver was op Erasmus nam

hij systematisch de tijd om mij te begeleiden.

Verder wil ik ook mijn vrienden en familie bedanken, zonder wiens steun deze thesis er

zeer anders had uitgezien. Naast hun eindeloze emotionele steun appreciëer ik ook sterk

hun luisterend oor terwijl ik praatte over de problemen en successen die ik tijdens het

uitwerken van deze thesis ben tegengekomen.

i

Page 3: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Abstract

Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het

nemen van accurate tijds- en geheugenmetingen toe te voegen. Daarnaast wilden we ook

de computationele complexiteit van ingediende oplossingen inschatten. In deze thesis

zullen we een methode beschrijven om automatisch tijds- en geheugenmetingen te nemen

en op basis van deze metingen de computationele complexiteit van programma’s in te

schatten. Om onze methode te testen, hebben we teruggegrepen naar oefeningen die in

Dodona aanwezig waren. Zo weten we meteen of onze methode ook echt bruikbaar zal

zijn binnen Dodona. Daarnaast hebben we een bevraging uitgevoerd bij studenten naar

de wijze waarop zij de metingen zouden gebruiken.

Niet-technische synopsis

De opleiding Informatica aan de faculteit Wetenschappen van de Universiteit Gent maakt

gebruik van Dodona om automatisch de code die studenten indienen te testen. Momenteel

wordt er enkel op correctheid getest en niet op performantie. In deze thesis ontwikkelen

we twee methoden om naast de correctheid ook de performantie van de code te testen.

De eerste methode voert metingen uit tijdens de uitvoering van de code. We kijken

waar in de code veel tijd gespendeerd wordt of veel geheugen gebruikt wordt. Hier kunnen

we dan feedback over geven aan de student, zodat ze een idee heeft van hoe performant

haar code is.

De tweede methode probeert de computationele complexiteit van de code in te schat-

ten. Computationele complexiteit is een theoretisch model dat een ruw idee geeft van

hoeveel langer de code zal uitvoeren (of hoeveel meer geheugen de code zal gebruiken)

naarmate de grootte van de inputs toeneemt. We schatten deze computationele com-

plexiteit in door automatisch een reeks experimenten uit te voeren waarbij de code steeds

andere inputs krijgt (met ook telkens andere groottes).

ii

Page 4: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Extended abstract

Introduction

Dodona (dodona.ugent.be) is an intelligent tutoring system that aims at using a variety

of computing technologies to provide immediate feedback in the context of learning com-

puter programming. The actual feedback is generated by judges and assigment-specific

software tests that specify how solutions can be automatically generated. Traditionally,

each programming language has its own judge. At the moment, Dodona only provides

feedback regarding the correctness of the output of the submitted code.

This thesis implements two methods that can be used to provide feedback regarding

the performance of the submitted code. The first method consists of simply measuring

how much time the execution of the code took and how much memory was used while

executing the code. The second method inferences the computational complexity of the

submitted code from a series of experiments where the time spent executing and the

maximum memory usage were measured using the implementation of the first method.

When implementing these methods, we want to make sure that they are as program-

ming language independent as possible, so that we don’t have to reimplement them for

each individual judge.

Performance measurements

To implement the automatic measurements we used Jupyter kernels. Jupyter kernels

are the technology behind Jupyter Notebooks. Using the Jupyter kernels allows us to

easily support multiple programming languages. The Jupyter framework offers a uniform

interface to interact with the kernels. This drastically reduces the need for programming

language-specific functionality. Because the Jupyter framework is written in Python and

thus offers the most support for Python we chose to create our implementation in Python.

We discern two ways of measuring the performance. The first way is inside the lan-

guage. This means that we use existing language tools for measuring the performance. We

created an implementation for Python based on the existing mem_profiler module. We

monitor the memory usage per executed line of code. Because we also save a timestamp

when measuring the memory usage we implicitely save all the data needed to measure the

time per executed line of code.

iii

Page 5: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figure 1: Memory measurements outside (left) and inside (right) the language. The red

lines on the graph for the measurements inside the language show the execution of the

functions within the program.

The second way of measuring the performance is outside the language. We use oper-

ating system tools to measure the memory usage of the Jupyter kernel at a fixed interval.

Measuring the execution time is simply saving the time difference between the start of

the execution and the end of the execution.

Measuring the performance inside the language gives us much more information to

construct the feedback we give to the student. For example, we can connect memory usage

to function calls in a graphical display of the memory footprint during code execution. It

is also obvious that measuring the performance inside the language incurs a significant

implementation cost for each supported programming language. Another downside of

measuring inside the language is that is significantly slower.

Figure 1 shows examples of graphs that can be created using the measurements from

outside the language and inside the language.

Inferencing computational complexity

To infer the computational complexity we run a series of experiments during which we

measure the execution time and the maximum memory usage for each experiment. For

each experiment we also record the input sizes. We use a list of complexity functions

defined by the exercise. This allows us to support arbitrary complexity functions.

To select the most likely complexity function we compute linear regressions between

the results of the complexity functions applied to the input sizes and the measurements.

The regression with the highest correlation coefficient is the one we pick as the most likely.

iv

Page 6: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

To determine which input sizes the experiments are run with, we start with initial

input sizes defined by the exercise. Using the measurements of this experiment we use

the linear regressions to predict future measurements. Then we select input sizes so that

(with these predictions added) the complexity functions do not correlate with each other.

The selected input sizes are then used for experiments. This is repeated until we are left

with one complexity function that correlates with the measurements.

For one of the tests of our implementation we used an exercise from a Computational

Biology course. The exercise was to compute an aligment in linear space. The time

complexity of the algorithm that needed to be implemented was O(n2). We created 4

implementations with different memory complexities. Given enough time our implement-

ation was able to correctly identify the memory complexities of these 4 programs. For each

of the 4 programs our implementation was also able to identify the O(n2) time complexity.

A limitation that we were not able to resolve is that for complexity functions smaller

than O(n) there is too much overhead from the Jupyter kernel which makes it so that our

implementation is unable to identify them.

Future work

Based on the research done for this thesis we have submitted grant applications for a doc-

torate that will focus on creating a universal judge for Dodona. Based on the experience

we have gained while working on this thesis we are confident that we can implement a

judge that will require minimal effort to support multiple programming languages. At

first, each exercise will need to declare its programming language. At a further stage in

the doctorate we will allow solutions for a single programming assigment to be submitted

in multiple programming languages.

Conclusion

We were able to implement programming language independent methods to measure the

performance of a program and infer its computational complexity.

v

Page 7: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Inhoudsopgave

1 Inleiding 1

1.1 Achtergrond . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.2 Opzet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

1.2.1 Uitvoeren van tijds- en geheugenmetingen . . . . . . . . . . . . . . 2

1.2.2 Inschatten van de computationele complexiteit . . . . . . . . . . . . 3

2 Inschatten van computationele complexiteit 4

2.1 Statische analyse van de broncode . . . . . . . . . . . . . . . . . . . . . . . 4

2.2 Complexiteit inschatten op basis van metingen . . . . . . . . . . . . . . . . 5

3 Werkwijze 11

3.1 Metingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.1.1 Binnen de taal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.1.2 Buiten de taal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.1.3 Vergelijking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.1.4 Overhead . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13

3.2 Inschatten computationele complexiteit . . . . . . . . . . . . . . . . . . . . 14

3.2.1 Eerste implementatie . . . . . . . . . . . . . . . . . . . . . . . . . . 14

3.2.2 Tweede implementatie . . . . . . . . . . . . . . . . . . . . . . . . . 15

4 Resultaten 20

4.1 Geheugenmetingen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

4.2 Inschatten complexiteit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4.2.1 Eerste implementatie . . . . . . . . . . . . . . . . . . . . . . . . . . 23

4.2.2 Tweede implementatie . . . . . . . . . . . . . . . . . . . . . . . . . 24

4.3 Feedback van studenten . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41

vi

Page 8: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

5 Toekomstig werk 47

5.1 Probleemstelling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

5.2 Doelstelling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48

5.3 Methodologie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49

6 Conclusie 53

vii

Page 9: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Hoofdstuk 1

Inleiding

In deze thesis willen we een methode ontwikkelen om automatisch tijds- en geheugenme-

tingen te nemen van uitvoeringen van computerprogramma’s. Verder willen we met deze

opgebouwde infrastuctuur om metingen te nemen automatisch de complexiteit van deze

computerprogramma’s inschatten. Meer specifiek willen we dit doen in een context voor

het testen van programma’s ingediend door studenten. We bespreken eerst de achtergrond

waaruit deze thesis is voortgekomen. Dan gaan we verder met een uitgebreidere uitleg van

het nut van de tijds- en geheugenmetingen en waarom we de complexiteit willen kunnen

inschatten.

1.1 Achtergrond

Deze thesis is voortgekomen uit het online leerplatform Dodona (dodona.ugent.be) dat

de Universiteit Gent ontwikkelt. Dodona is een platform voor studenten en docenten

dat op een didactisch onderbouwde manier zo snel en zo goed mogelijk feedback geeft.

Hierbij is het belangrijk om de feedback loop voor programmeeropdrachten te automati-

seren. Dodona werd opgezet als een generiek raamwerk. Docenten kunnen eigen scripts

definieren die de workflow vastleggen om ingediende oplossingen te beoordelen (judges

genaamd). Het is gebruikelijk om voor één programmeertaal één judge te hebben. Elke

judge ondersteunt ook maar één programmeertaal.

Dodona kan zowel ingezet worden bij examens als bij oefensessies. Studenten kunnen

onbeperkt oplossingen indienen, waarop ze telkens feedback krijgen over de correctheid.

De snelheid waarmee studenten deze feedback krijgen is belangrijk. Hoe sneller de feed-

1

Page 10: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

back bij de student geraakt, hoe sneller de student aan het werk kan gaan om eventuele

problemen op te lossen.

Momenteel bevatten de judges die Dodona gebruikt weinig ondersteuning om de per-

formantie van de code te beoordelen. De testen die Dodona uitvoert hebben altijd een

tijdslimiet. Effectieve uitvoeringstijd wordt gemeten, maar deze metingen worden niet in

de feedback verwerkt en voor de rest ook niet gebruikt.

1.2 Opzet

Gegeven de ervaring die met Dodona is opgedaan, willen we een uitbreiding maken om

ook de performantie van code te testen en hierop feedback te geven. We willen dit liefst

zo programmeertaalonafhankelijk mogelijk doen, zodat we deze functionaliteit niet per

judge opnieuw moeten implementeren. We onderscheiden twee delen aan het testen van

de performantie en de feedback die we hierover willen geven. Enerzijds is er het uitvoerven

van de tijds- en geheugenmetingen, anderzijds is er het inschatten van de computationele

complexiteit.

1.2.1 Uitvoeren van tijds- en geheugenmetingen

Er zijn een aantal redenen waarom we tijds- en geheugenmetingen willen doen en hierover

feedback willen geven aan studenten en docenten.

Voor studenten geeft dit (zeker voor geheugengebruik) visuele feedback over het ge-

drag van hun programma. Ze kunnen zien waar hun programma veel tijd spendeert of

waar het veel geheugen verbruikt. Dit kan helpen om hun programma’s te optimaliseren.

Daarnaast laat het ook gamification van de programmeeropdracht toe. Dit kan nu al

deels in Dodona: studenten kunnen zien hoeveel van hun medestudenten een oefening

reeds juist hebben opgelost. De tijds- en geheugenmetingen kunnen dit echter nog een

stuk versterken. In plaats van als eerste een juiste oplossing te proberen vinden, kunnen

studenten ook proberen om de snelste oplossing te maken of de oplossing die het minste

geheugen verbruikt. In het vak Computationele Biologie uit de Bachelor Informatica is

in academiejaar 2017–2018 hiermee al geëxperimenteerd: de code van de studenten werd

periodiek getest voor een selectie van oefeningen en een rangschikking op basis van de

uitvoeringstijd en het maximale geheugenverbuik werd online gezet. Dit motiveerde vele

2

Page 11: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

studenten om hun oplossing te blijven verbeteren. Het automatiseren van deze metingen

en het aanbieden van een live leaderboard kan deze competiviteit bij de studenten enkel

maar doen toenemen.

Docenten kunnen deze metingen gebruiken om de uitvoering van hun onderwijstaak te

verbeteren. Door eenvoudig te zien wat de tragere of minder geheugenefficiënte oplossin-

gen zijn, kunnen docenten gemakkelijker vaak voorkomende fouten opsporen en hierover

feedback geven aan de studenten. Voor vakken waar de efficiëntie van code relevant is,

kan dit ook een hulp zijn om de code van de studenten te beoordelen.

1.2.2 Inschatten van de computationele complexiteit

Een informele definitie van de computationele complexiteit van een programma is dat de

computationele complexiteit een theoretisch model is dat aangeeft hoe het gebruik van een

resource door een programma zal toenemen naarmate de grootte van de inputs toeneemt.

De twee meest gebruikte resources (en ook degene waar wij ons op zullen toeleggen) zijn

tijd en geheugen.

Het inschatten van de computationele complexiteit van een programma gebeurt door-

gaans op basis van een theoretische analyse. Wij willen deze computationele complexiteit

op een geautomatiseerde manier proberen inschatten. Dit laat ons toe om te testen of een

student een algoritme juist geïmplementeerd heeft. Hier betekent een juiste implementatie

niet alleen dat het de correcte resultaten teruggeeft, maar uiteraard ook dat de computa-

tionele complexiteit van de implementatie dezelfde is als de computationele complexiteit

van het algoritme. Voor vakken die zich toespitsen op algoritmen en datastructuren kan

dit gebruikt worden om het inzicht van de student te testen. Als een student een bepaald

algoritme of een bepaalde datastructuur niet juist kan implementeren, heeft de student

het idee van het algoritme waarschijnlijk niet begrepen.

3

Page 12: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Hoofdstuk 2

Inschatten van computationele

complexiteit

In de wetenschappelijke literatuur zijn er al methoden beschreven voor het inschatten van

de complexiteit van computerprogramma’s. Deze methoden zijn gebaseerd op verschil-

lende principes.

2.1 Statische analyse van de broncode

Het eerste type van methoden dat we hebben bekeken, is gebaseerd op het statisch ana-

lyseren van de broncode. We kunnen al meteen zien dat dit niet optimaal is, aangezien

we liefst zo onafhankelijk mogelijk van de programmeertaal willen werken.

Bij Healy et al. (2000) is de voorgestelde methode meer toegespitst op real-time syste-

men. Er wordt een methode uitgewerkt om het minimum en het maximum aantal iteraties

te bepalen van lussen met meerdere uitgangen. Dit minimum en maximum aantal iteraties

kan zowel symbolisch als numeriek bepaald worden. Voor de numerieke uitwerking moet

de gebruiker zelf waarden aanleveren voor variabelen waarvan de minima en maxima niet

statisch bepaald kunnen worden. De methode ondersteunt ook lussen waarvan de nesting

niet rechthoekig is (zoals bvb. in Listing 2.1).

Ook in het domein van de functionele programmeertalen zijn er al methoden ontwik-

keld om de complexiteit van programma’s in te schatten. In Danielsson (2008) wordt er

een bibliotheek ontwikkeld waarbij de gebruiker aantekeningen moet maken in de code,

om met deze aantekeningen via het typesysteem een model te maken van het aantal uit-

4

Page 13: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

1 for i in range ( 1 0 0 ) :

2 for j in range ( i ) :

3 print ( i , j )

Listing 2.1: Voorbeeldcode voor niet-rechthoekige lussen

gevoerde operaties in de code. Deze bibliotheek maakt gebruik van afhankelijke types.

Als een waarde het type Thunk n a heeft, kan een weak head normal form van de waarde

a in n stappen bekomen worden (geamortiseerd). De annotaties die door de gebruiker in

de code geplaatst moeten worden, zijn functies van het type Thunk n a → Thunk (1 +

n) a. Thunk is hierbij een identiteitsmonad.

Al deze methoden hebben echter hetzelfde probleem: ze verwachten een extra in-

spanning van de gebruiker. Daarnaast zijn ze ook niet programmeertaalonafhankelijk.

Aangezien we een tool willen maken die gebruikt kan worden zonder de code te moeten

aanpassen of veel te moeten weten van de programmeertaal, kunnen we hier dus weinig

uit gebruiken.

2.2 Complexiteit inschatten op basis van metingen

In McGeoch et al. (2002) worden enkele moeilijkheden besproken die het experimenteel

inschatten van de complexiteit gebaseerd op metingen theoretisch onmogelijk maken en

in de praktijk zo goed als altijd een stuk lastiger maken.

Zo is er bijvoorbeeld het probleem dat er te veel mogelijke inputs zijn, zelfs voor een

begrensde grootte van de input. Het exhaustief testen van alle inputs is ofwel onmogelijk

(als er oneindig veel mogelijke inputs zijn) of zeer onpraktisch (aangezien we ook resultaten

willen hebben op een redelijke termijn). Om met dit probleem rekening te houden, wordt

er gebruikt gemaakt van randomisatie van de inputs. Dit kunnen we gebruiken om een

hypothese over alle instanties om te zetten in een hypothese over het gemiddelde geval.

Een volgend probleem is dat de inputgrootte vaak ook niet begrensd is. Hierdoor is

elke inschatting van de complexiteit theoretisch gezien inherent onbetrouwbaar. Neem

bijvoorbeeld een algoritme dat O(n) stappen nodig heeft voor een inputgrootte van n <

106. Als het algoritme voor een inputgrootte van n > 106 ineens kwadratisch wordt

(omdat bijvoorbeeld voor n < 106 het algoritme enkele shortcuts kan nemen) en we

5

Page 14: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

hebben geen gevallen van deze grootte getest, dan is het al meteen onmogelijk om de

complexiteitsgrens juist in te schatten.

Ook het omgekeerde geval is een probleem. Als er geclaimd wordt dat een algoritme een

lineaire complexiteit heeft, maar de metingen zijn duidelijk kwadratisch, dan kan er altijd

gezegd worden dat de metingen niet ver genoeg gegaan zijn. Een makkelijk te construeren

voorbeeld hiervan is een typische implementatie van quicksort. Bij de meeste implemen-

taties wordt er vaak overgegaan naar een simpeler kwadratisch algoritme van zodra de

opgesplitste lijsten klein genoeg zijn. Deze implementaties hebben nog steeds complexiteit

O(nlog(n)), omdat de threshold om naar een kwadratisch sorteeralgoritme over te stappen

constant gekozen wordt en de uitvoeringen van het kwadratisch sorteeralgoritme voor de

complexiteit van de quicksort implementatie dus als constante meegerekend wordt. Als we

deze threshold zeer groot kiezen (bijvoorbeeld 106), dan zullen de metingen ook duidelijk

kwadratisch zijn voor inputs van een redelijke grootte. De complexiteit van het quicksort

algoritme is echter nog steeds O(nlog(n)).

Verder is ook de complexiteit van het machinemodel een probleem. Moderne com-

puters maken tijdsmetingen onbetrouwbaarder. Zo hebben bijvoorbeeld caches, virtueel

geheugen, out-of-order execution, compilers en time-sharing van processen allemaal een

invloed op het uitvoeren van programma’s en maken het dus moeilijker om de uitvoe-

ringstijd in te schatten. Hierdoor zit er een significante variantie op de metingen die we

doen. Zelfs geheugenmetingen worden bemoeilijkt. De meeste talen bevatten tegenwoor-

dig een runtime met een garbage collector. Dit verhoogt het programmeergemak, maar

deze garbage collectors en de momenten waarop ze geactiveerd worden zijn meestal niet-

deterministisch. Twee verschillende uitvoeringen van hetzelfde programma met dezelfde

inputs kunnen dus een verschillend maximum geheugengebruik hebben.

McGeoch (1987) bevat een korte bespreking van enkele technieken om de variantie op

de meetresultaten te verminderen. De eerste (en eenvoudigste) techniek om de variantie op

de meetresultaten te verminderen, is om per meetpunt meerdere metingen uit te voeren.

Dit laat toe om dan bijvoorbeeld een gemiddelde van de resultaten te nemen, wat dan

ook een betere inschatting zal zijn voor de gemiddelde duur van een uitvoering van het

programma met de gegeven inputs.

Het laatste probleem dat in McGeoch et al. (2002) wordt aangebracht, is de moei-

lijkheid om hypotheses te vinden. De gemeten functie van de uitvoeringstijden kan non-

6

Page 15: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

monotonisch zijn, terwijl we voor de complexiteitsgrenzen enkel geïnteresseerd zijn in een

monotonische bovengrens. Ook zijn voor kleine inputgroottes de termen van lagere orde

zeker niet te verwaarlozen.

Gulwani et al. (2009) ontwikkelden de tool SPEED om de computationele complexiteit

van programma’s te bepalen. SPEED is gebaseerd op automatische instrumentatie van

de code door meerdere tellers in de code te plaatsen. Met een tool die lineaire invarianten

genereert, berekent SPEED lineaire grenzen per individuele teller. Deze individuele gren-

zen worden dan samengesteld om totale grenzen te genereren die niet-lineair en disjunct

kunnen zijn. Er wordt ook een algoritme gegeven om het genereren van deze grenzen te

automatiseren. Om complexiteitsgrenzen in te schatten voor andere resources dan tijd,

is de methode bruikbaar zolang de gebruiker goede tellers kan instellen. Ook deze me-

thode was voor ons niet direct bruikbaar. Aangezien de code aangepast moet worden om

de tellers er in te kunnen plaatsen, is het per programmeertaal vrij veel werk om dit te

ondersteunen.

Een al iets meer programmeertaalonhafhankelijke methode werd uitgewerkt in Golds-

mith et al. (2007). Deze methode werkt met profilers om de kost per basic block te

meten. Deze kost wordt uitgedrukt in het aantal keer dat het basic block wordt uitge-

voerd. De gebruiker moet de argumenten voor de verschillende uitvoeringen in geven.

Ook de verschillende numerieke kenmerken waarop de complexiteit kan gebaseerd zijn,

moet de gebruiker voor elke set argumenten ingeven. De basic blocks worden dan ge-

clustered. Clusters zijn verzamelingen van basic blocks waarin met een lineair model het

aantal uitvoeringen voorspeld kan worden uit het aantal uitvoeringen van een ander basic

block in de verzameling. Op deze clusters worden dan lineaire modellen en powerlaw mo-

dellen toegepast. Hieruit volgt dat deze methode enkel complexiteitsfuncties van de vorm

O(xa) kan inschatten (waarbij a ingeschat wordt). Om de kwaliteit van het model in te

schatten, wordt gebruikt gemaakt van de R2 statistiek (een meting van de fitness van de

data tegenover het model). Deze methode is zoals gezegd al iets meer onafhankelijk van

de programmeertaal, aangezien de code zelf niet geanalyseerd of aangepast moet worden.

Per programmeertaal is er toch nog wat werk nodig om de werking tussen de profiler

en het programmeeronafhankelijke inschatten te implementeren. Elke programmeertaal

heeft zijn eigen profilers en er zal dus ook werk nodig zijn om deze profilers aan te spreken

en er de relevante data uit te halen.

7

Page 16: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

In McGeoch et al. (2002) worden vijf technieken aangebracht om van experimentele

metingen tot een complexiteitsgrens te komen. Deze technieken zijn gebaseerd op verschil-

lende bestaande statistische en numerieke methoden. In de uitleg over deze technieken die

volgt, duidt f de in te schatten functie aan en f de schatting van de functie op basis van

de gebruikte technieken. De inputgroottes en de metingen van de experimenten worden

respectievelijk voorgesteld als X en Y .

De eerste techniek is de “Guess Ratio” techniek. Deze techniek kan enkel gebruikt

worden voor f(x) van de vorm f(x) = a1xb1 + a2x

b2 + . . . + anxbn . Hierbij wordt er

een functie geraden van de vorm f(x) = xb. De verhouding f(x)/f(x) heeft dan enkele

eigenschappen:

• Wanneer f(x) ∈ O(f(x)) (met andere woorden, f(x) bevindt zich in dezelfde com-

plexiteitsklasse als f(x)) zal deze verhouding afnemen naar een positieve constante

wanneer x toeneemt.

• Wanneer f(x) /∈ O(f(x)) zal de verhouding op een bepaald moment beginnen toe-

nemen en een uniek minimum hebben op een locatie xr.

De “Guess Ratio” techniek maakt gebruik van deze eigenschappen. De verhouding

van Y/f(X) wordt bekeken. Als hier een stijging wordt waargenomen, weten we dat de

eigenschap f(x) /∈ O(f(x)) geldt. De techniek begint met een constante functie f(x) = x0.

Als de tweede eigenschap geldt wordt b met een kleine δ verhoogd. Dit wordt gedaan tot

de tweede eigenschap niet meer geldt. Hieruit moet dan volgen dat de eerste eigenschap

geldt en kunnen we dus concluderen dat f(x) ∈ O(f(x)). Aangezien we beginnen van

f(x) = x0 is het logisch dat hier een ondergrens voor de complexiteitsfunctie zal uitkomen.

De volgende voorgestelde techniek is de “Guess Difference” techniek. Deze techniek is

gelijkaardig aan de “Guess Ratio” techniek maar in plaats van de verhouding Y/f(x) te

gebruiken, wordt er gebruikt gemaakt van het verschil f(X)− Y . Deze techniek zal ook

een bovengrens inschatten in plaats van een ondergrens. De techniek schat functies van

de vorm f(x) = cxd + e in. Het basisidee van de techniek is dat wanneer f(x) /∈ O(f(x))

de kromme van het verschil op een punt moet toenemen (van zodra x groot genoeg is).

Hieruit volgt dat er een minimum moet zijn voor een bepaalde xd. Deze xd is omgekeerd

gecorreleerd met de a uit de geraden functie. Voor een grote a zal de verschilfunctie overal

8

Page 17: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

stijgen en is xd = 0, maar als a klein genoeg is, kan er voor kleine x een daling zijn in de

verschilfunctie.

De techniek werkt dan als volgt. Er wordt begonnen met een duidelijke bovengrens

f(x) = axb. Dan wordt er gezocht naar een a > 0 waarvoor xd > 0. Als deze gevonden

wordt, is er geweten dat de b nog te groot is en wordt deze dus verminderd met een stap

δ. Dit wordt herhaald zolang er een a > 0 gevonden wordt waarvoor xd > 0. De laagste

b waarvoor er nog een a gevonden werd is dan de kleinste bovengrens gevonden door de

techniek. Merk op dat deze techniek niet dezelfde klasse aan functies kan inschatten als

de “Guess Ratio” techniek. Als er nog extra niet-constante factoren in de in te schatten

functie zitten, dan kunnen we niet zeggen dat xb het minimum is waar we naar zoeken.

De derde techniek heet de “Power” techniek en werkt als volgt. Op de datasets X

en Y wordt een logaritmische transformatie toegepast. De waarde b van f(x) = xb is

dan de helling van de lineaire regressie op de getransformeerde data. Deze techniek is

gebaseerd op het volgende idee. Als we de functie f(x) = axb nemen en we passen

dezelfde logaritmische transformatie toe (x′ = ln(x) en y′ = f(x)), dan bekomen we

y′ = bx′ + a. Dit is juist het lineaire model dat we inschatten met onze regressie.

De voorlaatste techniek is de “BoxCox” techniek. Deze techniek is een veralgemening

van de “Power” techniek. In plaats van enkel een logaritmische transformatie toe te

passen, is de transformatie die toegepast wordt geparameteriseerd door de parameter λ.

De transformatie is dan als volgt:

Y λ =

Y λ−1λY

λ−1 als λ > 0

Y ln(Y ) als λ = 0

Hierbij is Y het geometrisch gemiddelde van Y , oftewel Y = exp(gem(ln(Y ))). De “Box-

Cox” techniek past deze transformaties toe op een reeks bs. Deze bs (of toch tenminste

het interval waarin ze gezocht moeten worden) zijn ingegeven door de gebruiker. De λ

van de transformatie is dan 1/b. De transformatie waarbij de som van de gekwadrateerde

verschillen het kleinste is, is degene waarvan de b als antwoord wordt teruggegeven.

De laatste techniek (de “Difference” techniek) is gebaseerd op de gedeelde verschillen-

methode van Newton voor de interpolatie van veeltermen. Deze techniek berekent telkens

Y i = diff (Y i−1)/diff (X)i−1 en X i = diff (X i−1). Hierbij is diff een functie die de ver-

schillen tussen opeenvolgende waarden in het argument teruggeeft. Y 0 en X0 zijn gelijk

aan de experimentele data (dus respectievelijk Y en X). De eerste b waarbij alle waarden

9

Page 18: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

van Y b gelijk zijn is de b van f(x) = xb.

De technieken voorgesteld in McGeoch et al. (2002) lijken optimaal voor ons om mee

verder te werken. Ze zijn onafhankelijk van de gebruikte programmeertaal. Het enige

probleem dat we kunnen zien is dat de technieken enkel werken voor polynomiale com-

plexiteitsfuncties, terwijl we natuurlijk zo veel mogelijk verschillende soorten complexi-

teitsfuncties willen ondersteunen.

10

Page 19: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Hoofdstuk 3

Werkwijze

Onze eigen implementatie voor het experimenteel inschatten van de computationele com-

plexiteit verliep in twee fases. We zijn begonnen met het nemen van metingen. Daarna

hebben we gewerkt aan het inschatten van de complexiteit op basis van deze metin-

gen. Verder hebben we ook gewerkt aan het automatisch kiezen van de parameters van

de metingen. De resulterende code van deze implementatie kan gevonden worden op

https://github.ugent.be/ecvpeteg/thesis-complexity/tree/master/code.

3.1 Metingen

Om van start te gaan met de opzet van deze thesis, zijn we begonnen met het automa-

tiseren van tijds- en geheugenmetingen van code. Hier was de opzet nog niet om het

systeem te creëren dat ons toelaat om automatisch verschillende testgevallen uit te voe-

ren. Het was vooral belangrijk om accurate metingen te kunnen uitvoeren. We hebben

hierbij gekozen om gebruik te maken van het bestaande Jupyter kernel framework (Kluy-

ver et al. (2016)). Jupyter kernels zijn de technologie achter de Jupyter Notebooks. We

hebben gekozen om gebruik te maken van deze Jupyter kernels om het gemakkelijk te

maken meerdere programmeertalen te ondersteunen. Het Jupyter framework biedt name-

lijk een uniforme interface om te interageren met deze kernels. Hierdoor is er zeer weinig

programmeertaal-specifieke functionaliteit nodig om een nieuwe programmeertaal te on-

dersteunen. Er bestaan ondertussen al 106 verschillende Jupyter kernels (Project Jupyter

(2018)) waardoor we met minimaal werk veel programmeertalen ondersteunen. Doordat

het Jupyter framework geschreven is in Python en er hiervoor dus ook het meeste support

11

Page 20: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

is, hebben we gekozen om de implementatie van onze methodes ook in Python te doen.

We onderscheiden twee manieren om metingen uit te voeren. We kunnen dit doen

binnen de taal en buiten de taal.

3.1.1 Binnen de taal

Met het uitvoeren van metingen binnen de taal bedoelen we dat we bestaande voorzie-

ningen van de taal gebruiken om de metingen uit te voeren. Specifiek zijn dit bestaande

profilers (ook geheugen-profilers). Voor Python hebben we hiervoor een implementatie

gemaakt op basis van een aangepaste versie van de bestaande mem_profiler module.

Hierbij monitoren we het geheugengebruik per uitgevoerde lijn code. Om deze module te

activeren, kan ze ook als Jupyter kernel extensie ingeladen worden. Aangezien we ook de

huidige tijd opslaan op het moment dat we de geheugenmeting uitvoeren, zit hier impliciet

ook een tijdsmeting in. We doen deze metingen per uitgevoerde lijn code en ook wanneer

een functie binnengegaan en buitengegaan wordt. Dit alles doen we om zoveel mogelijk

informatie te verzamelen die we kunnen gebruiken om feedback terug te geven aan de

student.

3.1.2 Buiten de taal

Om metingen te doen buiten de taal, voeren we de metingen uit op het Jupyter ker-

nelproces. Tijdsmetingen zijn hierbij simpel. We slaan de huidige tijd op wanneer de

Jupyter kernel de relevante code begint uit te voeren en wanneer de Jupyter kernel rap-

porteert dat de code klaar is met uitvoeren. Dan nemen we het verschil tussen deze twee

tijdstippen. Om het geheugengebruik te meten, voeren we externe metingen uit op het

geheugengebruik van het Jupyter kernelproces. Tijdens de eerste 3 seconden van de uit-

voering nemen we elke 10 milliseconden een sample van het geheugengebruik, daarna elke

50 milliseconden. We hebben ervoor gekozen om minder samples te nemen na 3 seconden.

Enerzijds omdat we bij programma’s die langer uitvoeren op een hogere resolutie feedback

zullen geven over de meetresultaten. Anderzijds omdat de meeste programma’s waarbij

we deze feedback willen tonen toch binnen de 3 seconden zullen afgerond zijn (aangezien

deze feedback van de soort is die snel bij de student terecht moet komen).

12

Page 21: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

3.1.3 Vergelijking

Het voordeel om de metingen binnen de taal te doen, is dat we veel informatie kunnen

gebruiken om feedback te geven aan de student. Bijvoorbeeld de uitgevoerde functies

samen met het geheugengebruik op één grafiek kunnen tonen, kunnen we met metingen

buiten de taal niet doen. Het is echter natuurlijk ook meteen duidelijk dat dit voor elke

ondersteunde programmeertaal een significante implementatiekost met zich meebrengt.

Door het gebruik van Jupyter kernels kunnen metingen buiten de taal op een generieke

manier uitgevoerd worden voor alle ondersteunde programmeertalen. Een ander verschil

is ook de kost in uitvoeringstijd. De code uitvoeren met een profiler is (toch zeker bij onze

Python-implementatie) significant trager dan zonder. Dit zorgt ervoor dat de feedback

minder snel tot bij de student geraakt. Als we met deze metingen de complexiteit willen

bepalen binnen een bepaalde tijdslimiet, dan zal deze tragere uitvoering zich ook mani-

festeren in minder uitgevoerde experimenten en dus ook minder betrouwbare resultaten.

Dit komt bij metingen buiten de taal uiteraard niet voor, aangezien het proces dat het

experiment uitvoert niet hoeft onderbroken te worden om de metingen te nemen.

3.1.4 Overhead

Bij beide manieren van werken moeten we voor elke uitvoering van het programma een

nieuwe Jupyter kernel opstarten om accurate geheugenresultaten te verkrijgen. Dit komt

voornamelijk door de runtime van de programmeertalen die we ondersteunen en doordat

de Jupyter kernel voorgaande resultaten bijhoudt (en de interactie tussen de twee). Dit

zorgt ervoor dat de gemeten allocaties van programma’s minder accuraat worden naar-

mate er meer testen uitgevoerd werden. Moderne runtimes zijn gebouwd om het aantal

allocaties zo klein mogelijk te houden zonder een grote overhead te creëren in het geallo-

ceerde geheugen. Daardoor worden de groottes van allocaties deels ook gebaseerd op het

huidige geheugengebruik van het programma. Daarnaast wordt niet al het geheugen dat

vrijgemaakt wordt door de garbage collector ook vrijgegeven naar het besturingssysteem

door de runtime van het programma. Al deze redenen zorgen ervoor dat de geheugenme-

tingen van latere testen binnen dezelfde Jupyter kernel minder accuraat zullen zijn. Het

opstarten van een nieuwe Jupyter kernel brengt wat overhead met zich mee. Voor Python

is deze overhead ongeveer 1 seconde.

13

Page 22: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

3.2 Inschatten computationele complexiteit

Om de computationele complexiteit in te schatten hebben we twee implementaties ge-

maakt. We implementeerden eerst de vijf technieken van McGeoch et al. (2002) zoals

beschreven in sectie 2.2. Deze technieken bleken toch niet optimaal te zijn (zoals uit-

gelegd op het einde van 3.2.1) waardoor we een tweede implementatie gemaakt hebben

die ruwweg gebaseerd is op de ideëen uit McGeoch et al. (2002). Bij deze tweede im-

plementatie bespreken we ook hoe we het kiezen van parameters voor de experimenten

geïmplementeerd hebben.

3.2.1 Eerste implementatie

Als eerste implementatie om met de uitgevoerde metingen de computationele complexiteit

in te schatten, hebben we ons gebaseerd op de vijf technieken uitgewerkt in McGeoch et al.

(2002) zoals besproken in hoofdstuk 2. Deze implementatie gaf meteen beloftevolle resul-

taten. De technieken die voorgesteld worden werkten meteen vrij goed voor polynomiale

algoritmen. Ook uitbreidingen maken naar algoritmen met meer ingewikkelde complexi-

teitsfuncties ging redelijk goed. Zo is er bijvoorbeeld een uitbreiding gemaakt voor algo-

ritmen met een logaritmische factor (dus van de vorm xalog(x)). Het werd echter al vrij

snel duidelijk dat de methoden veralgemenen zodat we een arbitaire complexiteitsfunctie

konden inschatten niet voor de hand zou liggen. Voor één enkele complexiteitsfunctie is

de implementatie van een extra techniek niet te ingewikkeld. Aangezien er zeer veel moge-

lijke complexiteitsfuncties zijn, leek deze kost ons te hoog. Het was bij sommige van deze

geïmplementeerde technieken ook niet meteen duidelijk hoe we de betrouwbaarheid van de

bekomen resultaten moesten inschatten. Zo gaf de “Difference” techniek bijvoorbeeld geen

informatie over hoe betrouwbaar het resultaat was. We willen weten hoe betrouwbaar het

resultaat is aangezien deze technieken eventueel een verschillend resultaat kunnen geven

en we dus in de feedback ook willen kunnen aangeven welke methode meest waarschijnlijk

het juiste antwoord heeft. Ook het ondersteunen van meerdere complexiteitsparameters is

met deze methode vrij lastig. In McGeoch (1987) wordt voorgesteld om alle parameters

behalve 1 vast te zetten en dan de parameter die niet vaststaat in te schatten tot alle

parameters ingeschat zijn. Dit zou in de meeste gevallen inderdaad werken, maar vraagt

veel extra uitvoeringen van de code. Er zijn ook enkele gevallen waarin dit niet meteen zal

14

Page 23: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

werken: het is niet snel in te zien hoe we hiermee het onderscheid kunnen maken tussen

een complexiteit van O(m+ n) en een complexiteit van O(m ∗ n).

3.2.2 Tweede implementatie

Om tegemoet te komen aan de gestelde problemen, zijn we overgeschakeld naar een an-

dere methode om de complexiteit in te schatten. Hierbij vertrekken we in plaats van

een generieke voorgedefinieerde lijst van complexiteitsfuncties van een lijst van complexi-

teitsfuncties die per oefening ingesteld wordt. We verwachten van de oefening om deze

complexiteitsfuncties toe te passen op de complexiteitsparameters. We hebben hiervoor

een Python klasse Exercise geschreven waar een oefening van kan overerven. Deze klasse

bevat een aantal abstracte methoden die door een specifieke oefening geïmplementeerd

moeten worden:

• name()

Deze methode moet de naam van de oefening teruggeven. Dit wordt enkel gebruikt

om de ruwe data georganiseerd te kunnen opslaan.

• get_function()

Deze methode moet de code teruggeven die uitgevoerd moet worden om de code

ingediend door de student te testen. De code moet teruggegeven worden onder

de vorm van een lijst van format strings. Hierbij moeten de format strings de

resultaten van de get_test methode interpoleren. Enkel op de uitvoering van het

laatste element van de lijst worden er metingen genomen. Dit is het geval om

beperkingen van programmeertalen weg te werken. Zo zijn we bijvoorbeeld bij Java

op de limiet op de grootte van de resulterende bytecode per methode gestoten. Om

dit te omzeilen, hebben we de testgevallen uitgeschreven naar een bestand dat dan

op voorhand ingelezen wordt om dynamisch een array te vullen. Dit inlezen willen

we echter niet bij het uitvoeren van de code zelf rekenen.

• initial_parameters()

Deze methode moet een tuple of een lijst teruggeven van initiële inputgroottes. De

methode dient om in de uitvoering van onze methode een beeld te hebben waar we

ongeveer moeten beginnen. Het kan namelijk zijn dat voor een bepaald probleem

al voor een inputgrootte van 100 het probleem veel tijd zal vragen, terwijl voor

15

Page 24: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

andere problemen een inputgrootte van 100 amper de moeite waard is om uit te

voeren. Aangezien het zeer moeilijk is (en ook buiten de scope van deze thesis)

om eerst een inschatting te maken van waar we ongeveer moeten beginnen en wat

de stapgrootte ongeveer moet zijn, vragen we dit aan de oefening. De stapgrootte

wordt ook vastgesteld op basis van de initiële parameters.

• get_test(parameters)

Deze methode moet, gegeven een tuple of een lijst van parameters, een tuple of lijst

van argumenten teruggeven. Deze argumenten zullen geïnterpoleerd worden in de

code die door de get_function methode wordt teruggegeven.

• symbolic_time_complexity()

Deze methode moet een tuple of een lijst van strings teruggeven waarin de mogelijke

tijdscomplexiteitsfuncties symbolisch voorgesteld worden. Deze methode wordt en-

kel gebruikt om achteraf een overzicht te kunnen geven van de complexiteitsfuncties

en de waarschijnlijkheid dat een oplossing voor de oefening die complexiteit heeft.

• time_complexity(parameters)

Deze methode moet een tuple of een lijst teruggeven van de resultaten van de com-

plexiteitsfuncties als de gegeven inputgroottes als parameters gebruikt worden. Dus

als bijvoorbeeld de complexiteitsfuncties m + n, m ∗ n en m ∗m zijn en de input-

groottes (5, 10) zijn, moet de methode (15, 50, 25) teruggeven.

• symbolic_memory_complexity()

Deze methode is identiek aan de symbolic_time_complexity()methode, maar dan

voor geheugencomplexiteitsfuncties in plaats van tijdscomplexiteitsfuncties.

• memory_complexity(parameters)

Deze methode is identiek aan de time_complexity(parameters) methode, maar

dan voor geheugencomplexiteitsfuncties in plaats van tijdscomplexiteitsfuncties.

• language()

Deze methode moet de programmeertaal teruggeven waarin de oefening gemaakt

moet worden. Deze taal zal gebruikt worden om de juiste Jupyter kernel op te

starten. Momenteel moet het resultaat van deze methode gelijk zijn aan de naam

16

Page 25: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

van de relevante Jupyter kernel. Dit komt echter zo goed als altijd overeen met de

naam van de programmeertaal.

• get_submissions()

Deze methode moet een dictionary teruggeven die de naam of de id van de student

afbeeldt op de code die de student heeft ingediend. Om de implementatie voor

oefeningen op Dodona te vergemakkelijken, is er een klasse DodonaExercise waarbij

de methode submissions_url() moet geïmplementeerd worden in plaats van de

methode get_submissions(), die dan uiteraard de URL van de oplossingen van

de oefening op Dodona moet teruggeven. Bij de DodonaExercise klasse is er ook

nog een methode preload_code() die dient om stukken code terug te geven die

noodzakelijk zijn voor de successvolle compilatie van een oplossing, maar er geen

deel van uitmaken (bijvoorbeeld de definitie van een interface waaraan studenten

moesten voldoen in Java).

Een klasse die al deze methoden implementeert, kan gebruikt worden om de complexi-

teit van de oplossingen in te schatten. Uiteindelijk zal er voor alle mogelijke complexi-

teitsfuncties een betrouwbaarheid als output gegeven worden. Het bepalen van deze

betrouwbaarheid is niet complex. Deze is gelijk aan de R2 statistiek (de correlatie-

coëfficient) van de lineaire regressie tussen de meetresultaten en de resultaten van de

time_complexity en memory_complexity methodes. Een voorbeeld van een implemen-

tatie voor een Python oefening kan gevonden worden op https://github.ugent.be/

ecvpeteg/thesis-complexity/blob/master/code/global_alignment_exercise.py. Een

voorbeeld van een Java oefening kan gevonden worden op https://github.ugent.be/

ecvpeteg/thesis-complexity/blob/master/code/stable_marriage_exercise.py.

Kiezen van parameters

Het belangrijkste element van de implementatie dat nog besproken moet worden, is hoe we

de parameters kiezen. Zoals gezegd in de bespreking van de Exercise klasse beginnen we

met het resultaat van de initial_parameters methode. Hiervoor wordt dus al meteen

een eerste meting gedaan. Daarna wordt het volgende proces herhaald tot er geen volgende

argumenten worden teruggegeven of tot de tijdslimiet overschreden is.

1. Verzamel alle complexiteitsfuncties waarvoor de betrouwbaarheid hoog genoeg is.

17

Page 26: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Deze betrouwbaarheidsdrempel kan ingesteld worden (met als standaardwaarde 0.8).

2. Voeg parameters toe aan een verzameling tot alle complexiteitsfuncties niet paars-

gewijs correleren. Hierbij wordt de lineaire regressie, die berekend werd om de

betrouwbaarheid te bepalen, gebruikt om tijdelijk nieuwe elementen aan de dataset

van de metingen toe te voegen. We zeggen dat twee complexiteitsfuncties niet cor-

releren wanneer de R2 statistiek van de lineaire regressie tussen de twee kleiner is

dan de betrouwbaarheidsdrempel.

De tijds- en geheugencomplexiteitsfuncties worden hier uiteraard apart behandeld.

3. Verwijder de parameters waarvoor de verwachte uitvoeringstijd te groot is. Hierbij

nemen we de verwachte uitvoeringstijd als de grootste verwachte uitvoeringstijd van

alle tijdscomplexiteitfuncties waar we nog rekening mee houden.

4. Verwijder de parameters die niet noodzakelijk zijn om de functies niet paarsgewijs te

laten correleren. Dit kan bijvoorbeeld voorkomen wanneer er voor O(n) en O(n3) te

onderscheiden een parameter 1000 is toegevoegd. Als er later ook nog een parameter

1500 wordt toegevoegd om O(n) en O(n2) te onderscheiden, dan is de parameter

1000 niet meer nodig. Aangezien we in de vorige stap misschien parameters verwij-

derd hebben waardoor er sommige complexiteitsfuncties wel paarsgewijs correleren,

zorgen we er hier voor dat het aantal paarsgewijs correlerende complexiteitsfuncties

niet stijgt. Dit proces is op dit moment nog vrij naïef uitgewerkt: de parameters

worden niet in een bepaalde volgorde overlopen of in meerdere volgordes getest, dus

het kan dat we een parameter verwijderen die er voor zorgt dat we meerdere andere

parameters niet kunnen verwijderen.

5. Geef de parameters terug. Dit kan op meerdere manieren gebeuren, afhankelijk

van hoe het programma ingesteld is. Als er bijvoorbeeld nog maar één (of geen)

complexiteitsfunctie correleert in de beide categorieën, dan zal het beschreven pro-

ces geen parameters bepaald hebben. Het is mogelijk om in te stellen dat er altijd

parameters teruggegeven worden, waarbij er dan teruggevallen wordt op het maxi-

mum van de parameters dat tot nu toe werd uitgevoerd plus de stapgrootte. Voor

meerdere parameters wordt dit maximum apart bepaald. Met andere woorden, de

verschillende maxima kunnen van verschillende uitvoeringen komen.

18

Page 27: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Daarnaast kan er ook gekozen worden hoeveel keer elke set parameters teruggegeven

wordt. Dit kan helpen om de variantie op de resultaten te verminderen (zoals

beschreven in sectie 2.2).

Verminderen van overhead

Om het probleem van de overhead dat in sectie 3.1.4 werd vermeld op te lossen, hebben

we ervoor gekozen om een pool van kernels op te starten bij het begin van onze analyse.

Op die manier staat er al een kernel klaar op het ogenblik dat een meting moet uitgevoerd

worden. Van zodra er een kernel uit deze pool gebruikt wordt, wordt er een nieuwe Jupyter

kernel opgestart en aan deze pool toegevoegd.

19

Page 28: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Hoofdstuk 4

Resultaten

Om de effectiviteit van onze implementatie te testen hebben we enkele experimenten

gedaan. We beginnen met een overzicht van de verschillende grafieken die we met de

metingen kunnen maken. Daarna bespreken we enkele programma’s waarvan we de com-

plexiteit inschatten. Uiteindelijk is er ook nog een bespreking van een bevraging die we bij

studenten hebben uitgevoerd om het nut van de grafieken in te schatten en een vergelij-

king te maken van manueel ingeschatte complexiteit tegenover de complexiteit ingeschat

door onze methode.

4.1 Geheugenmetingen

Om de verschillende manieren te tonen waarop de geheugenmetingen gebruikt kunnen

worden, hebben we een simpel voorbeeldprogramma geschreven met duidelijke verschillen

in geheugengebruik doorheen de tijd. Dit voorbeeldprogramma kan gevonden worden in

Listing 4.1.

Het belangrijkste element van het programma is de functie my_func2. Deze alloceert

twee lijsten van verschillende grootte. De grootste (b) wordt na de allocatie meteen

expliciet terug vrijgegeven. De kleinere lijst (a) wordt teruggegeven. Grafieken van de

metingen buiten de taal en binnen de taal kunnen respectievelijk gevonden worden in

Figuur 4.1 en Figuur 4.2.

Het is meteen duidelijk dat voor de metingen binnen de taal veel minder meetpunten

genomen moeten worden om een gelijkaardige figuur te krijgen. Buiten de taal moeten

we op vaste intervallen metingen uitvoeren, terwijl we binnen de taal enkel metingen

20

Page 29: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

1 import time

2

3 def my_func2 ( ) :

4 a = [ 1 ] ∗ (10 ∗∗ 6)

5 b = [ 2 ] ∗ (2 ∗ 10 ∗∗ 7)

6 del b

7 return a

8

9 def my_func ( ) :

10 my_func2 ( )

11 time . s l e e p ( 0 . 1 )

12 c = my_func2 ( )

13 time . s l e e p ( 0 . 1 )

14 my_func2 ( )

15 time . s l e e p ( 0 . 1 )

16 del c

17 time . s l e e p ( 0 . 1 )

18 my_func2 ( )

19 time . s l e e p ( 0 . 1 )

Listing 4.1: Voorbeeldprogramma voor de grafieken van de geheugenmetingen. De oproep

naar de functie my_func staat niet in de code, aangezien die door onze metingssoftware

dynamisch ingevoegd wordt.

21

Page 30: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.1: Geheugenmetingen buiten de taal van het voorbeeldprogramma in Listing

4.1.

Figuur 4.2: Geheugenmetingen binnen de taal van het voorbeeldprogramma in Listing

4.1. De rode lijnen op de grafiek tonen het verloop van de functies binnen het programma

(de bovenste lijn is de tijd waarin de my_func methode wordt opgeroepen, de andere lijnen

zijn de oproepen van my_func2).

22

Page 31: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

uitvoeren tussen het uitvoeren van twee statements. We kunnen echter ook zien dat de

metingen binnen de taal zelfs voor dit kleine voorbeeld al een duidelijke overhead met

zich meebrengen. De metingen buiten de taal zijn klaar na ongeveer 1.1 seconden, terwijl

er voor de metingen binnen de taal 1.3 seconden nodig zijn.

Een ander resultaat dat in deze metingen kan gezien worden is de invloed van de

garbage collector. In beide metingen kunnen we zien dat na de derde uitvoer van my_func2

de kleinere lijst onnodig in het geheugen blijft. De lijst wordt pas vrijgegeven bij het

expliciete verwijderen van de c variabele. Ook na de vierde uitvoer van my_func2 is dit

het geval.

We kunnen ook zien dat we bij de metingen binnen de taal een koppeling kunnen

maken tussen de functie-aanroepen en de geheugenmetingen doorheen de tijd.

4.2 Inschatten complexiteit

We bespreken eerst de resultaten die we behaald hadden met de implementatie van de

vijf technieken uit McGeoch et al. (2002). Daarna volgt een uitgebreidere bespreking van

onze eigen techniek voor het inschatten van de complexiteit.

4.2.1 Eerste implementatie

Om de technieken uit McGeoch et al. (2002) te testen, maakten we gebruik van simula-

tiefuncties. Deze geven een fictieve tijdsmeting terug. Een functie waar we meetpunten

uit haalden was bijvoorbeeld 10n2 + 5n+ 100 + random.randint(0, 10). Drie van de vijf

methoden vonden hiervoor een tijdscomplexiteit van O(n2). De “Guess Ratio” techniek

schat een ondergrens voor de complexiteit van O(n1.9869) met een betrouwbaarheid van

0.998. De “Guess Difference” techniek schat een bovengrens van O(n2.6499). Deze tech-

niek geeft geen informatie terug over de betrouwbaarheid, dus we rekenen dit antwoord

fout. De “Power” techniek schat een complexiteit van O(n2) met een betrouwbaarheid van

0.998. De “Box-Cox” techniek geeft een 95% betrouwbaarheidsinterval terug. Het interval

dat teruggeven wordt is echter [2.691, 2.776]. Dit beschouwen we dus ook als een fout

antwoord. De “Difference” techniek schat een complexiteit van O(n2) in. Deze techniek

geeft ook geen info terug over de betrouwbaarheid van dit resultaat, maar het is wel juist.

De totale gesimuleerde uitvoeringstijd was tussen de 0.5 en 2 minuten. Deze werd bepaald

23

Page 32: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

door de resultaten van de functie bij elkaar op te tellen en als milliseconden te beschou-

wen. De argumenten voor de simulatiefunctie werden nog manueel gekozen. Aangezien

we ons al snel realiseerden dat deze manier van werken niet zeer uitbreidbaar was (zoals

uitgelegd in sectie 3.2.1), hebben we verder geen andere experimenten uitgevoerd.

4.2.2 Tweede implementatie

In het tweede semester hebben we voor een oefening uit het vak Computationele Bio-

logie gebruik gemaakt van de techniek die we al hadden geïmplementeerd om tijds- en

geheugenmetingen te doen. Hiermee wilden we nagaan hoe nuttig deze metingen zijn

voor studenten om de complexiteit van code in te schatten. De oefening die de studenten

moesten oplossen was het implementeren van een algoritme om tussen twee strings met

lengtem en n (bijvoorbeeld DNA sequenties) een optimale alignment te vinden met lineair

geheugengebruik. Een naïeve implementatie die gebruikt maakt van dynamisch program-

meren heeft normaal O(m×n) als geheugencomplexiteit, maar in Hirschberg (1975) wordt

een algoritme beschreven met O(min(m,n)) geheugencomplexiteit. De resultaten van de

bevraging bij de studenten over het nut van de informatie die ze uit onze metingen kregen

zullen in sectie 4.3 besproken worden. Daarnaast hebben we deze oefening ook gebruikt

om aan de hand van eigen oplossingen te kijken hoe goed de complexiteit werd inge-

schat. In totaal hebben we vier oplossingen getest met eenvoudige implementaties. Deze

implementaties hadden telkens tijdscomplexiteit O(m × n). De geheugencomplexiteiten

van de vier implementaties (en waarvoor getest werd) waren O(min(m,n)), O(m), O(n)

en O(m × n). Voor de tijdscomplexiteit waren de complexiteitsfuncties waarvoor getest

werd de volgende: O(m × n), O(m) en O(n). Hierbij waren we vooral geïnteresseerd in

de geheugencomplexiteit. (De O(m) en O(n) tijdscomplexiteitsfuncties zouden vrij snel

geëlimineerd moeten worden.) Voor dit eerste experiment was de totale uitvoeringstijd

voor het experimenteel testen maximaal 10 minuten, werden de argumenten drie keer uit-

gevoerd, was de betrouwbaarheidslimiet gelijk aan 0.8 en stopten we niet eerder tot er in

beide categoriën nog maar één complexiteitsfunctie overschoot met een betrouwbaarheid

hoger dan de limiet (zoals uitgelegd in het “Kiezen van parameters” deel van sectie 3.2.2).

De gebruikte inputgroottes voor de verschillende programma’s kunnen gevonden worden

in Tabel 4.1.

De evoluties van de correlatiecoëfficienten van de lineaire regressies tussen de tijdscom-

24

Page 33: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Test Programma 1 Programma 2 Programma 3 Programma 4

1 500, 500 500, 500 500, 500 500, 500

2 500, 1500 500, 1500 500, 1500 500, 1500

3 1500, 500 1500, 500 1500, 500 1500, 500

4 500, 1500 500, 1500 500, 1500 500, 1500

5 1500, 500 1500, 500 1500, 500 1500, 500

6 500, 1500 500, 1500 500, 1500 500, 1500

7 1500, 500 2500, 1500 1500, 500 1500, 500

8 3500, 3500 1500, 5000 1500, 4000 4500, 1500

9 5000, 1500 2500, 1500 4000, 1500 4500, 1500

10 3500, 3500 1500, 5000 1500, 4000 4500, 1500

11 5000, 1500 2500, 1500 4000, 1500 4500, 3500

12 3500, 3500 1500, 5000 1500, 4000 7000, 1500

13 5000, 1500 4500, 7000 4000, 1500 4500, 15000

14 14500, 3500 2500, 13000 4000, 17000 4500, 4500

15 2500, 10500 11500, 4000 16000, 1500

16 2500, 18500 17000, 4000 4500, 3500

17 13500, 5000 7000, 1500

18 4500, 15000

Tabel 4.1: Gebruikte inputgroottes bij het uitvoeren van de testen voor het eerste experi-

ment. De eerste waarde is telkens m (de lengte van de eerste string) en de tweede waarde

is telkens n (de lengte van de tweede string). Alle programma’s hebben tijdscomplexiteit

O(m× n). Het eerste programma heeft geheugencomplexiteit O(min(m,n)). Het tweede

programma heeft geheugencomplexiteit O(m). Het derde programma heeft geheugencom-

plexiteit O(n). Het vierde programma heeft geheugencomplexiteit O(m × n). Niet alle

inputgroottes komen drie keer voor in de lijst. De eerste inputgroottes worden niet her-

haald omdat ze niet door de functie om de parameters te kiezen teruggegeven worden. De

paar laatste elementen van de lijst kunnen ook minder dan drie keer voorkomen omdat

de uitvoering afgebroken werdt door de tijdslimiet.

25

Page 34: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.3: Evolutie van de correlatiecoëfficienten voor de tijdscomplexiteitsfuncties na

elke uitgevoerde test van het eerste experiment (algoritme van Hirschberg, variabele ge-

heugencomplexiteit). Elk programma heeft duidelijk tijdscomplexiteit O(m× n).

plexiteitsfuncties toegepast op de inputgroottes en de meetresultaten na elke uitgevoerde

test voor de vier programma’s zijn te vinden in Figuur 4.3. Ter referentie zijn ook de

uitvoeringstijden te zien in Figuur 4.4. We kunnen zien dat voor elk programma duidelijk

O(m× n) als tijdscomplexiteit gevonden wordt. De tijdscomplexiteitsfuncties die lineair

zijn in één van de parameters, worden over het algemeen snel uitgesloten.

Voor de geheugencomplexiteitsfuncties zijn de evoluties van de correlatiecoëfficienten

te vinden in Figuur 4.5. Ter referentie zijn ook weer de resultaten van de metingen

te vinden in Figuur 4.6. Voor het geheugengebruik kunnen we zien dat de methode

het minder goed doet. Voor het tweede, derde en vierde programma vindt de methode

telkens het juiste resultaat. Bij het eerste programma is het resultaat ook juist, maar

een stuk minder duidelijk. De correlatiecoëfficient ligt een stuk lager, en dit voor alle

complexiteitsfuncties. Vermoedelijk is er bij het eerste programma meer variantie op het

geheugengebruik en zou een langere uitvoer een betrouwbaarder resultaat opleveren.

Na het uitvoeren van dit experiment met een vaste tijdslimiet hebben we een expe-

26

Page 35: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.4: Gemeten uitvoeringstijd voor elke uitgevoerde test van het eerste experiment.

De gebruikte parameters kunnen gevonden worden in tabel 4.1.

27

Page 36: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.5: Evolutie van de correlatiecoëfficienten voor de geheugenscomplexiteitsfuncties

na elke uitgevoerde test van het eerste experiment.

28

Page 37: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.6: Gemeten geheugengebruik voor elke uitgevoerde test van het eerste experi-

ment. De gebruikte parameters kunnen gevonden worden in tabel 4.1.

29

Page 38: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

riment uitgevoerd om de impact van de maximale uitvoeringstijd op de evolutie van de

correlatiecoëfficienten in te schatten. Hierbij werden de vier programma’s 5 minuten, 10

minuten, 20 minuten en zonder tijdslimiet uitgevoerd. Hierbij stoppen we niet als er maar

één complexiteitsfunctie overblijft waarvoor de correlatiecoëfficient boven de ingestelde li-

miet ligt, behalve in het geval zonder tijdslimiet (anders hebben we geen stopconditie).

De resultaten voor de tijdscomplexiteitsfuncties kunnen respectievelijk gevonden worden

op Figuur 4.7, Figuur 4.9, Figuur 4.11 en Figuur 4.13. De resultaten voor de geheugen-

complexiteitsfuncties kunnen respectievelijk gevonden worden op Figuur 4.8, Figuur 4.10,

Figuur 4.12 en Figuur 4.14.

We kunnen zien dat bij dit experiment de extra tijd voor het inschatten van de tijds-

complexiteit niet veel verschil maakt. De methode vindt altijd snel de (juiste) tijdscom-

plexiteit O(m × n). Voor het inschatten van de geheugencomplexiteit kunnen we zien

dat deze extra tijd wel helpt. Bij 5 en 10 minuten (Figuur 4.8 en Figuur 4.10) toont de

schatting voor enkele programma’s een vreemd gedrag. Bij 20 minuten (Figuur 4.12) zien

we dat voor alle programma’s de juiste complexiteit zich systematisch bovenaan bevindt.

Ook voor het experiment zonder tijdslimiet (Figuur 4.14) is dit het geval. We zien wel

nog steeds dat het lastiger is om de O(min(m,n)) complexiteitsfunctie te onderscheiden

dan de andere complexiteitsfuncties.

Iets anders dat we kunnen opmerken is dat de figuren van de experimenten met meer

tijd geen uitbreiding zijn op de figuren van de experimenten met minder tijd. Dit komt

door de variantie op de meetresultaten. Door deze variantie worden er ander inputgroottes

gekozen en kunnen de berekende correlatiecoëfficienten verschillen.

Traag stijgende functies

Bij een volgend experiment hebben we bekeken hoe goed onze techniek functies kan in-

schatten die zeer traag stijgen. Hiervoor hebben we getest of onze techniek voor binair

zoeken in een gesorteerde lijst een O(log(n)) tijdscomplexiteit kan vinden. Op Figuur

4.15 kunnen we zien dat de methode er niet zo goed in slaagt om O(log(n)) als tijdscom-

plexiteitsfunctie te vinden. Als we kijken naar Figuur 4.16 dan kunnen we zien dat de

tijdsmetingen zich ook nagenoeg lineair gedragen. De implementatie is nochtans duidelijk

logaritmisch (te zien op Listing 4.2). Het probleem bevindt zich ook niet in de gekozen

inputgroottes. De initiële inputgrootte is 50000. Na deze meting kiest onze techniek als

30

Page 39: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.7: Evolutie van de correlatiecoëfficienten voor de tijdscomplexiteitsfuncties na

elke uitgevoerde test van het geval van het tweede experiment met 5 minuten als tijdsli-

miet. Elk programma heeft duidelijk tijdscomplexiteit O(m× n).

31

Page 40: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.8: Evolutie van de correlatiecoëfficienten voor de geheugenscomplexiteitsfuncties

na elke uitgevoerde test van het geval van het tweede experiment met 5 minuten als

tijdslimiet.

32

Page 41: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.9: Evolutie van de correlatiecoëfficienten voor de tijdscomplexiteitsfuncties na

elke uitgevoerde test van het geval van het tweede experiment met 10 minuten als tijds-

limiet. Elk programma heeft duidelijk tijdscomplexiteit O(m× n).

33

Page 42: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.10: Evolutie van de correlatiecoëfficienten voor de geheugenscomplexiteitsfunc-

ties na elke uitgevoerde test van het geval van het tweede experiment met 10 minuten als

tijdslimiet.

34

Page 43: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.11: Evolutie van de correlatiecoëfficienten voor de tijdscomplexiteitsfuncties

na elke uitgevoerde test van het geval van het tweede experiment met 20 minuten als

tijdslimiet. Elk programma heeft duidelijk tijdscomplexiteit O(m× n).

35

Page 44: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.12: Evolutie van de correlatiecoëfficienten voor de geheugenscomplexiteitsfunc-

ties na elke uitgevoerde test van het geval van het tweede experiment met 20 minuten als

tijdslimiet.

36

Page 45: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.13: Evolutie van de correlatiecoëfficienten voor de tijdscomplexiteitsfuncties na

elke uitgevoerde test van het geval van het tweede experiment waar er geen tijdslimiet

was ingesteld. Elk programma heeft duidelijk tijdscomplexiteit O(m× n).

37

Page 46: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.14: Evolutie van de correlatiecoëfficienten voor de geheugenscomplexiteitsfunc-

ties na elke uitgevoerde test van het geval van het tweede experiment waar er geen tijds-

limiet was ingesteld.

38

Page 47: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.15: Evolutie van de correlatiecoëfficienten voor de tijdscomplexiteitsfuncties van

het binair zoeken experiment.

volgende inputgrootte 150000 omdat er vanaf dan een merkbaar verschil zit in de ver-

wachte resultaten. Dit verschil zit ook in deze resultaten en de twee complexiteitsfuncties

correleren niet meer met elkaar, waardoor er vanaf dan enkel met de stapgrootte wordt

vermeerderd. We vermoeden dat deze lineairiteit te wijten is aan een overhead van de

Jupyter kernel. We zijn er niet in geslaagd deze overhead weg te werken. De argumenten

worden in een aparte uitvoering waarop geen metingen uitgevoerd in variabelen ingeladen,

dus de constructie van de lijst is niet het probleem. Het eigenlijke probleem hebben we

niet kunnen vinden.

Java programma’s

Om de effectiveit van onze techniek te testen op Java programma’s hebben we als laatste

experiment een oefening uit het vak Algoritmen en Datastructeren gebruikt. De oefening

bestond uit het implementeren van het Stable Marriage Problem. In Gale and Shapley

(1962) werd dit probleem uitgelegd en werd meteen ook een algoritme gegeven voor een

optimale oplossing die in het slechtste geval in kwadratische tijd gevonden kan worden.

39

Page 48: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.16: Tijdsmetingen voor het binair zoeken experiment.

1 def zoeken ( element , l i j s t ) :

2 begin = 0

3 e inde = len ( l i j s t ) − 1

4 while begin <= einde :

5 m = ( begin + einde ) // 2

6 i f l i j s t [m] == element :

7 return m

8 e l i f l i j s t [m] > element :

9 e inde = m − 1

10 else :

11 begin = m + 1

12 return −1

Listing 4.2: Implementatie van binair zoeken waarvan de tijdscomplexiteit gezocht wordt.

40

Page 49: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.17: Evolutie van de correlatiecoëfficienten voor de tijdscomplexiteitsfuncties van

het Stable Marriage Problem experiment.

Hierbij zijn we meteen ook een interessant probleem tegengekomen. Onze eerste imple-

mentatie om testgevallen te genereren deed dit op een volledig willekeurige manier. Dit

resulteerde echter in slechte resultaten van onze methode. Wanneer we een tweede imple-

mentatie maakten om testgevallen te genereren hebben we ons gebaseerd op een methode

om het slechtste geval te construeren zoals voorgesteld in Kapur and Krishnamoorthy

(1985). Na deze aanpassing vindt onze methode voor een Java implementatie van het

Gale-Shapley algoritme een duidelijke O(n2) tijdscomplexiteit. In Figuur 4.17 kan de

evolutie van de correlatiecoëfficienten van de tijdscomplexiteitsfuncties gevonden worden

voor een oplossing van een student voor deze opdracht.

4.3 Feedback van studenten

Zoals vermeld in sectie 4.2.2 hebben we ook een bevraging gedaan onder de studenten die

het opleidingsonderdeel Computationele Biologie gevolgd hebben in academiejaar 2017–

2018. De code van de studenten werd 5 minuten uitgevoerd en op die tijd draaiden we

41

Page 50: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

zoveel mogelijk testen. De parameters van de testen werden nog niet bepaald op de manier

zoals beschreven in 3.2.2. Ze werden gekozen door te beginnen bij 500 en er telkens 500

bij op te tellen. De parameters m en n waren dus ook altijd gelijk. Van deze testen werd

de uitvoeringstijd en het maximum geheugengebruik bijgehouden. Elke student kreeg van

drie andere studenten telkens de code samen met 4 grafieken. De eerste twee grafieken

waren voor de gemeten tijd. Hierbij werden voor een grafiek de resultaten van de andere

student alleen op een grafiek gezet. De tweede grafiek toonde de resultaten van de andere

student samen met de resultaten van alle andere studenten. De volgende twee grafieken

waren hetzelfde maar dan voor geheugengebruik in plaats van tijd. Een voorbeeld van

de grafieken die aan de studenten gegeven werden kan gezien worden in Figuur 4.18. De

drie outliers op deze grafieken verklaren we door het feit dat er op het moment dat dit

experiment uitgevoerd werd, we nog niet keken naar fouten tijdens de uitvoering van de

code. De code die uitgevoerd werd kwam immers van correcte oplossingen van Dodona

kwam. Een latere heruitvoering van het experiment, wanneer fouten binnen de uitvoering

wel opgevangen werden, maakte dan echter duidelijk dat deze 3 indieningen toch nog

fouten bevatten zodra de inputgrootte een stuk groter werd dan de inputgrootte van de

testen die op Dodona werden uitgevoerd.

Aan de studenten werd dan gevraagd een inschatting te geven van de tijds- en geheu-

gencomplexiteit van de code. Daarnaast waren er ook 3 vragen om in te schatten hoe de

studenten de gegeven grafieken gebruikt hadden. Deze vragen waren als volgt:

• Hoe heb je de gegeven grafieken gebruikt?

• Wat vond je nuttig aan de gegeven grafieken?

• Wat zou je verbeteren aan de gegeven grafieken?

Bij de eerste vraag kwamen 5 verschillende antwoorden naar voor. In Tabel 4.2 kunnen

deze gevonden worden, samen met het aantal studenten die dat antwoord gaven. We

kunnen zien dat er veel studenten de grafieken gebruikt hebben om de complexiteit in te

schatten of om de snelheid en het geheugengebruik te vergelijken.

De tweede vraag had een duidelijk dominerend antwoord, namelijk dat het handig

was om de oplossing te positioneren tegenover de andere studenten. Voor de volledigheid

kunnen de verschillende antwoorden en het aantal keer dat ze gegeven werden, gevonden

worden in Tabel 4.3.

42

Page 51: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 4.18: De linkse grafieken tonen de metingen van één indiening. De rechtse grafieken

tonen deze metingen in vergelijking met de rest van de studenten. De outliers op de

rechtse grafieken komen door een gebrek aan afhandeling van fouten binnen de code van

de studenten.

Antwoord Aantal

Prestaties van het programma bekijken. 8

Vergelijken van prestaties met andere studenten. 19

Het bepalen van de complexiteit. 18

Kijken of het programma in lineaire ruimte geïmplementeerd is. 2

Checken van manueel bepaalde complexiteit. 2

Tabel 4.2: Antwoorden van studenten op de vraag “Hoe heb je de gegeven grafieken

gebruikt?”

43

Page 52: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Antwoord Aantal

Handig om de oplossing te positioneren tegenover de andere studenten. 33

Nuttig bij het vormen van een score. 3

Individuele tijdsgrafiek gaf duidelijk complexiteit weer. 2

Zelf geen metingen te moeten uitvoeren. 2

Extra vertrouwen in de zelf bekomen meetresultaten. 2

De grafieken. 5

Tabel 4.3: Antwoorden van studenten op de vraag “Wat vond je nuttig aan de gegeven

grafieken?”

Bij de derde vraag waren er veel verschillende antwoorden. Deze kunnen gevonden

worden in Tabel 4.4. Het meest voorkomende antwoord had betrekking op de uitschie-

ters. De 3 inzendingen waar fouten in zaten konden zeer veel testgevallen afwerken omdat

ze vroeg in hun uitvoering afgebroken werden. Dit maakte de grafieken een stuk min-

der leesbaar. Dit vonden veel studenten duidelijk niet handig bij het gebruiken van de

grafieken.

Naast de vragen over de grafieken, werd er ook van elke student gevraagd om een

inschatting te maken van de tijds- en geheugencomplexiteit van de stukken code die ze

moesten beoordelen. Hierdoor hadden we na de verwerking van de ingediende complexitei-

ten ongeveer 3 inschattingen van de complexiteit per oplossing. (Het aantal inschattingen

is niet exact 3 omdat sommige studenten geen antwoord hadden gegeven of niet geant-

woord hadden met een complexiteitsfunctie.)

Van de tijdscomplexiteitsfuncties kwam voor 46 van de 53 oplossingen de complexi-

teit ingeschat door onze techniek en de complexiteit ingeschat door de studenten volledig

overeen. Van de zeven oplossingen waar de manuele inschattingen en de automatische

inschatting niet volledig overeen kwam waren er zes waar er maar één manuele inschat-

ting niet overeenkwam. Deze zijn duidelijke fouten van de student (bijvoorbeeld een

inschatting van O(m+ n) voor een algoritme dat minstens O(m× n) is). Bij de zevende

oplossing waar de manuele inschattingen en de automatische inschattingen niet overeen-

kwamen is het minder duidelijk: er zijn twee inschattingen waarvan er één duidelijk fout

is (O(2m∗n)) en de andere een inschatting van O(m×n) is (van deze student hadden maar

twee andere studenten de code gekregen). De code van de student is zeer simpel; deze

44

Page 53: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Antwoord Aantal

Grafieken/vergelijkingen toevoegen met de eigen code. 2

Layout van de PDFs verbeteren. 3

Interactief de configuratie van de grafieken kunnen aanpassen. 1

Resultaten consistenter maken. 1

Langere tijdsduur. 3

Rekening houden met uitschieters bij de grafieken. 5

Lijnen in plaats van punten voor de andere studenten. 1

Ook informatie over de correctheid van de code toevoegen. 1

Ook de data geven waarop de grafieken gebaseerd zijn. 2

Meer uitleg geven bij de grafieken. 1

Een oplossing vinden voor de grijze wolk van resultaten. 2

Een onderscheid maken tussen de n beste resultaten en de rest. 1

Voor de geheugencomplexiteit 2 grafieken i.p.v. min(m,n). 2

O-notatie van de complexiteit meeleveren. 1

Tabel 4.4: Antwoorden van studenten op de vraag “Wat zou je verbeteren aan de gegeven

grafieken?”

45

Page 54: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

geeft het probleem door aan de BioPython library. De automatische inschatting geeft een

geheugencomplexiteit van O(m2 ∗ n2) met een betrouwbaarheid van 0.9940, maar de be-

trouwbaarheid van O(m×n) is 0.9930. Het is hier dus niet meteen duidelijk wat het juiste

antwoord is, maar het lijkt ons zeer onwaarschijnlijk dat de BioPython implementatie als

tijdscomplexiteit O(m2 ∗ n2) zou hebben.

Voor de geheugencomplexiteitsfuncties kwam voor 34 van 53 oplossingen de complexi-

teit ingeschat door de methode en de complexiteit ingeschat door de studenten volledig

overeen. Van de negentien oplossingen waar niet alle manueel ingeschatte complexiteiten

overeen komen met de automatisch ingeschatte complexiteit waren er vijf waar er maar één

manuele inschatting niet overeen kwam. Verdere inspectie van deze inschattingen maakte

duidelijk dat de automatische inschatting juist is en de studenten een foute inschatting

gemaakt hadden. Bij de veertien resterende oplossingen waren er nooit 2 studenten die

dezelfde inschatting hadden gemaakt. Bij acht van deze oplossingen geeft de automatische

inschatting van de complexiteit een duidelijk antwoord, dat ook lijkt te kloppen als we

zelf de oplossing bekijken. Voor de resterende zes oplossingen geeft ook de automatische

inschatting geen duidelijk antwoord: de betrouwbaarheden zijn vrij laag of liggen dicht

bij elkaar. Hierbij valt het ook op dat de studenten vooral bij deze oplossingen als in-

schattingen complexiteitsfuncties hebben gegeven waar de automatische inschatter niet

op testte (bijvoorbeeld O(m+ n)). Er is één oplossing waar de automatische inschatting

er zelfs niet in geslaagd is om tot een zinnig antwoord te komen. Hierbij komt voor geen

enkele complexiteitsfunctie de betrouwbaarheid boven de 0.1 uit. Dit is echter ook niet

onlogisch, aangezien het geheugengebruik zich vreemd gedraagt. Zo heeft de code voor

de parameters (2500, 8000) een maximum geheugengebruik gehad van 1.9 MB, maar voor

de parameters (3000, 8500) een maximum geheugengebruik gehad van 1.0 MB.

46

Page 55: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Hoofdstuk 5

Toekomstig werk

Uit het gebruik van Jupyter kernels voor het uivoeren van code die op Dodona wordt

ingediend, ontstond het idee voor het ontwikkelen van TESTed, een framework voor

het educatief test van software dat moet toelaten om programmeeropdrachten op een

programmeertaal-onhafhankelijke manier te evalueren. Naast feedback over correctheid

zal TESTed ook feedback geven over computationele complexiteit van ingediende oplos-

singen, volledigheid van een testsuite inschatten en testen genereren op basis van voor-

beeldoplossingen.

In dit afsluitende hoofdstuk geven we een korte bespreking van het TESTed framework.

5.1 Probleemstelling

Algemeen gezien wordt het testen van software uitgevoerd om de kwaliteit van de software

die getest wordt in te schatten. Wanneer dit gebeurt in een educatieve context — waarbij

studenten oplossingen indienen voor specifieke programmeeropdrachten — dan spreken

we van het educatief testen van software (educational software testing). Het educatief

testen van software vindt zelf zijn oorsprong in programmeren in competitieverband,

waarbij deelnemers van programmeerwedstrijden software moeten schrijven die voldoet

aan bepaalde specificaties. Daarbij worden de ingediende oplossingen door een judge

afgetoetst aan een vooraf gedefinieerde reeks van testgevallen. Bij wedstrijden blijven

deze testgevallen en de individuele resultaten van de testen geheim. In een educatieve

context krijgen judges de bijkomende taak om nuttige en uitgebreide feedback te geven

die de studenten naar een correcte oplossing moet leiden.

47

Page 56: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Bij het testen van software is één van de doelstellingen van de ontwikkelaars van judges

om de evaluatie onafhankelijk te maken van de taal waarin de oplossing geprogrammeerd

werd. Hiervoor bestaan verschillende strategieën: triviaal is bijvoorbeeld om invoer te

geven aan de programmeur, die dan de corresponderende uitvoer moet indienen. Hierbij

moet de judge de code niet zelf uitvoeren. Een uitbreiding hiervan — die in de praktijk het

vaakst gebruikt wordt — is om de ingediende programmacode te evalueren via IO: de judge

voert de ingediende programmacode uit, waarbij het proramma invoer in tekstformaat kan

inlezen vanaf stdin en de corresponderende uitvoer in tekstformaat dient uit te schrijven

naar stdout. In een educatieve context leggen beide strategieën echter grote restricties

op, zowel aan de manier waarop de oplossing moet geïmplementeerd worden maar vooral

aan de diepgang van feedback die kan geproduceerd worden. Bij de eerste strategie kan er

helemaal geen feedback gegeven worden over de kwaliteit van de software zelf. De tweede

aanpak verplicht de ingediende software om gegevens te lezen uit stdin en resultaten te

schrijven naar stdout, wat deels de focus wegneemt van de eigenlijke opdracht en weinig

aanknopingspunten biedt om gedetailleerde feedback te geven.

De moderne softwareontwikkelingspraktijk is uitermate test-gedreven (Beck (2003)).

Dit heeft ervoor gezorgd dat er zeer veel tools en frameworks bestaan om software te tes-

ten. Het gebruik hiervan in een educatieve context heeft echter als belangrijkste beperking

dat alle tools en frameworks programmeertaalafhankelijk zijn. Het intelligente tutoring-

systeem Dodona (dodona.ugent.be) dat werd ontwikkeld aan het Computational Biology

Lab van de Universiteit Gent heeft als doel om enkele problemen met bestaande educa-

tieve judges aan te pakken. Op dit moment worden er in Dodona echter nog altijd een

afzonderlijke judges geschreven voor elke nieuwe programmeertaal die ondersteund wordt.

Deze judges delen heel veel eigenschappen en hebben vaak een gelijkaardige architectuur,

waardoor het implementeren van nieuwe judges resulteert in veel dubbel werk.

5.2 Doelstelling

De opzet van dit onderzoek is om een programmeertaal-onafhankelijke judge te schrijven

voor Dodona, die moet toelaten om met één enkele specificatie van een programmeerop-

dracht, oplossingen te evalueren die in een groot aantal programmeertalen kunnen inge-

diend worden. Naast het evalueren van de correctheid van de ingediende oplossingen, zal

48

Page 57: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

dit programmeertaal-onafhankelijk framework voor het educatief testen van software –

TESTed genaamd – ook een inschatting kunnen maken van de computationele complexi-

teit (tijds- en geheugengebruik), analyses kunnen uitvoeren van de compleetheid van de

testen en automatisch testgevallen kunnen genereren op basis van voorbeeldoplossingen.

5.3 Methodologie

De innovatieve architectuur van TESTed (Figuur 5.1) scheidt het plannen van de tests

en het genereren van de feedback van het uitvoeren van de programmacode en het eva-

lueren van de toestand na het uitvoeren van een testgeval. Het uitvoeren van de code

gebeurt door een zogenaamde execution kernel. Dit is in essentie een Jupyter kernel, de

centrale component die gebruikt wordt door Jupyter Notebooks (Kluyver et al. (2016)).

Hierdoor komt het ondersteunen van nieuwe programmeertalen voor TESTed neer op het

ontwikkelen van Jupyter kernels voor die talen. Op dit moment staan er al meer dan 100

Jupyter kernels officiëel geregistreerd (Project Jupyter (2018)).

Er zijn drie manieren waarop TESTed software kan evalueren. De eerste manier is het

uitvoeren van testen op de code binnen de execution kernel. Dit correspondeert met de

traditionele manier van het testen van software, waar de software en het testframework in

dezelfde taal geschreven zijn. Op die manier garandeert TESTed dat testen die inherent

programmeertaal-specifiek zijn nog steeds ondersteund worden. Het doel van werkpakket

1 (WP1) is om een werkend prototype van TESTed te ontwikkelen zoals beschreven in

Figuur 5.1. Hierbij geldt wel de restrictie dat een test nog steeds voor een specifieke

programmeertaal moet geschreven worden. Dit zal ons echter toelaten om het prototype

in de praktijk te gebruiken als proof-of-concept voor de voorgestelde architectuur van

TESTed. We beschikken immers over een grote verzameling programmeeropdrachten

waarvan we kunnen evalueren hoe gemakkelijk ze kunnen overgezet worden naar TESTed.

Tegelijkertijd zullen we ook op zoek gaan naar de gemeenschappelijke eigenschappen en de

verschillende teststrategieën in de bestaande Dodona judges. Daarnaast zal het initiële

prototype van TESTed dienen als basis voor het onderzoek en de ontwikkeling van de

andere drie werkpakketten.

Ik heb er uitermate veel vertrouwen in dat de voorgestelde architectuur van TESTed

ook in de praktijk zal werken. Ik maak immers ook gebruik maak Jupyter kernels in mijn

49

Page 58: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Figuur 5.1: Architectuur van het software testing framework TESTed die het inplannen

van testen en het genereren van feedback scheidt van het uitvoeren van de code en de

testen en het evalueren van de toestand na het uitvoeren van de testen.

50

Page 59: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

masterproef, waar ik computationele benaderingen voor het inschatten van computatio-

nele complexiteit aan het onderzoeken ben. Hiervoor heb ik al een uitbreiding van de

Jupyter kernels ontwikkeld die tijds- en geheugenmetingen uitvoert op de uitgevoerde

code. Dit onderzoek zal verder uitgewerkt worden in werkpakket 2 (WP2) maar is ook

de inspiratie geweest voor het uittekenen van de architectuur van TESTed.

Het uiteindelijke doel van TESTed is om zoveel mogelijk softwaretesten te kunnen

uitvoeren op een manier die onafhankelijk is van de programmeertaal van de ingediende

oplossing. Het verkennen van dit idee is de focus van een derde werkpakket (WP3),

waar we zowel abstracte evaluatie als evaluatie binnen een afzonderlijke evaluation ker-

nel zullen onderzoeken. Deze twee benaderingen voor het evalueren van programma-

code vereisen wederzijdse transformaties tussen de programmeertaal-specifieke voorstel-

ling van de toestand na het uivoeren van een test op de programmacode en een abstracte

programmeertaal-onafhankelijke representatie hiervan.

De ervaring van het Computational Biology Lab met het implementeren van Dodona

judges en het ontwikkelen van programmeeropdrachten, leert ons dat de meeste opdrach-

ten voor educatieve doeleinden zich zeer goed lenen om zowel de verwachte als de ge-

genereerde toestand na het uitvoeren van een test op de ingediende programmacode te

vertalen naar een abstracte (programmeertaal-onafhankelijke) voorstelling. Om dit uit

te werken en aan de praktijk af te toetsen, kunnen we gebruik maken van de meer dan

2.000 programmeeropdrachten die reeds voor Dodona ontwikkeld werden en van de meer

dan 2 miljoen oplossingen die reeds werden ingediend voor deze opdrachten. Op basis

daarvan kunnen we experimenteel de mogelijke beperkingen van de abstracte voorstelling

in kaart brengen, om zo het abstractieproces gradueel te verfijnen en uit te breiden. We

verwachten dat slechts voor een aantal specifieke opdrachten zal blijken dat de verwach-

te/gegenereerde toestand moeilijk te abstraheren valt, of dat die zo programmeertaal-

specifiek is dat het niet de moeite loont om daarvoor een abstracte voorstelling te maken.

Voor deze gevallen blijft immers altijd de mogelijkheid bestaan om een programmeertaal-

specifieke evaluatie uit te voeren binnen de execution kernel (WP1).

Jupyter kernels zijn uitermate uitbreidbaar en hun integratie in de architectuur van

TESTed laat toe om bepaalde aspecten van het testen van software op een generieke ma-

nier te implementeren. Als concreet voorbeeld hiervan zullen we in WP2 een uitbreiding

ontwikkelen van de execution kernel, waarmee de computationele complexiteit van inge-

51

Page 60: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

diende oplossingen kan ingeschat worden. Dit deel van het project is een verderzetting

van het onderzoek dat ik momenteel uitvoer in het kader van mijn masterproef. Voor het

inschatten van de computationele complexiteit voeren we een lineaire regressie-analyse

uit tussen de gemeten resultaten en abstracte getallen die berekend worden uit de functie

van de verwachte complexiteit en de complexiteitsparameters van de uitgevoerde testen.

In het laatste werkpakket (WP4) focussen we tenslotte op het proces waarmee testen

gespecificeerd worden. Omdat zelfs voor eenvoudige software het aantal mogelijke testen

schier oneindig is, moet voor het testen van software een strategie gebruikt worden om tes-

ten te selecteren die binnen een realistische tijd kunnen uitgevoerd worden én die de kans

maximaliseren om bugs of andere defecten in de software te vinden. Vertrekkend van de

test coverage analyse voor een verzameling van 700+ bestaande programmeeropdrachten

en hun voorbeeldoplossingen in Python, onderzoeken we verschillende strategieën waarmee

TESTed op een dynamische manier testen kan genereren, complementair aan de manueel

gegenereerde testen die nu gebruikt worden. In het bijzonder laten we ons hiervoor inspi-

reren door symbolische uitvoering (King (1976); Khurshid et al. (2003)), bounded model

checking (Anielak et al. (2015)) en abstracte interpretatie (Nielson (1989)).

52

Page 61: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Hoofdstuk 6

Conclusie

In deze thesis hebben we een techniek ontwikkeld om automatisch tijds- en geheugen-

metingen te nemen. Deze metingen kunnen genomen worden op een programmeertaal-

onafhankelijke manier. Verder ontwikkelden we een techniek om de computationele com-

plexiteit van programma’s in te schatten. De methode werkt goed voor de tijdscomplexi-

teit en redelijk goed voor de geheugencomplexiteit. Voor complexiteitsfuncties kleiner

dan O(n) zorgt de overhead van de Jupyter kernels er voor dat de metingen niet accuraat

genoeg genomen kunnen worden om de complexiteit goed in te schatten. Een bevra-

ging bij studenten toonde aan dat de metingen een nuttig instrument vormen om code te

beoordelen en om de complexiteit van de code manueel in te schatten.

Mogelijke verbeteringen bevinden zich zo goed als allemaal in het verbeteren van

de nauwkeurigheid van de metingen. Zo zou bijvoorbeeld de lineaire overhead van de

Jupyter kernel wegwerken toelaten om de complexiteitsfuncties kleiner dan O(n) beter in

te schatten.

53

Page 62: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Bibliografie

Anielak, G., Jakacki, G. and Lasota, S. (2015), ‘Incremental test case generation using

bounded model checking: an application to automatic rating’, International Journal on

Software Tools for Technology Transfer 17(3), 339–349.

Beck, K. (2003), Test-driven development: by example, Addison-Wesley Professional.

Danielsson, N. A. (2008), ‘Lightweight semiformal time complexity analysis for purely

functional data structures’, SIGPLAN Not. 43(1), 133–144.

Gale, D. and Shapley, L. S. (1962), ‘College admissions and the stability of marriage’,

The American Mathematical Monthly 69(1), 9–15.

Goldsmith, S. F., Aiken, A. S. and Wilkerson, D. S. (2007), Measuring empirical computa-

tional complexity, in ‘Proceedings of the the 6th Joint Meeting of the European Software

Engineering Conference and the ACM SIGSOFT Symposium on The Foundations of

Software Engineering’, ESEC-FSE ’07, ACM, New York, NY, USA, pp. 395–404.

Gulwani, S., Mehra, K. K. and Chilimbi, T. (2009), ‘Speed: Precise and efficient static

estimation of program computational complexity’, SIGPLAN Not. 44(1), 127–139.

Healy, C., Sjödin, M., Rustagi, V., Whalley, D. and Engelen, R. v. (2000), ‘Suppor-

ting timing analysis by automatic bounding of loop iterations’, Real-Time Systems

18(2), 129–156.

Hirschberg, D. S. (1975), ‘A linear space algorithm for computing maximal common sub-

sequences’, Communications of the ACM 18(6), 341–343.

Kapur, D. and Krishnamoorthy, M. S. (1985), ‘Worst-case choice for the stable marriage

problem’, Information processing letters 21(1), 27–30.

54

Page 63: Computationelebenaderingenvoor deductievandecomputationele ...€¦ · Voor het online leerplatform Dodona was een gewenste feature geïdentificeerd om het nemenvanaccuratetijds-engeheugenmetingentoetevoegen.

Khurshid, S., Păsăreanu, C. S. and Visser, W. (2003), Generalized symbolic execution for

model checking and testing, in ‘International Conference on Tools and Algorithms for

the Construction and Analysis of Systems’, Springer, pp. 553–568.

King, J. C. (1976), ‘Symbolic execution and program testing’, Communications of the

ACM 19(7), 385–394.

Kluyver, T., Ragan-Kelley, B., Pérez, F., Granger, B. E., Bussonnier, M., Frederic, J.,

Kelley, K., Hamrick, J. B., Grout, J., Corlay, S. et al. (2016), Jupyter notebooks-a

publishing format for reproducible computational workflows., in ‘ELPUB’, pp. 87–90.

McGeoch, C. C. (1987), Experimental analysis of algorithms., Technical report, Carnegie-

Mellon Univ Pittsburgh PA Dept Of Computer Science.

McGeoch, C., Sanders, P., Fleischer, R., Cohen, P. R. and Precup, D. (2002), Using

finite experiments to study asymptotic performance, in ‘Experimental algorithmics’,

Springer, pp. 93–126.

Nielson, F. (1989), ‘Two-level semantics and abstract interpretation’, Theoretical Compu-

ter Science 69(2), 117–242.

Project Jupyter (2018), ‘List of available jupyter kernels’.

URL: https://github.com/jupyter/jupyter/wiki/Jupyter-kernels

55