-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Merge pull request #30 from SOPSLab/metrics-demo
- Loading branch information
Showing
18 changed files
with
2,533 additions
and
22 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
/* Copyright (C) 2019 Joshua J. Daymude, Robert Gmyr, and Kristian Hinnenthal. | ||
* The full GNU GPLv3 can be found in the LICENSE file, and the full copyright | ||
* notice can be found at the top of main/main.cpp. */ | ||
|
||
#include "alg/demo/metricsdemo.h" | ||
|
||
#include <algorithm> // for std::max | ||
#include <cmath> // for std::sqrt, std::pow | ||
|
||
MetricsDemoParticle::MetricsDemoParticle(const Node& head, | ||
const int globalTailDir, | ||
const int orientation, | ||
AmoebotSystem& system, | ||
const int counterMax) | ||
: AmoebotParticle(head, globalTailDir, orientation, system), | ||
_counter(counterMax), | ||
_counterMax(counterMax) { | ||
_state = getRandColor(); | ||
} | ||
|
||
void MetricsDemoParticle::activate() { | ||
// First decrement the particle's counter. If it's zero, reset the counter and | ||
// get a new color. | ||
_counter--; | ||
if (_counter == 0) { | ||
_counter = _counterMax; | ||
_state = getRandColor(); | ||
} | ||
|
||
// Next, handle movement. If the particle is contracted, choose a random | ||
// direction to try to expand towards, but only do so if the node in that | ||
// direction is unoccupied. Otherwise, if the particle is expanded, simply | ||
// contract its tail. | ||
if (isContracted()) { | ||
int expandDir = randDir(); | ||
if (canExpand(expandDir)) { | ||
expand(expandDir); | ||
} else if (hasObjectAtLabel(expandDir)) { | ||
system.getCount("# Wall Bumps").record(); | ||
} | ||
} else { // isExpanded(). | ||
contractTail(); | ||
} | ||
} | ||
|
||
int MetricsDemoParticle::headMarkColor() const { | ||
switch(_state) { | ||
case State::Red: return 0xff0000; | ||
case State::Orange: return 0xff9000; | ||
case State::Yellow: return 0xffff00; | ||
case State::Green: return 0x00ff00; | ||
case State::Blue: return 0x0000ff; | ||
case State::Indigo: return 0x4b0082; | ||
case State::Violet: return 0xbb00ff; | ||
} | ||
|
||
return -1; | ||
} | ||
|
||
int MetricsDemoParticle::tailMarkColor() const { | ||
return headMarkColor(); | ||
} | ||
|
||
QString MetricsDemoParticle::inspectionText() const { | ||
QString text; | ||
text += "Global Info:\n"; | ||
text += " head: (" + QString::number(head.x) + ", " | ||
+ QString::number(head.y) + ")\n"; | ||
text += " orientation: " + QString::number(orientation) + "\n"; | ||
text += " globalTailDir: " + QString::number(globalTailDir) + "\n\n"; | ||
text += "Local Info:\n"; | ||
text += " state: "; | ||
text += [this](){ | ||
switch(_state) { | ||
case State::Red: return "red\n"; | ||
case State::Orange: return "orange\n"; | ||
case State::Yellow: return "yellow\n"; | ||
case State::Green: return "green\n"; | ||
case State::Blue: return "blue\n"; | ||
case State::Indigo: return "indigo\n"; | ||
case State::Violet: return "violet\n"; | ||
} | ||
return "no state\n"; | ||
}(); | ||
text += " counter: " + QString::number(_counter); | ||
|
||
return text; | ||
} | ||
|
||
MetricsDemoParticle::State MetricsDemoParticle::getRandColor() const { | ||
// Randomly select an integer and return the corresponding state via casting. | ||
return static_cast<State>(randInt(0, 7)); | ||
} | ||
|
||
MetricsDemoSystem::MetricsDemoSystem(unsigned int numParticles, int counterMax) { | ||
// In order to enclose an area that's roughly 3.7x the # of particles using a | ||
// regular hexagon, the hexagon should have side length 1.4*sqrt(# particles). | ||
int sideLen = static_cast<int>(std::round(1.4 * std::sqrt(numParticles))); | ||
Node boundNode(0, 0); | ||
for (int dir = 0; dir < 6; ++dir) { | ||
for (int i = 0; i < sideLen; ++i) { | ||
insert(new Object(boundNode)); | ||
boundNode = boundNode.nodeInDir(dir); | ||
} | ||
} | ||
|
||
// Let s be the bounding hexagon side length. When the hexagon is created as | ||
// above, the nodes (x,y) strictly within the hexagon have (i) -s < x < s, | ||
// (ii) 0 < y < 2s, and (iii) 0 < x+y < 2s. Choose interior nodes at random to | ||
// place particles, ensuring at most one particle is placed at each node. | ||
std::set<Node> occupied; | ||
while (occupied.size() < numParticles) { | ||
// First, choose an x and y position at random from the (i) and (ii) bounds. | ||
int x = randInt(-sideLen + 1, sideLen); | ||
int y = randInt(1, 2 * sideLen); | ||
Node node(x, y); | ||
|
||
// If the node satisfies (iii) and is unoccupied, place a particle there. | ||
if (0 < x + y && x + y < 2 * sideLen | ||
&& occupied.find(node) == occupied.end()) { | ||
insert(new MetricsDemoParticle(node, -1, randDir(), *this, counterMax)); | ||
occupied.insert(node); | ||
} | ||
} | ||
|
||
// Set up metrics. | ||
_counts.push_back(new Count("# Wall Bumps")); | ||
_measures.push_back(new PercentRedMeasure("% Red", 1, *this)); | ||
_measures.push_back(new MaxDistanceMeasure("Max. Distance", 1, *this)); | ||
} | ||
|
||
PercentRedMeasure::PercentRedMeasure(const QString name, | ||
const unsigned int freq, | ||
MetricsDemoSystem& system) | ||
: Measure(name, freq), | ||
_system(system) {} | ||
|
||
double PercentRedMeasure::calculate() const { | ||
int numRed = 0; | ||
|
||
// Loop through all particles of the system. | ||
for (const auto& p : _system.particles) { | ||
// Convert the pointer to a MetricsDemoParticle so its color can be checked. | ||
auto metr_p = dynamic_cast<MetricsDemoParticle*>(p); | ||
if (metr_p->_state == MetricsDemoParticle::State::Red) { | ||
numRed++; | ||
} | ||
} | ||
|
||
return numRed / static_cast<double>(_system.size()) * 100; | ||
} | ||
|
||
MaxDistanceMeasure::MaxDistanceMeasure(const QString name, | ||
const unsigned int freq, | ||
MetricsDemoSystem& system) | ||
: Measure(name, freq), | ||
_system(system) {} | ||
|
||
double MaxDistanceMeasure::calculate() const { | ||
double maxDist = 0.0; | ||
for (const auto& p1 : _system.particles) { | ||
double x1 = p1->head.x + p1->head.y / 2.0; | ||
double y1 = std::sqrt(3.0) / 2 * p1->head.y; | ||
for (const auto& p2 : _system.particles) { | ||
double x2 = p2->head.x + p2->head.y / 2.0; | ||
double y2 = std::sqrt(3.0) / 2 * p2->head.y; | ||
maxDist = std::max(std::sqrt(std::pow(x2 - x1, 2) + std::pow(y2 - y1, 2)), | ||
maxDist); | ||
} | ||
} | ||
|
||
return maxDist; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/* Copyright (C) 2020 Joshua J. Daymude, Robert Gmyr, and Kristian Hinnenthal. | ||
* The full GNU GPLv3 can be found in the LICENSE file, and the full copyright | ||
* notice can be found at the top of main/main.cpp. */ | ||
|
||
// Defines the particle system and composing particles for the MetricsDemo code | ||
// tutorial. MetricsDemo demonstrates how to add custom "counts" and "measures" | ||
// to an algorithm, enabling the capture of quantitative data. The pseudocode is | ||
// available in the docs: | ||
// [https://amoebotsim.rtfd.io/en/latest/tutorials/tutorials.html#metricsdemo-capturing-data]. | ||
// | ||
// Run on the simulator command line using metricsdemo(#particles, counter max). | ||
|
||
#ifndef AMOEBOTSIM_ALG_DEMO_METRICSDEMO_H_ | ||
#define AMOEBOTSIM_ALG_DEMO_METRICSDEMO_H_ | ||
|
||
#include <QString> | ||
|
||
#include "core/amoebotparticle.h" | ||
#include "core/amoebotsystem.h" | ||
|
||
class MetricsDemoParticle : public AmoebotParticle { | ||
friend class PercentRedMeasure; | ||
|
||
public: | ||
enum class State { | ||
Red, | ||
Orange, | ||
Yellow, | ||
Green, | ||
Blue, | ||
Indigo, | ||
Violet | ||
}; | ||
|
||
// Constructs a new particle with a node position for its head, a global | ||
// compass direction from its head to its tail (-1 if contracted), an offset | ||
// for its local compass, a system that it belongs to, and a maximum value for | ||
// its counter. | ||
MetricsDemoParticle(const Node& head, const int globalTailDir, | ||
const int orientation, AmoebotSystem& system, | ||
const int counterMax); | ||
|
||
// Executes one particle activation. | ||
void activate() override; | ||
|
||
// Functions for altering the particle's color. headMarkColor() (resp., | ||
// tailMarkColor()) returns the color to be used for the ring drawn around the | ||
// particle's head (resp., tail) node. In this demo, the tail color simply | ||
// matches the head color. | ||
int headMarkColor() const override; | ||
int tailMarkColor() const override; | ||
|
||
// Returns the string to be displayed when this particle is inspected; used to | ||
// snapshot the current values of this particle's memory at runtime. | ||
QString inspectionText() const override; | ||
|
||
protected: | ||
// Returns a random State. | ||
State getRandColor() const; | ||
|
||
// Member variables. | ||
State _state; | ||
int _counter; | ||
const int _counterMax; | ||
|
||
private: | ||
friend class MetricsDemoSystem; | ||
}; | ||
|
||
class MetricsDemoSystem : public AmoebotSystem { | ||
friend class PercentRedMeasure; | ||
friend class MaxDistanceMeasure; | ||
|
||
public: | ||
// Constructs a system of the specified number of MetricsDemoParticles | ||
// enclosed by a hexagonal ring of objects. | ||
MetricsDemoSystem(unsigned int numParticles = 30, int counterMax = 5); | ||
}; | ||
|
||
class PercentRedMeasure : public Measure { | ||
public: | ||
// Constructs a PercentRedMeasure by using the parent constructor and adding a | ||
// reference to the MetricsDemoSystem being measured. | ||
PercentRedMeasure(const QString name, const unsigned int freq, | ||
MetricsDemoSystem& system); | ||
|
||
// Calculated the percentage of particles in the system in the Red state. | ||
double calculate() const final; | ||
|
||
protected: | ||
MetricsDemoSystem& _system; | ||
}; | ||
|
||
class MaxDistanceMeasure : public Measure { | ||
public: | ||
// Constructs a MaxDistanceMeasure by using the parent constructor and adding | ||
// a reference to the MetricsDemoSystem being measured. | ||
MaxDistanceMeasure(const QString name, const unsigned int freq, | ||
MetricsDemoSystem& system); | ||
|
||
// Calculates the largest Cartesian distance between any pair of particles in | ||
// the system. | ||
double calculate() const final; | ||
|
||
protected: | ||
MetricsDemoSystem& _system; | ||
}; | ||
|
||
#endif // AMOEBOTSIM_ALG_DEMO_METRICSDEMO_H_ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.