Can ChatGPT generate correct Prolog code for this problem?

One of the big ticket concerns of ChatGPT for teachers now is that ChatGPT can generate code for most simple programming questions.

However there may be questions that ChatGPT can not answer, even with some feedback in a conversation (a series of replies in a single chat).

In a ChatGPT conversation, ChatGPT saw these facts:
Note: ChatGPT was able to create the facts based on questions only as listed but it took a few replies to get it to this.

connected(oxford_circus, tottenham_court_road).
connected(oxford_circus, bond_street).
connected(oxford_circus, leicester_square).
connected(oxford_circus, green_park).
connected(tottenham_court_road, holborn).
connected(tottenham_court_road, bond_street).
connected(holborn, covent_garden).
connected(holborn, leicester_square).
connected(leicester_square, piccadilly_circus).
connected(leicester_square, covent_garden).
connected(piccadilly_circus, green_park).
connected(piccadilly_circus, oxford_circus).
connected(green_park, bond_street).
connected(bond_street, oxford_circus).
connected(goodge_street, tottenham_court_road).
connected(covent_garden, holborn).

As there is no need to have a fact with a connection of the opposite form,

  • E.g. both connected(A,B) and connected(B,A),
  • I.e. connected(oxford_circus, bond_street). and connected(bond_street, oxford_circus).

I tried to get ChatGPT to remove the opposite form by asking this question.

For connected/2 remove fact if the reverse is listed.

which supplied this code

clean_edges :-
    retractall(connected(X, Y)),
    retractall(connected(Y, X)),
    forall(connected(X, Y), (assert(connected(X, Y)), assert(connected(Y, X)))).

That code does not work.

So I created some code of my own that worked,

:- dynamic connected/2.

clean_edges :-
    nodes(Nodes),
    foreach(
        (
            member(A,Nodes),
            member(B,Nodes)
        ),
        (
            connected(A,B),
            connected(B,A),
            % format('retract(connected(~w,~w))~n',[B,A]),
            retract(connected(B,A)), !
        ;
            true
        )
    ).

After it worked I tried to refactor by hand to be simpler but then ran into the use of negation in foreach/2 so stopped trying to refactor it but then thought it might be a problem ChatGPT can’t answer correctly.

So can ChatGPT given just the facts create code that does the same as my clean_edges/0 in one question? In a few questions? Given some hints?

My thought is that because AFAIK ChatGPT does not make use of attributes or such, only attention, it can not understand how the use of negation works with such code. I am sure it might stumble upon a correct solution if the question is worded correctly but then if asked with the same question in a different conversation not give a correct answer.


Note about nodes/1:

ChatGPT will create the correct nodes/1 predicate if asked.

Create Prolog predicate nodes/1 to get unique list of the stations from the connected/2 predicate.

nodes(Nodes) :-
    findall(X, (connected(X, _); connected(_, X)), TempNodes),
    sort(TempNodes, Nodes).

Example run using my code (Click triangle to expand)
?- listing(connected(A,B)).
:- dynamic example_01:connected/2.

example_01:connected(oxford_circus, tottenham_court_road).
example_01:connected(oxford_circus, bond_street).
example_01:connected(oxford_circus, leicester_square).
example_01:connected(oxford_circus, green_park).
example_01:connected(tottenham_court_road, holborn).
example_01:connected(tottenham_court_road, bond_street).
example_01:connected(holborn, covent_garden).
example_01:connected(holborn, leicester_square).
example_01:connected(leicester_square, piccadilly_circus).
example_01:connected(leicester_square, covent_garden).
example_01:connected(piccadilly_circus, green_park).
example_01:connected(piccadilly_circus, oxford_circus).
example_01:connected(green_park, bond_street).
example_01:connected(bond_street, oxford_circus).
example_01:connected(goodge_street, tottenham_court_road).
example_01:connected(covent_garden, holborn).

true.

?- clean_edges.
Correct to: "example_01:clean_edges"? yes
retract(connected(oxford_circus,bond_street))
retract(connected(holborn,covent_garden))
true.

?- listing(connected(A,B)).
:- dynamic example_01:connected/2.

example_01:connected(oxford_circus, tottenham_court_road).
example_01:connected(oxford_circus, leicester_square).
example_01:connected(oxford_circus, green_park).
example_01:connected(tottenham_court_road, holborn).
example_01:connected(tottenham_court_road, bond_street).
example_01:connected(holborn, leicester_square).
example_01:connected(leicester_square, piccadilly_circus).
example_01:connected(leicester_square, covent_garden).
example_01:connected(piccadilly_circus, green_park).
example_01:connected(piccadilly_circus, oxford_circus).
example_01:connected(green_park, bond_street).
example_01:connected(bond_street, oxford_circus).
example_01:connected(goodge_street, tottenham_court_road).
example_01:connected(covent_garden, holborn).

true.

EDIT

If one ask ChatGPT to write such a question giving the code, then ask the generated question, the generated code is not what is desired.

Me

What is an effect question to ask to generate this Prolog predicate without saying rectractall/1 and without saying forall/2:

clean_edges :-
forall((connected(A,B), A @< B),
retractall(connected(B,A))).

ChatGPT

“Write a Prolog predicate clean_edges/0 that removes all connected/2 facts in which the second argument is greater than the first argument.”

Me

“Write a Prolog predicate clean_edges/0 that removes all connected/2 facts in which the second argument is greater than the first argument.”

ChatGPT

Here’s the implementation of the predicate clean_edges/0 :

clean_edges :-
    (   connected(A,B),
        A @< B,
        retract(connected(B,A)),
        fail
    ;   true
    ).
1 Like

Maybe this?

clean_edges :-
     forall((connected(A,B), A @< B),
            retractall(connected(B,A))).
1 Like

Yes, it works as needed.


Example run using Jans code (Click triangle to expand)
?- listing(connected(A,B)).
:- dynamic example_01:connected/2.

example_01:connected(oxford_circus, tottenham_court_road).
example_01:connected(oxford_circus, bond_street).
example_01:connected(oxford_circus, leicester_square).
example_01:connected(oxford_circus, green_park).
example_01:connected(tottenham_court_road, holborn).
example_01:connected(tottenham_court_road, bond_street).
example_01:connected(holborn, covent_garden).
example_01:connected(holborn, leicester_square).
example_01:connected(leicester_square, piccadilly_circus).
example_01:connected(leicester_square, covent_garden).
example_01:connected(piccadilly_circus, green_park).
example_01:connected(piccadilly_circus, oxford_circus).
example_01:connected(green_park, bond_street).
example_01:connected(bond_street, oxford_circus).
example_01:connected(goodge_street, tottenham_court_road).
example_01:connected(covent_garden, holborn).

?- clean_edges.
Correct to: "example_01:clean_edges"? yes
true.

?- listing(connected(A,B)).
:- dynamic example_01:connected/2.

example_01:connected(oxford_circus, tottenham_court_road).
example_01:connected(oxford_circus, leicester_square).
example_01:connected(oxford_circus, green_park).
example_01:connected(tottenham_court_road, holborn).
example_01:connected(tottenham_court_road, bond_street).
example_01:connected(holborn, leicester_square).
example_01:connected(leicester_square, piccadilly_circus).
example_01:connected(leicester_square, covent_garden).
example_01:connected(piccadilly_circus, green_park).
example_01:connected(piccadilly_circus, oxford_circus).
example_01:connected(green_park, bond_street).
example_01:connected(bond_street, oxford_circus).
example_01:connected(goodge_street, tottenham_court_road).
example_01:connected(covent_garden, holborn).

true.

Before that run above I used a modified version to see the retractall statements being called:

clean_edges :-
    forall(
        (connected(A,B), A @< B),
        (
            format('retractall(connected(~w,~w))~n',[B,A]),
            retractall(connected(B,A))
        )
    ).

When run

?- clean_edges.
Correct to: "example_01:clean_edges"? yes
retractall(connected(tottenham_court_road,oxford_circus))
retractall(connected(leicester_square,holborn))
retractall(connected(piccadilly_circus,leicester_square))
retractall(connected(oxford_circus,bond_street))
retractall(connected(tottenham_court_road,goodge_street))
retractall(connected(holborn,covent_garden))
true.

which was doing more than I anticipated but in checking each retractall noticed that some of the connected/2 being retracted were nonexistent so did not matter.

Will have to remember that one for future use. :grinning:


Makes we wonder if we give ChatGPT the declarative reading of a predicate if it can generate the Prolog code. If that works then ChatGPT could be used to improve the declarative reading or maybe the reverse and create the declarative reading from code.