Skip to content

Commit 8a3a4a9

Browse files
authored
support seq operation in formula generator (#208)
1 parent ac15fe8 commit 8a3a4a9

File tree

14 files changed

+175
-42
lines changed

14 files changed

+175
-42
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ To install or update LODA, please follow the [installation instructions](https:/
88
* Check maximum number of steps in incremental evaluator
99
* Avoid fixed number of 100 terms in `minimize` command; now configurable using `-t` with default of 10 terms
1010

11+
### Features
12+
13+
* Support `seq` operation in formula generator
14+
1115
## v22.11.7
1216

1317
### Bugfixes

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ Options:
5252
-b Print result in b-file format from offset 0
5353
-B <number> Print result in b-file format from a custom offset
5454
-o <string> Export format (formula,loda,pari)
55+
-d Export with dependencies to other programs
5556
-s Evaluate program to number of execution steps
5657
-c <number> Maximum number of interpreter cycles (no limit: -1)
5758
-m <number> Maximum number of used memory cells (no limit: -1)

src/commands.cpp

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ void Commands::help() {
8686
<< std::endl;
8787
std::cout << " -o <string> Export format (formula,loda,pari)"
8888
<< std::endl;
89+
std::cout
90+
<< " -d Export with dependencies to other programs"
91+
<< std::endl;
8992
std::cout << " -s Evaluate program to number of "
9093
"execution steps"
9194
<< std::endl;
@@ -192,18 +195,18 @@ void Commands::export_(const std::string& path) {
192195
Parser parser;
193196
Program program = parser.parse(getProgramPathAndSeqId(path).first);
194197
const auto& format = settings.export_format;
198+
Formula formula;
199+
auto generate = FormulaGenerator::generate;
195200
if (format.empty() || format == "formula") {
196-
auto formula = FormulaGenerator::generate(program, false);
197-
if (!formula.first) {
201+
if (!generate(program, -1, formula, false, settings.with_deps)) {
198202
throw std::runtime_error("program cannot be converted to formula");
199203
}
200-
std::cout << formula.second.toString(false) << std::endl;
204+
std::cout << formula.toString(false) << std::endl;
201205
} else if (format == "pari") {
202-
auto formula = FormulaGenerator::generate(program, true);
203-
if (!formula.first) {
206+
if (!generate(program, -1, formula, true, settings.with_deps)) {
204207
throw std::runtime_error("program cannot be converted to pari");
205208
}
206-
std::cout << formula.second.toString(true) << std::endl;
209+
std::cout << formula.toString(true) << std::endl;
207210
} else if (format == "loda") {
208211
ProgramUtil::print(program, std::cout);
209212
} else {
@@ -313,20 +316,23 @@ void Commands::testPari() {
313316
}
314317
auto seq = manager.getSequences().at(id);
315318
auto program = parser.parse(seq.getProgramPath());
316-
auto formula = FormulaGenerator::generate(program, true);
317-
if (!formula.first) {
319+
Formula formula;
320+
if (!FormulaGenerator::generate(program, id, formula, true, true)) {
318321
continue;
319322
}
320-
auto pariCode = formula.second.toString(true);
323+
auto pariCode = formula.toString(true);
321324
Log::get().info(seq.id_str() + ": " + pariCode);
322325
Sequence expSeq;
323326
size_t numTerms = seq.existingNumTerms();
324327
if (inceval.init(program) ||
325328
pariCode.find("binomial") != std::string::npos) {
326329
numTerms = std::min<size_t>(numTerms, 10);
327330
}
331+
if (ProgramUtil::hasOp(program, Operation::Type::SEQ)) {
332+
numTerms = std::min<size_t>(numTerms, 3);
333+
}
328334
evaluator.eval(program, expSeq, numTerms);
329-
auto genSeq = Pari::eval(formula.second, 0, numTerms - 1);
335+
auto genSeq = Pari::eval(formula, 0, numTerms - 1);
330336
if (genSeq != expSeq) {
331337
Log::get().info("Generated sequence: " + genSeq.to_string());
332338
Log::get().info("Expected sequence: " + expSeq.to_string());

src/expression.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,21 @@ int Expression::compare(const Expression& e) const {
8383
return 0;
8484
}
8585
case Expression::Type::FUNCTION:
86-
// reverse sorting for function names!
87-
if (name < e.name) {
86+
// custom sorting for function names
87+
if (name == e.name) {
88+
return compareChildren(e);
89+
} else if (name.empty()) {
8890
return 1;
89-
} else if (e.name < name) {
91+
} else if (e.name.empty()) {
92+
return -1;
93+
} else if (std::islower(name[0]) && std::isupper(e.name[0])) {
94+
return 1;
95+
} else if (std::isupper(name[0]) && std::islower(e.name[0])) {
9096
return -1;
97+
} else if (name < e.name) {
98+
return 1;
9199
} else {
92-
return compareChildren(e);
100+
return -1;
93101
}
94102
case Expression::Type::NEGATION:
95103
case Expression::Type::SUM:

src/formula_gen.cpp

Lines changed: 98 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
#include "formula_gen.hpp"
22

3+
#include <set>
34
#include <stdexcept>
45

56
#include "evaluator_inc.hpp"
67
#include "expression_util.hpp"
78
#include "log.hpp"
9+
#include "oeis_sequence.hpp"
10+
#include "parser.hpp"
811
#include "program_util.hpp"
912

1013
std::string memoryCellToName(Number index) {
@@ -127,6 +130,12 @@ bool update(Formula& f, const Operation& op, bool pariMode) {
127130
res = Expression(Expression::Type::FUNCTION, "max", {prevTarget, source});
128131
break;
129132
}
133+
case Operation::Type::SEQ: {
134+
res =
135+
Expression(Expression::Type::FUNCTION,
136+
OeisSequence(source.value.asInt()).id_str(), {prevTarget});
137+
break;
138+
}
130139
case Operation::Type::TRN: {
131140
res = Expression(
132141
Expression::Type::FUNCTION, "max",
@@ -231,15 +240,12 @@ int64_t getNumInitialTermsNeeded(int64_t cell, const Formula& f,
231240
}
232241
}
233242

234-
std::pair<bool, Formula> FormulaGenerator::generate(const Program& p,
235-
bool pariMode) {
236-
std::pair<bool, Formula> result;
237-
result.first = false;
243+
bool generateSingle(const Program& p, Formula& result, bool pariMode) {
238244
Formula f, tmp;
239245

240246
// indirect operands are not supported
241247
if (ProgramUtil::hasIndirectOperand(p)) {
242-
return result;
248+
return false;
243249
}
244250
const int64_t numCells = ProgramUtil::getLargestDirectMemoryCell(p) + 1;
245251

@@ -251,16 +257,16 @@ std::pair<bool, Formula> FormulaGenerator::generate(const Program& p,
251257
if (use_ie) {
252258
// TODO: remove this limitation
253259
if (ie.getLoopCounterCell() != 0) {
254-
return result;
260+
return false;
255261
}
256262
// TODO: remove this limitation
257263
if (!ie.getLoopCounterDependentCells().empty()) {
258-
return result;
264+
return false;
259265
}
260266
// TODO: remove this limitation
261267
for (auto& op : ie.getPreLoop().ops) {
262268
if (op.type == Operation::Type::MUL || op.type == Operation::Type::DIV) {
263-
return result;
269+
return false;
264270
}
265271
}
266272
}
@@ -294,7 +300,7 @@ std::pair<bool, Formula> FormulaGenerator::generate(const Program& p,
294300
main = p;
295301
}
296302
if (!update(f, main, pariMode)) {
297-
return result;
303+
return false;
298304
}
299305
Log::get().debug("Updated formula: " + f.toString(false));
300306

@@ -317,13 +323,13 @@ std::pair<bool, Formula> FormulaGenerator::generate(const Program& p,
317323
if (op.type == Operation::Type::MOV &&
318324
op.source.type == Operand::Type::DIRECT) {
319325
if (hasArithmetic) {
320-
return result;
326+
return false;
321327
}
322328
auto s = operandToExpression(op.source);
323329
f.entries[t] = s;
324330
} else {
325331
if (!update(f, op, pariMode)) {
326-
return result;
332+
return false;
327333
}
328334
hasArithmetic = true;
329335
}
@@ -392,14 +398,14 @@ std::pair<bool, Formula> FormulaGenerator::generate(const Program& p,
392398
}
393399
}
394400
if (keys.size() > 2) {
395-
return result;
401+
return false;
396402
}
397403
if (recursive.size() > 1) {
398-
return result;
404+
return false;
399405
}
400406
for (auto r : recursive) {
401407
if (deps.count(r) > 1) {
402-
return result;
408+
return false;
403409
}
404410
}
405411

@@ -441,7 +447,82 @@ std::pair<bool, Formula> FormulaGenerator::generate(const Program& p,
441447
}
442448

443449
// success
444-
result.first = true;
445-
result.second = f;
446-
return result;
450+
result = f;
451+
return true;
452+
}
453+
454+
bool addProgramIds(const Program& p, std::set<int64_t>& ids) {
455+
// TODO: check for recursion
456+
Parser parser;
457+
for (auto op : p.ops) {
458+
if (op.type == Operation::Type::SEQ) {
459+
auto id = op.source.value.asInt();
460+
if (ids.find(id) == ids.end()) {
461+
ids.insert(id);
462+
OeisSequence seq(id);
463+
try {
464+
auto q = parser.parse(seq.getProgramPath());
465+
addProgramIds(q, ids);
466+
} catch (const std::exception&) {
467+
return false;
468+
}
469+
}
470+
}
471+
}
472+
return true;
473+
}
474+
475+
void addFormula(Formula& main, Formula extension) {
476+
int64_t numCells =
477+
main.entries.size() + extension.entries.size() + 1; // upper bound
478+
for (int64_t i = 0; i < numCells; i++) {
479+
auto from = memoryCellToName(i);
480+
if (usesFunction(main, from) && usesFunction(extension, from)) {
481+
for (int64_t j = 1; j < numCells; j++) {
482+
auto to = memoryCellToName(j);
483+
if (!usesFunction(main, to) && !usesFunction(extension, to)) {
484+
Log::get().debug("Replacing " + from + " by " + to);
485+
extension.replaceName(from, to);
486+
break;
487+
}
488+
}
489+
}
490+
}
491+
main.entries.insert(extension.entries.begin(), extension.entries.end());
492+
}
493+
494+
bool FormulaGenerator::generate(const Program& p, int64_t id, Formula& result,
495+
bool pariMode, bool withDeps) {
496+
if (!generateSingle(p, result, pariMode)) {
497+
result.clear();
498+
return false;
499+
}
500+
if (withDeps) {
501+
std::set<int64_t> ids;
502+
if (!addProgramIds(p, ids)) {
503+
return false;
504+
}
505+
Parser parser;
506+
for (auto id2 : ids) {
507+
OeisSequence seq(id2);
508+
Program p2;
509+
try {
510+
p2 = parser.parse(seq.getProgramPath());
511+
} catch (const std::exception&) {
512+
result.clear();
513+
return false;
514+
}
515+
Formula f2;
516+
if (!generateSingle(p2, f2, pariMode)) {
517+
result.clear();
518+
return false;
519+
}
520+
auto from = memoryCellToName(Program::INPUT_CELL);
521+
auto to = seq.id_str();
522+
Log::get().debug("Replacing " + from + " by " + to);
523+
f2.replaceName(from, to);
524+
addFormula(result, f2);
525+
}
526+
}
527+
return true;
447528
}

src/include/formula_gen.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55

66
class FormulaGenerator {
77
public:
8-
static std::pair<bool, Formula> generate(const Program& p, bool pariMode);
8+
static bool generate(const Program& p, int64_t id, Formula& result,
9+
bool pariMode, bool withDeps);
910
};

src/include/util.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Settings {
2626
int64_t max_memory;
2727
int64_t max_cycles;
2828
bool use_steps;
29+
bool with_deps;
2930
bool parallel_mining;
3031
bool report_cpu_hours;
3132
int64_t num_miner_instances;

src/oeis_manager.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -645,9 +645,9 @@ void OeisManager::dumpProgram(size_t id, Program &p, const std::string &file,
645645
}
646646
}
647647
tmp.ops.push_back(nop);
648-
auto formula = FormulaGenerator::generate(p, false);
649-
if (formula.first) {
650-
nop.comment = "Formula: " + formula.second.toString(false);
648+
Formula formula;
649+
if (FormulaGenerator::generate(p, id, formula, false, false)) {
650+
nop.comment = "Formula: " + formula.toString(false);
651651
tmp.ops.push_back(nop);
652652
}
653653
nop.comment.clear();
@@ -665,9 +665,9 @@ void OeisManager::alert(Program p, size_t id, const std::string &prefix,
665665
std::stringstream buf;
666666
buf << prefix << " program for " << seq
667667
<< " Terms: " << seq.getTerms(settings.num_terms);
668-
auto formula = FormulaGenerator::generate(p, false);
669-
if (formula.first) {
670-
buf << ". Formula: " << formula.second.toString(false);
668+
Formula formula;
669+
if (FormulaGenerator::generate(p, id, formula, false, false)) {
670+
buf << ". Formula: " << formula.toString(false);
671671
}
672672
if (!submitted_by.empty()) {
673673
buf << ". " << ProgramUtil::PREFIX_SUBMITTED_BY << " " << submitted_by;

src/test.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -884,12 +884,12 @@ void Test::formula() {
884884
OeisSequence seq(e.first);
885885
Log::get().info("Testing formula for " + seq.id_str() + ": " + e.second);
886886
auto p = parser.parse(seq.getProgramPath());
887-
auto r = FormulaGenerator::generate(p, false);
888-
if (!r.first) {
887+
Formula f;
888+
if (!FormulaGenerator::generate(p, seq.id, f, false, true)) {
889889
Log::get().error("Cannot generate formula from program", true);
890890
}
891-
if (r.second.toString(false) != e.second) {
892-
Log::get().error("Unexpected formula: " + r.second.toString(false), true);
891+
if (f.toString(false) != e.second) {
892+
Log::get().error("Unexpected formula: " + f.toString(false), true);
893893
}
894894
}
895895
}

src/util.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Settings::Settings()
3232
max_memory(DEFAULT_MAX_MEMORY),
3333
max_cycles(DEFAULT_MAX_CYCLES),
3434
use_steps(false),
35+
with_deps(false),
3536
parallel_mining(false),
3637
report_cpu_hours(true),
3738
num_miner_instances(0),
@@ -129,6 +130,8 @@ std::vector<std::string> Settings::parseArgs(int argc, char *argv[]) {
129130
option = Option::EXPORT_FORMAT;
130131
} else if (opt == "s") {
131132
use_steps = true;
133+
} else if (opt == "d") {
134+
with_deps = true;
132135
} else if (opt == "p") {
133136
parallel_mining = true;
134137
} else if (opt == "P") {

0 commit comments

Comments
 (0)