Solving Systems of Equations with Prolog

I’m using: SWI-Prolog version ???

SWI-Prolog version 7.6.4 for amd64

I want the code to:

Find the values for X and Y that make both conditions true. My query is substitution(X, Y).

But what I’m getting is:

It’s telling me there is no solution.

My code looks like this:

substitution(X, Y) :-
  (X * 2) - (6 * Y) is 7,
  (X / 7) - (8 * Y) is 7.

There are ways to accomplish your goal in Prolog, but what you tried isn’t such a way. Prolog will parse the above as two calls to the predicate is/2. is/2 simply evaluates the right side and matches the result to the left side. (In this case, 7 does not resemble (X2) - (6Y) so it fails. is/2 is not a means of doing systems of equations.

If this is a homework problem (it looks like it might be), let us know what approaches you were instructed to use to try to solve these equations and we can advise.

If this is not homework, explain and we might point you to packages available that you can use that will do algebra suitable for your purposes.

1 Like

This is not homework. My thought was that is/2 would unify the variables, evaluating both sides to ensure they were equal. Is this not the case?

is/2 merely evaluates a constant arithmetic expression that you provide on the right side. If the right side is not constant, it will fail.

?- X = 3, Y = 7, Z is X * 6 + Y *2.

That’s the intended use. You’ll notice the forum links is/2 to its documentation where you can see more precise specs.

You used the word ‘unify’. The ‘=/2’ operator performs unify, but this is strictly a structural matching operation and doesn’t do arithmetic.

Tools like the library clpr will extend the behavior of unify to do arithmetic.

1 Like

Thanks! Just got it working.

:- [library(clpr)].

substitution(X, Y) :-
  {(X * 2) - (6 * Y) =:= 7},
  {(X / 7) - (8 * Y) =:= 7}.

About as a simple as I could hope for.

First, install CLP(BNR):

?- pack_install(clpBNR).

then you can try this snippet (save it for example in lineq.pl, then consult it) .

:- module(lineq,
          [substitution/2]).

:- use_module(library(clpBNR)).

substitution(X, Y) :-
    {(X * 2) - (6 * Y) == 7,
     (X / 7) - (8 * Y) == 7
    },
    solve([X, Y]).

It find these solutions,

 ?- substitution(X,Y).
::(X, real(-1.0Inf, -1.7976931348623157e+308)),
::(Y, real(-1.0Inf, -2.996155224770526e+307)) ;
::(X,  0.924528301886792...),
::(Y,  -0.858490566037735...) ;
::(X, real(1.7976931348623157e+308, 1.0Inf)),
::(Y, real(2.996155224770526e+307, 1.0Inf)).
2 Likes

How are there multiple solutions? If you graph the two conditions, the answer should only the intersection point. Is it treating those numbers as infinity?

Sorry, I’m not sure about the cause.
Moreover, when I attempted to restrict the range by means of a declaration [X,Y]::real(-100,100) there was no answer.
Hope that @ridgeworks, the author of pack(clpBNR), will help us to better understand these problems.

Any variables in clpBNR constraints that aren’t otherwise constrained are assumed to have a range -inf to inf as in:

?- {X*2-6*Y == 7, X/7-8*Y==7}.
X::real(-1.0Inf, 1.0Inf),
Y::real(-1.0Inf, 1.0Inf).

With infinite bounds, it’s basically impossible to narrow anything since, for example, X*inf == inf. Using solve finds the single solution but also reports that there may be others between the largest positive and negative floats and their respective infinities - it can’t prove otherwise.

However, just by declaring them to have finite bounds using ::, the correct solution is determined without even requiring solve to force narrowing:

?- [X,Y]::real, {X*2-6*Y == 7, X/7-8*Y==7}.
X:: 0.924528301886792...,
Y:: -0.858490566037735... .

Declarations are just another way to constrain values - in this case the default range is the range of finite 64 bit floating point values. Not sure why declaring them with an even narrower range failed to generate an answer - it works for me:

?- [X,Y]::real(-100,100), {X*2-6*Y == 7, X/7-8*Y==7}.
X:: 0.924528301886792...,
Y:: -0.858490566037735... .

?- {X*2-6*Y == 7, X/7-8*Y==7}, [X,Y]::real(-100,100).
X:: 0.924528301886792...,
Y:: -0.858490566037735... .
3 Likes

Thanks for the explanation.

Of course, I first tried the declaration [X,Y]::real, … but after I saw (using the debugger) that the declaration could not be processed, I opted to show @Ktedon the results I got yesterday, albeit problematic.

This morning, started the session, I got this message, that could be indicative of some code misalignment between CLP(BNR) and SWI-Prolog (currently running on Win10, I attempt to keep up-to-date running the installer every time Jan announces an update)

Welcome to SWI-Prolog (threaded, 64 bits, version 8.5.8)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
Please run ?- license. for legal details.

For online help and background, visit https://www.swi-prolog.org
For built-in help, use ?- help(Topic). or ?- apropos(Word).

?- use_module(library(clpBNR)).
ERROR: c:/users/carlo/appdata/local/swi-prolog/pack/clpbnr/prolog/clpbnr.pl:412:33: Syntax error: Operand expected, unquoted comma or bar found
ERROR: c:/users/carlo/appdata/local/swi-prolog/pack/clpbnr/prolog/clpbnr.pl:439:16: Syntax error: Operand expected, unquoted comma or bar found
% *** clpBNR v0.9.9alpha ***.
true.

and I can see that the ‘unquoted comma’, is there. This has been changed recently, so seems there is a simple fix, but now I cannot explain why the code was running yesterday.
Now will try to restart and consult the snippet posted above

(…suspence…)

Well, my fault, the errors were there, but exposed with a message box (I use the IDE and the development version), I (dumbly) removed the declaration from the snippet, without investigation about the cause, and since then I got the solution, I opted for showing it.

Sorry for the noise…

?- use_module(library(clpq)).
true.

?- {2*X -6*Y = 7, (X/7) -8*Y = 7}.
X = 49r53,
Y = -91r106.


3 Likes

But useful noise; looks like I’ve got a naked “,” issue. Kind of ironic since I think I was the instigator for this change to the parser.

I’ll push a fix to clpBNR shortly. (I’m not in a position to run the latest MacOS fat binaries and SWISH seems to be still running 8.5.7 so I trust it will be a trivial change.)

yes, I just surrounded the 2 naked comma with hyphens, and it’s all over now!

clpBNR starts using rational interval bounds in the fixed point iteration (doesn’t use simplex solver for linear equations), but at some point the size of the rationals exceeds the size threshold and get converted to floats. Usefully, the final intervals do contain the precise rational values:

?- [X,Y]::real(-100,100),{X*2-6*Y == 7, X/7-8*Y==7}, X=49r53, Y= -91r106.
X = 49r53,
Y = -91r106.

Since it is a linear system of equations, another way to do this without using constraints is to use library(type_ndarray) in pack arithmetic_types (full disclosure: I also published this pack) with Cramer’s rule:

?- Am is ndarray([[2,-6],[1r7,-8]]), Bs is ndarray([[7],[7]]), Xs is ndarray([[X],[Y]]),
Xs is dot(inverse(Am),Bs).
Am = #(#(2, -6), #(1r7, -8)),
Bs = #(#(7), #(7)),
Xs = #(#(49r53), #(-91r106)),
X = 49r53,
Y = -91r106.

I assume you mean single quotes?

yes, I meant that. Sorry…