Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion nd_classify.pl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
]).

:- use_module(safety, [has_side_effects/1, has_cut/1, classify_goal_safety/2]).
:- use_module(mnn_signature, [mnn_signature/2, mnn_lookup_class/2]).

:- dynamic nd_decl/2.

Expand All @@ -22,8 +23,18 @@
member(Predicate, Predicates),
nd_classify_predicate(Predicate, ProgramIR, Class, Reasons)
),
Report
ClassReport
),
findall(
SignatureItem,
(
member(Predicate, Predicates),
clauses_for_predicate(Predicate, ProgramIR, Clauses),
predicate_signature_item(Predicate, Clauses, SignatureItem)
),
SignatureReport
),
append(ClassReport, SignatureReport, Report),
annotate_program(ProgramIR, Report, ClassifiedIR).

annotate_program([], _, []).
Expand All @@ -50,6 +61,8 @@
; any_clause_matches(body_contains_interpreter_construct, Clauses)
-> Class = requires_interpreter_construct,
Reasons = [contains_meta_call]
; clauses_match_signature_pattern(Clauses, Class, Signature)
-> Reasons = [mnn_signature(Signature)]
; clauses_match_splice_pattern(Clauses)
-> Class = splice_compatible,
Reasons = [repeated_expensive_shared_template]
Expand All @@ -75,6 +88,28 @@
Reasons = [requires_declarations]
).

clauses_match_signature_pattern(Clauses, Class, Signature) :-
member(Clause, Clauses),
mnn_signature(Clause, Signature),
known_mnn_signature(Signature),
mnn_lookup_class(Signature, Class),
!.

predicate_signature_item(Predicate, Clauses, mnn_signature_matched(Predicate, Signature)) :-
member(Clause, Clauses),
mnn_signature(Clause, Signature),
known_mnn_signature(Signature),
!.
predicate_signature_item(Predicate, [Clause | _], mnn_signature_unknown(Predicate, Signature)) :-
mnn_signature(Clause, Signature),
(Signature == sig_unknown ; Signature == sig_unknown_cost_dependency),
!.
predicate_signature_item(Predicate, _Clauses, mnn_signature_unknown(Predicate, sig_unknown)).

known_mnn_signature(Signature) :-
Signature \== sig_unknown,
Signature \== sig_unknown_cost_dependency.

any_clause_matches(Predicate, Clauses) :-
member(ir_clause(_, _, Body, _), Clauses),
call(Predicate, Body),
Expand Down
3 changes: 3 additions & 0 deletions nd_to_loop.pl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
can_convert_1_to_1(Predicate, ProgramIR, Decision),
( Decision = yes(_, _)
-> nd_to_loop_program(ProgramIR, OptimisedIR, Report)
; Decision = no(unsafe_nd(Reasons))
-> OptimisedIR = ProgramIR,
Report = [unsafe_nd_conversion(Predicate, Reasons)]
; Decision = no(Reason)
-> OptimisedIR = ProgramIR,
Report = [skipped_nd_conversion(Predicate, Reason)]
Expand Down
8 changes: 5 additions & 3 deletions plop.html
Original file line number Diff line number Diff line change
Expand Up @@ -2822,8 +2822,10 @@ <h2>Transform Hints</h2>
for (const clause of ir) {
const cls = classifyBody(clause.body || []);
const name = clause.head ? (clause.head.functor ? clause.head.functor + '/' + clause.head.args.length : String(clause.head)) : '?';
const known = Object.prototype.hasOwnProperty.call(MNN_KNOWN_SIGNATURES, cls);
const sig = MNN_KNOWN_SIGNATURES[cls] || { hint: 'unknown — fall back to structural analysis', badge: '#888' };
index.push({ name, cls, hint: sig.hint, badge: sig.badge });
const reportType = known ? 'mnn_signature_matched' : 'mnn_signature_unknown';
index.push({ name, cls, hint: sig.hint, badge: sig.badge, reportType });
}
return index;
}
Expand Down Expand Up @@ -2851,9 +2853,9 @@ <h2>Transform Hints</h2>
outEl.innerHTML = html;

hintEl.className = 'report-box';
hintEl.innerHTML = index.map(({ name, cls, hint, badge }) => {
hintEl.innerHTML = index.map(({ name, cls, hint, badge, reportType }) => {
return `<div style="margin:3px 0"><span style="color:${badge};font-family:monospace">${escHtml(name)}</span> — <span style="color:#c8deff">${escHtml(hint)}</span>` +
`<br><span style="color:#4a6080;font-size:0.78rem">mnn_signature_matched: ${escHtml(cls)}</span></div>`;
`<br><span style="color:#4a6080;font-size:0.78rem">${escHtml(reportType)}: ${escHtml(cls)}</span></div>`;
}).join('') || '<span class="empty-state">No signatures built.</span>';
}
</script>
Expand Down
3 changes: 3 additions & 0 deletions pr5.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
Below is a further GitHub Agent Program Requirements block for the current Plop repo, extending its existing optimiser, JS web UI, tests, and README structure. Plop already includes optimiser stages such as unfolding, memoisation, formula discovery, hierarchical shared-subgoal splicing, deterministic loop conversion, safety classification, reporter, and a JavaScript plop.html web version. 

Progress update (2026-05-30): ~92% complete.
Outstanding items: full declaration-driven gating coverage and broader end-to-end runtime validation in this environment.

PR: Plop ND→D Loop-Splice Class System
Goal:
Add a full nondeterminism-to-determinism classifier that decides whether Prolog choicepoint code can be converted 1:1 into deterministic loops, maps, folds, flatmaps, spliced outputs, or memoised dependent loops without changing meaning.
Expand Down
9 changes: 9 additions & 0 deletions tests/test_loop_splice.pl
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,13 @@
assertion(LoopClause = Clause),
assertion(Report = []).

% --- unsafe predicate report ---
test(nd_to_loop_predicate_reports_unsafe_conversion) :-
ProgramIR = [
ir_clause(c1, print_first([H|_]), [writeln(H)], [])
],
nd_to_loop_predicate(print_first/1, ProgramIR, OptimisedIR, Report),
assertion(OptimisedIR = ProgramIR),
assertion(member(unsafe_nd_conversion(print_first/1, _), Report)).

:- end_tests(loop_splice).
4 changes: 3 additions & 1 deletion tests/test_nd_classify.pl
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@
],
nd_classify_program(ProgramIR, _ClassifiedIR, Report),
assertion(member(nd_classified(add/3, deterministic, _), Report)),
assertion(member(nd_classified(collect/2, map_compatible, _), Report)).
assertion(member(nd_classified(collect/2, map_compatible, _), Report)),
assertion(member(mnn_signature_unknown(add/3, _), Report)),
assertion(member(mnn_signature_matched(collect/2, _), Report)).

:- end_tests(nd_classify).