Skip to content

Commit daf8365

Browse files
authored
pattern-based generator (v7) (#132)
1 parent 578f5bf commit daf8365

File tree

9 files changed

+181
-17
lines changed

9 files changed

+181
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ To install or update LODA, please follow the [installation instructions](https:/
55
### Features
66

77
* Added `profile` command for measuring program evaluation time
8+
* Added pattern-based generator (generator: `v7`, miner profile: `pattern`)
89

910
### Enhancements
1011

miners.pattern.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"miners": [
3+
{
4+
"name": "pattern",
5+
"overwrite": "none",
6+
"backoff": true,
7+
"generators": [
8+
"v7"
9+
],
10+
"matchers": [
11+
"direct",
12+
"linear1",
13+
"linear2",
14+
"binary",
15+
"decimal"
16+
]
17+
}
18+
],
19+
"generators": [
20+
{
21+
"name": "v7",
22+
"version": 7,
23+
"mutationRate": 0.3
24+
}
25+
]
26+
}

src/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ ifdef LODA_PLATFORM
88
CXXFLAGS += -DLODA_PLATFORM=$(LODA_PLATFORM)
99
endif
1010

11-
OBJS = api_client.o benchmark.o big_number.o blocks.o commands.o config.o distribution.o evaluator.o evaluator_inc.o extender.o external/jute.o file.o finder.o generator.o generator_v1.o generator_v2.o generator_v3.o generator_v4.o generator_v5.o generator_v6.o interpreter.o iterator.o main.o matcher.o memory.o metrics.o miner.o minimizer.o mutator.o number.o oeis_list.o oeis_manager.o oeis_sequence.o optimizer.o parser.o program.o program_util.o reducer.o semantics.o setup.o sequence.o stats.o test.o util.o web_client.o
11+
OBJS = api_client.o benchmark.o big_number.o blocks.o commands.o config.o distribution.o evaluator.o evaluator_inc.o extender.o external/jute.o file.o finder.o generator.o generator_v1.o generator_v2.o generator_v3.o generator_v4.o generator_v5.o generator_v6.o generator_v7.o interpreter.o iterator.o main.o matcher.o memory.o metrics.o miner.o minimizer.o mutator.o number.o oeis_list.o oeis_manager.o oeis_sequence.o optimizer.o parser.o program.o program_util.o reducer.o semantics.o setup.o sequence.o stats.o test.o util.o web_client.o
1212

1313
loda: external/jute.h external/jute.cpp $(OBJS)
1414
$(CXX) $(LDFLAGS) -o loda $(OBJS)

src/Makefile.windows.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ CXXFLAGS = $(CXXFLAGS) /Z7
1010
CXXFLAGS = $(CXXFLAGS) /DLODA_PLATFORM=$(LODA_PLATFORM)
1111
!ENDIF
1212

13-
SRCS = api_client.cpp benchmark.cpp big_number.cpp blocks.cpp commands.cpp config.cpp distribution.cpp evaluator.cpp evaluator_inc.cpp extender.cpp external/jute.cpp file.cpp finder.cpp generator.cpp generator_v1.cpp generator_v2.cpp generator_v3.cpp generator_v4.cpp generator_v5.cpp generator_v6.cpp interpreter.cpp iterator.cpp main.cpp matcher.cpp memory.cpp metrics.cpp miner.cpp minimizer.cpp mutator.cpp number.cpp oeis_list.cpp oeis_manager.cpp oeis_sequence.cpp optimizer.cpp parser.cpp program.cpp program_util.cpp reducer.cpp semantics.cpp sequence.cpp setup.cpp stats.cpp test.cpp util.cpp web_client.cpp
13+
SRCS = api_client.cpp benchmark.cpp big_number.cpp blocks.cpp commands.cpp config.cpp distribution.cpp evaluator.cpp evaluator_inc.cpp extender.cpp external/jute.cpp file.cpp finder.cpp generator.cpp generator_v1.cpp generator_v2.cpp generator_v3.cpp generator_v4.cpp generator_v5.cpp generator_v6.cpp generator_v7.cpp interpreter.cpp iterator.cpp main.cpp matcher.cpp memory.cpp metrics.cpp miner.cpp minimizer.cpp mutator.cpp number.cpp oeis_list.cpp oeis_manager.cpp oeis_sequence.cpp optimizer.cpp parser.cpp program.cpp program_util.cpp reducer.cpp semantics.cpp sequence.cpp setup.cpp stats.cpp test.cpp util.cpp web_client.cpp
1414

1515
loda: external/jute.h external/jute.cpp $(SRCS)
1616
cl /EHsc /Feloda.exe $(CXXFLAGS) $(SRCS)

src/generator.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "generator_v4.hpp"
88
#include "generator_v5.hpp"
99
#include "generator_v6.hpp"
10+
#include "generator_v7.hpp"
1011
#include "semantics.hpp"
1112
#include "util.hpp"
1213

@@ -38,6 +39,10 @@ Generator::UPtr Generator::Factory::createGenerator(const Config &config,
3839
generator.reset(new GeneratorV6(config, stats));
3940
break;
4041
}
42+
case 7: {
43+
generator.reset(new GeneratorV7(config, stats));
44+
break;
45+
}
4146
default: {
4247
Log::get().error(
4348
"Unknown generator version: " + std::to_string(config.version), true);

src/generator_v7.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
#include "generator_v7.hpp"
2+
3+
#include "file.hpp"
4+
#include "oeis_sequence.hpp"
5+
#include "parser.hpp"
6+
#include "program_util.hpp"
7+
#include "setup.hpp"
8+
9+
GeneratorV7::GeneratorV7(const Config &config, const Stats &stats)
10+
: Generator(config, stats),
11+
mutator(stats, config.mutation_rate, true // mutate comments!
12+
) {
13+
// load patterns
14+
Parser parser;
15+
Program program;
16+
const std::string patterns_home = Setup::getProgramsHome() + "patterns";
17+
if (isDir(patterns_home)) {
18+
for (const auto &it : std::filesystem::directory_iterator(patterns_home)) {
19+
if (it.path().filename().extension().string() != ".asm") {
20+
continue;
21+
}
22+
const auto path = it.path().string();
23+
Operation dummy(Operation::Type::NOP);
24+
dummy.comment = "dummy";
25+
try {
26+
program = parser.parse(path);
27+
ProgramUtil::removeOps(program, Operation::Type::NOP);
28+
bool has_comment = false;
29+
for (auto &op : program.ops) {
30+
has_comment = has_comment || !op.comment.empty();
31+
}
32+
if (has_comment) {
33+
// add dummy comments at begin and end of program
34+
program.ops.insert(program.ops.begin(), dummy);
35+
program.ops.push_back(dummy);
36+
patterns.push_back(program);
37+
} else {
38+
Log::get().warn("Missing annotations in pattern " +
39+
it.path().filename().string());
40+
}
41+
} catch (std::exception &) {
42+
Log::get().warn("Cannot load pattern " + path);
43+
}
44+
}
45+
}
46+
if (patterns.empty()) {
47+
Log::get().error("No patterns found", true);
48+
} else {
49+
Log::get().info("Loaded " + std::to_string(patterns.size()) + " patterns");
50+
}
51+
}
52+
53+
Program GeneratorV7::generateProgram() {
54+
auto program = patterns[Random::get().gen() % patterns.size()];
55+
56+
// std::cout << "==== BEGIN PATTERN ======" << std::endl;
57+
// ProgramUtil::print(program, std::cout);
58+
// std::cout << "==== END PATTERN ======" << std::endl << std::endl;
59+
60+
mutator.mutateRandom(program);
61+
ProgramUtil::removeOps(program, Operation::Type::NOP);
62+
ProgramUtil::removeComments(program);
63+
64+
return program;
65+
}
66+
67+
std::pair<Operation, double> GeneratorV7::generateOperation() {
68+
throw std::runtime_error("unsupported operation");
69+
return {};
70+
}

src/include/generator_v7.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#pragma once
2+
3+
#include "generator.hpp"
4+
#include "mutator.hpp"
5+
6+
/*
7+
* Generator that uses pattern files. A pattern is a program with annotation
8+
* that indicate positions where mutations should be applied.
9+
*/
10+
class GeneratorV7 : public Generator {
11+
public:
12+
GeneratorV7(const Config &config, const Stats &stats);
13+
14+
virtual Program generateProgram() override;
15+
16+
virtual std::pair<Operation, double> generateOperation() override;
17+
18+
private:
19+
std::vector<Program> patterns;
20+
Mutator mutator;
21+
};

src/include/mutator.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010
class Mutator {
1111
public:
12-
Mutator(const Stats &stats, double mutation_rate = 0.3); // magic number
12+
Mutator(const Stats &stats,
13+
double mutation_rate = 0.3, // magic number
14+
bool mutate_comment = false);
1315

1416
void mutateRandom(Program &program);
1517

@@ -21,14 +23,16 @@ class Mutator {
2123
void mutateConstants(const Program &program, size_t num_results,
2224
std::stack<Program> &result);
2325

24-
double mutation_rate;
25-
2626
RandomProgramIds2 random_program_ids;
2727

2828
private:
29+
const double mutation_rate;
30+
const bool mutate_comment;
2931
std::vector<Number> constants;
3032
std::vector<Operation::Type> operation_types;
3133

3234
std::discrete_distribution<> constants_dist;
3335
std::discrete_distribution<> operation_types_dist;
36+
37+
std::vector<size_t> tmp_comment_positions;
3438
};

src/mutator.cpp

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
#define CONSTANTS_START -100
66
#define CONSTANTS_END 1000
77

8-
Mutator::Mutator(const Stats &stats, double mutation_rate)
9-
: mutation_rate(mutation_rate), random_program_ids(stats) {
8+
Mutator::Mutator(const Stats &stats, double mutation_rate, bool mutate_comment)
9+
: random_program_ids(stats),
10+
mutation_rate(mutation_rate),
11+
mutate_comment(mutate_comment) {
1012
// initialize constants distribution from stats
1113
constants.resize(CONSTANTS_END - CONSTANTS_START + 1);
1214
for (int64_t i = 0; i <= CONSTANTS_END - CONSTANTS_START; i++) {
@@ -36,31 +38,58 @@ int64_t getRandomPos(const Program &program) {
3638
}
3739

3840
void Mutator::mutateRandom(Program &program) {
41+
int64_t num_cells, num_mutations, pos;
42+
size_t i;
43+
3944
// get number of used memory cells
40-
const int64_t num_cells =
41-
ProgramUtil::getLargestDirectMemoryCell(program) + 1;
45+
num_cells = ProgramUtil::getLargestDirectMemoryCell(program) + 2;
4246

4347
// calculate the number of mutations to apply
44-
int64_t num_mutations =
45-
static_cast<int64_t>(program.ops.size() * mutation_rate) + 1;
48+
num_mutations = static_cast<int64_t>(program.ops.size() * mutation_rate) + 1;
4649
num_mutations = Random::get().gen() % num_mutations; // could be zero
4750
if (mutation_rate > 0.0) {
4851
num_mutations++; // at least one mutation
4952
}
5053

54+
// mutate only operations with comments?
55+
if (mutate_comment) {
56+
tmp_comment_positions.clear();
57+
for (i = 0; i < program.ops.size(); i++) {
58+
if (!program.ops[i].comment.empty()) {
59+
tmp_comment_positions.push_back(i);
60+
}
61+
}
62+
if (tmp_comment_positions.empty()) {
63+
throw std::runtime_error("missing program comments for mutation");
64+
}
65+
}
66+
5167
// mutate existing operations or add new ones
52-
int64_t pos;
53-
static const Operation mov_zero(Operation::Type::MOV,
68+
static const Operation add_zero(Operation::Type::ADD,
5469
Operand(Operand::Type::DIRECT, 0),
5570
Operand(Operand::Type::CONSTANT, 0));
5671
for (; num_mutations > 0; num_mutations--) {
72+
// choose whether to add a new operation or mutate an existing one
5773
if (Random::get().gen() % 2 == 0 || program.ops.empty()) {
5874
// add new operation
59-
pos = Random::get().gen() % program.ops.size();
60-
program.ops.insert(program.ops.begin() + pos, mov_zero);
75+
if (mutate_comment) {
76+
i = Random::get().gen() % tmp_comment_positions.size();
77+
pos = tmp_comment_positions[i];
78+
for (; i < tmp_comment_positions.size(); i++) {
79+
tmp_comment_positions[i]++;
80+
}
81+
} else {
82+
pos = Random::get().gen() % program.ops.size();
83+
}
84+
program.ops.insert(program.ops.begin() + pos, add_zero);
6185
} else {
6286
// mutate existing operation
63-
pos = getRandomPos(program);
87+
if (mutate_comment) {
88+
pos = tmp_comment_positions[Random::get().gen() %
89+
tmp_comment_positions.size()];
90+
} else {
91+
pos = getRandomPos(program);
92+
}
6493
}
6594
mutateOperation(program.ops[pos], num_cells);
6695
}
@@ -77,7 +106,15 @@ void Mutator::mutateOperation(Operation &op, int64_t num_cells) {
77106
op.source =
78107
Operand(Operand::Type::DIRECT, Random::get().gen() % num_cells);
79108
}
80-
op.target = Operand(Operand::Type::DIRECT, Random::get().gen() % num_cells);
109+
if (op.type == Operation::Type::MOV &&
110+
Random::get().gen() % 4 > 0) { // magic number
111+
// avoid overwriting
112+
op.target = Operand(Operand::Type::DIRECT,
113+
(Random::get().gen() % 2) + num_cells - 1);
114+
} else {
115+
op.target =
116+
Operand(Operand::Type::DIRECT, Random::get().gen() % num_cells);
117+
}
81118
ProgramUtil::avoidNopOrOverflow(op);
82119
} else if (op.type == Operation::Type::SEQ) {
83120
// op.comment = "mutated from " + ProgramUtil::operationToString(op);

0 commit comments

Comments
 (0)